The initial CVE-2026-6682 fix (merged in !50362) hardened the exFAT
mount path, but the CVE as reported by runZero is a FAT32 defect in
mount_volume() and is reachable in ESP-IDF's default configuration
(exFAT and 64-bit LBA disabled). This corrects the fix.
Root cause: `fasize *= fs->n_fats` is a DWORD multiply with no overflow
guard. A crafted BPB_FATSz32 such as 0x80000001 with NumFATs=2 wraps
`fasize` to 0x00000002. The wrapped (too-small) FAT size then places
`fs->database` inside the FAT region, so a forged directory entry in the
overlapping sector yields an attacker-controlled `finfo.fsize`. Any
caller that uses that size as a read length overflows its buffer with
attacker-controlled bytes (CVSS 7.6, path to RCE). The later
`fs->fsize < szbfat` check does not catch this because it compares the
un-doubled single-FAT size, which is still large.
Fix: reject a per-FAT size that overflows DWORD when multiplied by the
FAT count, and reject a reserved+FAT+root system-area size that overflows
DWORD, before either value is used to derive the data-area base.
The previous exFAT cluster-heap/bitmap 64-bit promotions are retained as
defense-in-depth and their comments relabeled accordingly (they are not
CVE-2026-6682). The SBOM cve-exclude-list reason is updated to describe
the FAT32 overflow and its fix.
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/
Legacy fatfs code being ported to ESP-IDF may use `f_gets()`, `f_puts()`,
`f_putc()`, and `f_printf()` calls, but the define `FF_USE_STRFUNC` is set to 0
without Kconfig options to enable it.
This commit retains the existing default behavior of `FF_USE_STRFUNC=0` and
adds Kconfig settings so users can configure `FF_USE_STRFUNC` and the related
`FF_PRINT_LLI`, `FF_PRINT_FLOAT`, and `FF_STRF_ENCODE` if needed.
Closes: https://github.com/espressif/esp-idf/issues/13350
Signed-off-by: Eric Wheeler <esp-idf@z.ewheeler.org>
FF_USE_TRIM is set by default with this commit. Fatfs invokes disk_ioctl
with CTRL_TRIM to erase the sectors calling sdmmc_erase_sectors to choose the right
argument for the erase operation based on media type.
This is a breaking change: applications which used diskio.h to
call ff_diskio_register, will now need to include diskio_impl.h.
Including diskio.h will include the original diskio.h header from
FatFS library.
Fixed problem with multiple mount/unmount for different devices.
Additional check for structure length included into the code.
Remove useless spaces.
Added initialization for reserved memory.
components/fatfs/src/ff.c: In function 'f_fdisk':
components/fatfs/src/ff.c:5995:5: error: this 'for' clause does not guard... [-Werror=misleading-indentation]
for (n = 16; n < 256 && sz_disk / n / cluster_size > 1024; n *= 2) ;
^~~
components/fatfs/src/ff.c:5996:2: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'for'
if (n == 256) n--;
^~
This change allows readonly FATFS to be mounted without wear levelling
support. This will provide the customers a simple way to mount FATFS images
generated on host and flashed onto the chip during factory provisioning.
Since NVS encryption is not supported yet and NVS entry size is limited,
the change will provide an easy alternative for securing the provisioning data
by just marking FATFS parition as encrypted.
This commit adds character encoding configurations in for the fatfs component.
The FF_LFN_UNICODE definition in ffconf.h can now be changed to accept UTF-8 or
UTF-16 encoded filernames. Test cases using UTF-8 encoded file paths and names in
FatFs have also been added.
Closes#1183