diff --git a/components/fatfs/sbom.yml b/components/fatfs/sbom.yml index d9e037070cb..6f99c066ad8 100644 --- a/components/fatfs/sbom.yml +++ b/components/fatfs/sbom.yml @@ -1,5 +1,20 @@ name: 'FatFs' -version: 'R0.15' +version: 'R0.16' supplier: 'Organization: Espressif Systems (Shanghai) CO LTD' originator: 'Person: ChaN' description: 'Generic FAT Filesystem Module for embedded systems.' +cve-exclude-list: + - cve: CVE-2026-6682 + reason: Integer overflow in exFAT mount size validation. Patched by promoting the cluster-heap and bitmap-base multiplies to 64-bit in mount_volume(). + - cve: CVE-2026-6683 + reason: exFAT divide-by-zero when NumClusters == 0. Patched by rejecting an empty cluster heap at mount and guarding the divisor (n_fatent > 2) in sync_fs(). + - cve: CVE-2026-6685 + reason: Unsigned-subtraction wrap in the dirty-cache refill check. Patched by requiring the cached sector to lie within the direct-I/O range in both f_write() and f_read(). + - cve: CVE-2026-6687 + reason: exFAT label-length overflow in f_getlabel(). Patched by clamping XDIR_NumLabel to the 11-unit exFAT label maximum. + - cve: CVE-2026-6684 + reason: GPT partition-scan loop DoS. Already fixed upstream in FatFs R0.16, where test_gpt_header() caps the partition-entry count at 128. + - cve: CVE-2026-6686 + reason: Read of uninitialized clusters after f_lseek() extends a file past EOF. Longstanding FatFs behavior; not a memory-safety defect and zero-filling every extended cluster is prohibitively costly on flash. Applications sharing media across trust boundaries must zero regions before exposing them. + - cve: CVE-2026-6688 + reason: Long-filename overflow in downstream callers copying FILINFO.fname. Not exposed in ESP-IDF; vfs_fat.c uses bounded snprintf/strlcpy and fname is bounded in-library by FF_MAX_LFN. diff --git a/components/fatfs/src/ff.c b/components/fatfs/src/ff.c index b5290534475..5afeee2d5d7 100644 --- a/components/fatfs/src/ff.c +++ b/components/fatfs/src/ff.c @@ -1133,7 +1133,7 @@ static FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */ #if FF_FS_EXFAT else if (fs->fs_type == FS_EXFAT) { /* exFAT: Update PercInUse field in BPB */ if (disk_read(fs->pdrv, fs->win, fs->winsect = fs->volbase, 1) == RES_OK) { /* Load VBR */ - BYTE perc_inuse = (fs->free_clst <= fs->n_fatent - 2) ? (BYTE)((QWORD)(fs->n_fatent - 2 - fs->free_clst) * 100 / (fs->n_fatent - 2)) : 0xFF; /* Precent in use 0-100 or 0xFF(unknown) */ + BYTE perc_inuse = (fs->n_fatent > 2 && fs->free_clst <= fs->n_fatent - 2) ? (BYTE)((QWORD)(fs->n_fatent - 2 - fs->free_clst) * 100 / (fs->n_fatent - 2)) : 0xFF; /* Precent in use 0-100 or 0xFF(unknown). CVE-2026-6683: guard divisor (n_fatent-2) against zero */ if (fs->win[BPB_PercInUseEx] != perc_inuse) { /* Write it back into VBR if needed */ fs->win[BPB_PercInUseEx] = perc_inuse; @@ -3554,13 +3554,14 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ ncl = ld_32(fs->win + BPB_NumClusEx); /* Number of clusters */ if (ncl > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */ + if (ncl == 0) return FR_NO_FILESYSTEM; /* CVE-2026-6683: reject empty cluster heap (n_fatent-2==0 causes divide-by-zero in sync_fs) */ fs->n_fatent = ncl + 2; /* Boundaries and Limits */ fs->volbase = bsect; fs->database = bsect + ld_32(fs->win + BPB_DataOfsEx); fs->fatbase = bsect + ld_32(fs->win + BPB_FatOfsEx); - if (maxlba < (QWORD)fs->database + ncl * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size required) */ + if (maxlba < (QWORD)fs->database + (QWORD)ncl * fs->csize) return FR_NO_FILESYSTEM; /* CVE-2026-6682: promote to 64-bit before multiply to avoid integer overflow that would accept an undersized volume */ fs->dirbase = ld_32(fs->win + BPB_RootClusEx); /* Get bitmap location and check if it is contiguous (implementation assumption) */ @@ -3576,7 +3577,7 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ } bcl = ld_32(fs->win + i + 20); /* Bitmap cluster */ if (bcl < 2 || bcl >= fs->n_fatent) return FR_NO_FILESYSTEM; /* (Wrong cluster#) */ - fs->bitbase = fs->database + fs->csize * (bcl - 2); /* Bitmap sector */ + fs->bitbase = fs->database + (LBA_t)fs->csize * (bcl - 2); /* Bitmap sector (CVE-2026-6682: 64-bit multiply to avoid overflow) */ for (;;) { /* Check if bitmap is contiguous */ if (move_window(fs, fs->fatbase + bcl / (SS(fs) / 4)) != FR_OK) return FR_DISK_ERR; cv = ld_32(fs->win + bcl % (SS(fs) / 4) * 4); @@ -4083,11 +4084,11 @@ FRESULT f_read ( if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); #if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ #if FF_FS_TINY - if (fs->wflag && fs->winsect - sect < cc) { + if (fs->wflag && fs->winsect >= sect && fs->winsect - sect < cc) { /* CVE-2026-6685: guard against unsigned wrap when winsect < sect (mis-offset would be an OOB write into rbuff) */ memcpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs)); } #else - if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) { + if ((fp->flag & FA_DIRTY) && fp->sect >= sect && fp->sect - sect < cc) { /* CVE-2026-6685: guard against unsigned wrap when fp->sect < sect (mis-offset would be an OOB write into rbuff) */ memcpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); } #endif @@ -4198,12 +4199,12 @@ FRESULT f_write ( if (disk_write(fs->pdrv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); #if FF_FS_MINIMIZE <= 2 #if FF_FS_TINY - if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + if (fs->winsect >= sect && fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write (CVE-2026-6685: guard against unsigned wrap when winsect < sect) */ memcpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs)); fs->wflag = 0; } #else - if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + if (fp->sect >= sect && fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write (CVE-2026-6685: guard against unsigned wrap when fp->sect < sect) */ memcpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs)); fp->flag &= (BYTE)~FA_DIRTY; } @@ -5565,9 +5566,11 @@ FRESULT f_getlabel ( #if FF_FS_EXFAT if (fs->fs_type == FS_EXFAT) { WCHAR hs; - UINT nw; + UINT nw, nchar; - for (si = di = hs = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */ + nchar = dj.dir[XDIR_NumLabel]; /* Number of UTF-16 characters in the label entry */ + if (nchar > 11) nchar = 11; /* CVE-2026-6687: clamp to the exFAT maximum (11) to prevent OOB read of the entry and overflow of the caller label buffer */ + for (si = di = hs = 0; si < nchar; si++) { /* Extract volume label from 83 entry */ wc = ld_16(dj.dir + XDIR_Label + si * 2); if (hs == 0 && IsSurrogate(wc)) { /* Is the code a surrogate? */ hs = wc; continue;