Share
## 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):

![AddressSanitizer crash: SEGV READ in memcpy, called from btrfs_read_extent_reg fs/btrfs/inode.c:514](assets/asan_crash.png)

```
==...==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.
```