Document the three runZero "Seven FatFs bugs" CVEs that require no source change
in this component, so vulnerability scanners have their disposition:
- CVE-2026-6684: GPT partition-scan loop DoS. Already fixed upstream in R0.16,
where test_gpt_header() caps the partition-entry count at 128.
- CVE-2026-6686: read of uninitialized clusters after f_lseek() past EOF.
Longstanding, behavioral; not a memory-safety defect and zero-filling every
extended cluster is prohibitively costly on flash.
- CVE-2026-6688: long-filename overflow in downstream callers. Not exposed in
ESP-IDF; vfs_fat.c uses bounded copies and fname is bounded by FF_MAX_LFN.
Reference: https://www.runzero.com/blog/fatfs-bugs/
f_getlabel() extracts the exFAT volume label with a loop bounded by the on-disk
byte dj.dir[XDIR_NumLabel] (0-255):
for (si = di = hs = 0; si < dj.dir[XDIR_NumLabel]; si++)
wc = ld_16(dj.dir + XDIR_Label + si * 2);
The exFAT label field holds at most 11 UTF-16 units (22 bytes). A crafted
directory entry with a larger count both reads past the 22-byte label field and,
through put_utf(... &label[di], 4), writes past the end of the caller-provided
label buffer (the canonical API examples use small fixed stack buffers) -> stack
buffer overflow.
Clamp the character count to the exFAT maximum of 11 before the extraction loop.
Record the CVE in the component SBOM.
Note: f_getlabel() takes no destination-buffer size, so under UTF-8 output
(FF_LFN_UNICODE == 2) 11 units can still expand to up to 34 bytes; the clamp
downgrades this from attacker-unbounded to spec-bounded. ESP-IDF's VFS layer
does not call f_getlabel(); direct callers on untrusted media should size their
buffer accordingly. A complete fix requires an upstream size-aware API change.
Reference: https://www.runzero.com/blog/fatfs-bugs/
After a direct multi-sector disk_read()/disk_write(), FatFs decides whether the
cached sector overlaps the direct-I/O range with:
fp->sect - sect < cc (and the FF_FS_TINY variant fs->winsect - sect < cc)
`sect`, `fp->sect` and `fs->winsect` are unsigned LBA_t. On 32-bit LBA_t builds,
if the cached sector is below `sect`, the subtraction wraps to a huge value that
can still compare `< cc`, so the code computes a bogus large offset:
- in f_write() it mis-copies from the direct write buffer (data corruption);
- in f_read() it is worse: memcpy(rbuff + (wrapped_offset * SS), ...) is an
out-of-bounds WRITE into the caller-supplied read buffer.
Add an explicit lower-bound check (fp->sect >= sect, resp. fs->winsect >= sect)
before the range test on both the read and write paths and both the FF_FS_TINY
and normal variants, so the condition is exactly "cached sector lies within
[sect, sect + cc)". Record the CVE in the component SBOM.
Reference: https://www.runzero.com/blog/fatfs-bugs/
The FAT12/16/32 mount path rejects a zero cluster count, but the exFAT path
accepted NumClusters == 0. That yields fs->n_fatent == 2, and sync_fs() later
computes the "percent in use" field as:
... * 100 / (fs->n_fatent - 2)
which is a division by zero (n_fatent - 2 == 0) -> crash. On a device that
syncs during an update this can brick the unit.
Reject ncl == 0 at exFAT mount time, and add a defense-in-depth
`fs->n_fatent > 2` guard around the division in sync_fs() so the divisor can
never be zero even if some future path produces such a filesystem object.
Record the CVE in the component SBOM.
Reference: https://www.runzero.com/blog/fatfs-bugs/
The exFAT mount path validates that the media is large enough to hold the
declared cluster heap with:
if (maxlba < (QWORD)fs->database + ncl * fs->csize) ...
`ncl` (DWORD, up to MAX_EXFAT) and `fs->csize` (WORD) are both promoted to
`unsigned int`, so `ncl * fs->csize` is evaluated in 32-bit arithmetic and can
wrap before the QWORD promotion of the sum. A crafted image with a large
NumClusters/SecPerClus can therefore make an undersized volume pass the "size
is large enough" check; subsequent cluster->sector math then addresses media
outside the actual device.
Promote the multiply to 64-bit ((QWORD)ncl * fs->csize). Apply the same
promotion to the bitmap-base computation ((LBA_t)fs->csize * (bcl - 2)), which
has the identical overflow shape. Record the CVE in the component SBOM.
Reference: https://www.runzero.com/blog/fatfs-bugs/
The vendored FatFs sources are revision R0.16 (FF_DEFINED == 80386, per
components/fatfs/src/ff.h and ff.c) but the SBOM recorded R0.15. Correct the
recorded version so vulnerability tracking matches the actual sources.
Reformat the test partition before each mount so those tests always start from
a known-empty filesystem.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move SDM regdma retention descriptors out of esp_hal_gpio and into
per-target
esp_driver_sdm sources so the driver owns its backup scope and restore
flow.
Remove ~50 duplicate local definitions of ALIGN_UP/ALIGN_DOWN/ALIGN_UP_BY/
ALIGN_DOWN_BY across the codebase and replace them with canonical
ESP_ALIGN_UP/ESP_ALIGN_DOWN from esp_macros.h.
fix(bt/controller): Fixed BR/EDR controller bugs found by regression tests on ESP32-S31
Closes BTQABR2023-776, BTQABR2023-786, BTQABR2023-800, BT-4378, BT-4381, and BT-4386
See merge request espressif/esp-idf!50251