## https://sploitus.com/exploit?id=07CB3C0E-3727-5A6A-A302-92F9CE87C26E
# U-Boot btrfs Out-of-Bounds Read (`btrfs_read_extent_reg`)
**Tested against U-Boot commit:** `6741b0dfb41`
An out-of-bounds read in U-Boot's btrfs filesystem driver, reachable from an
untrusted btrfs image. Found by source review and confirmed with a working
AddressSanitizer reproducer on a sandbox build.
> **Defensive / research use only.** This repository documents a vulnerability
> found in U-Boot's open-source code and provides a proof-of-concept used to
> confirm it on a local test build, for responsible disclosure to the U-Boot
> maintainers. The crafted image is a test artifact that makes a sanitizer fire
> on an out-of-bounds read; it is not a weapon and targets no third-party system.
## Repository Contents
| File | Description |
|------|-------------|
| `README.md` | This document (technical writeup). |
| `make_btrfs_oob_poc.py` | Builds the crafted btrfs image (offset corruption + crc32c fix-up). |
| `assets/asan_crash.png` | AddressSanitizer output from the reproducer. |
## Summary
The U-Boot btrfs filesystem driver contains an out-of-bounds read in
`btrfs_read_extent_reg()` (`fs/btrfs/inode.c`). When reading a **compressed**
file extent, the driver uses the `file_extent_offset` field taken directly from
the on-disk extent item as an offset into a heap buffer, without validating it
against the buffer's size. A crafted btrfs image with a large
`file_extent_offset` causes `memcpy()` to read far beyond the end of the
allocated decompression buffer.
Because this is a bootloader, the usual runtime memory protections (KASAN,
ASLR, MMU-enforced isolation in later boot stages) are not present. The out-of-
bounds data is copied into the destination buffer, i.e. into the "contents" of
the file being read, which makes this an information-leak primitive; depending
on memory layout it can also cause an immediate crash (denial of service during
boot).
- **Component:** `fs/btrfs/inode.c`, function `btrfs_read_extent_reg()`
- **Class:** Out-of-bounds read (CWE-125)
- **Trigger:** Reading a file from a malicious btrfs image with a compressed
extent whose `file_extent_offset` is larger than the extent's `ram_bytes`
- **Impact:** Information disclosure and/or denial of service in the bootloader,
from untrusted storage; relevant to any device that reads a btrfs filesystem
from removable or otherwise attacker-influenced media
- **Attacker input:** An untrusted btrfs image (SD card, USB, tampered
partition)
## Affected Code
```c
/* fs/btrfs/inode.c, btrfs_read_extent_reg() -- compressed extent path */
csize = btrfs_file_extent_disk_num_bytes(leaf, fi);
dsize = btrfs_file_extent_ram_bytes(leaf, fi); /* from image */
...
dbuf = malloc_cache_aligned(dsize); /* buffer sized by dsize */
...
ret = btrfs_decompress(btrfs_file_extent_compression(leaf, fi), cbuf,
csize, dbuf, dsize);
...
memcpy(dest,
dbuf + btrfs_file_extent_offset(leaf, fi) + offset - key.offset,
len); /* sectorsize) &&
IS_ALIGNED(len, fs_info->sectorsize));
ASSERT(offset >= key.offset &&
offset + len host bind 0 /path/to/btrfs_poc.img
=> load host 0 0x1000000 testfile
```
### Observed result
AddressSanitizer reports a `READ` memory access fault inside `memcpy`, called
from `btrfs_read_extent_reg` at the vulnerable line, on a stock build (no debug
instrumentation):

```
==...==ERROR: AddressSanitizer: SEGV on unknown address 0x000028c617c0
The signal is caused by a READ memory access.
#0 memcpy lib/string.c:593
#1 btrfs_read_extent_reg fs/btrfs/inode.c:514
#2 btrfs_file_read fs/btrfs/inode.c:751
#3 btrfs_read fs/btrfs/btrfs.c:227
#4 _fs_read fs/fs.c:628
#5 do_load fs/fs.c:847
...
```
The read faults inside `memcpy`, called from `btrfs_read_extent_reg`, with the
crafted `file_extent_offset` value in effect (`file_extent_offset = 268435456`,
`dsize = 131072`).
## Impact
- **Information disclosure.** For out-of-bounds offsets that land on mapped
memory, the bytes read past `dbuf` are copied into `dest` โ the file contents
returned to whatever consumed the read. In a boot flow this can leak
bootloader memory into a loaded artifact.
- **Denial of service.** For offsets that land on unmapped memory (as in the PoC),
the read faults and the boot process crashes.
- **Context.** U-Boot runs before the OS, at high privilege, without the runtime
memory-safety mitigations of a mature kernel. The input is an untrusted btrfs
image, which is realistic for devices that boot from or mount removable or
attacker-influenced storage.
## Suggested Fix
Validate `file_extent_offset` (together with the read window) against the
decompressed buffer size before the copy, and make the check unconditional
(not an `assert`). For example, before the `memcpy` in the compressed path:
```c
u64 src_off = btrfs_file_extent_offset(leaf, fi) + offset - key.offset;
if (offset dsize ||
(u64)src_off + len > dsize) {
ret = -EUCLEAN;
goto out;
}
memcpy(dest, dbuf + src_off, len);
```
More broadly, consider validating `btrfs_file_extent_item` fields
(`ram_bytes`, `offset`, `num_bytes`) at leaf-load time, mirroring the semantic
checks performed by the Linux kernel's btrfs tree-checker, so that all
downstream consumers can rely on sane values.
## Disclosure
This issue was found by source review of U-Boot's public code and confirmed on a
local sandbox build with AddressSanitizer, for the purpose of responsible
disclosure to the U-Boot maintainers. Report it via the U-Boot security process
(`u-boot@lists.denx.de`, and the security contact listed in the U-Boot tree)
with this document and the PoC attached. Please allow the maintainers time to
respond and prepare a fix before any public discussion.
## Notes and Caveats
- Static analysis established that the code does not validate
`file_extent_offset`; the ASan crash confirms the out-of-bounds read
dynamically, giving a working reproducer.
- The PoC demonstrates the DoS/crash form directly. The information-leak form
follows from the same primitive (out-of-bounds bytes reaching `dest`), but its
exploitability depends on memory layout and on how the loaded data is
subsequently used.
- Exact line numbers (`inode.c:514`, `751`) correspond to the tested commit and
may differ across versions; the logic is unchanged wherever the compressed-
extent `memcpy` uses `btrfs_file_extent_offset` without a size check.
```