VYPR
Unrated severityNVD Advisory· Published May 27, 2026· Updated May 27, 2026

CVE-2026-45915

CVE-2026-45915

Description

In the Linux kernel, the following vulnerability has been resolved:

fat: avoid parent link count underflow in rmdir

Corrupted FAT images can leave a directory inode with an incorrect i_nlink (e.g. 2 even though subdirectories exist). rmdir then unconditionally calls drop_nlink(dir) and can drive i_nlink to 0, triggering the WARN_ON in drop_nlink().

Add a sanity check in vfat_rmdir() and msdos_rmdir(): only drop the parent link count when it is at least 3, otherwise report a filesystem error.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

FAT filesystem driver in Linux kernel had an rmdir parent link count underflow that could be triggered by corrupted directory inodes.

Vulnerability

In the Linux kernel's FAT filesystem driver, both vfat_rmdir() and msdos_rmdir() unconditionally called drop_nlink(dir) when removing a directory entry. A corrupted FAT image could present a directory inode with an incorrect i_nlink value (e.g., 2 even when subdirectories exist). Calling drop_nlink() in such cases could drive the link count to 0, triggering the WARN_ON in drop_nlink() [1]. The fix adds a sanity check so the link count is only decremented when it is at least 3; otherwise an error is reported [1].

Exploitation

An attacker would need the ability to mount a crafted FAT filesystem image on a system running an affected kernel. The image must contain a directory inode with an i_nlink value artificially set to 2 (or another value <3) while having child subdirectories. When a user or process runs rmdir on that directory, the kernel triggers the bug [1]. No special privileges beyond mounting a filesystem (typically requiring root or CAP_SYS_ADMIN) are needed after the crafted image is placed.

Impact

On success, the attacker causes a kernel WARN_ON splat. While the patch notes do not describe a broader denial-of-service or memory corruption, the triggered warning could cause system instability or be used to fill kernel logs. The primary impact is availability degradation via the warning [1].

Mitigation

The fix was committed in Linux kernel stable branches. Users should update their kernel to a version containing commit cd569b87378b (or its backport) [1]. No workaround is available other than avoiding the use of untrusted FAT filesystem images. The issue is not listed on CISA's Known Exploited Vulnerabilities (KEV) catalog as of publication date.

AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

16
8cafcb881364

fat: avoid parent link count underflow in rmdir

2 files changed · +12 3
  • fs/fat/namei_msdos.c+6 1 modified
    diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
    index 0b920ee40a7f9f..262ec1b790b560 100644
    --- a/fs/fat/namei_msdos.c
    +++ b/fs/fat/namei_msdos.c
    @@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_CTIME);
    
  • fs/fat/namei_vfat.c+6 2 modified
    diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
    index 4f3cc2b3089e74..8bf5f7a9fd2382 100644
    --- a/fs/fat/namei_vfat.c
    +++ b/fs/fat/namei_vfat.c
    @@ -804,7 +804,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
    -- 
    cgit 1.3-korg
    
    
    
7fe0de287e93

fat: avoid parent link count underflow in rmdir

2 files changed · +12 3
  • fs/fat/namei_msdos.c+6 1 modified
    diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
    index 9d062886fbc19d..63a323be9179df 100644
    --- a/fs/fat/namei_msdos.c
    +++ b/fs/fat/namei_msdos.c
    @@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_CTIME);
    
  • fs/fat/namei_vfat.c+6 2 modified
    diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
    index 9bc7d1602c15b7..2a0d9a9c2c8f79 100644
    --- a/fs/fat/namei_vfat.c
    +++ b/fs/fat/namei_vfat.c
    @@ -808,7 +808,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
    -- 
    cgit 1.3-korg
    
    
    
9894c79fd946

fat: avoid parent link count underflow in rmdir

2 files changed · +12 3
  • fs/fat/namei_msdos.c+6 1 modified
    diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
    index efba301d68aec8..fba3a07d39478d 100644
    --- a/fs/fat/namei_msdos.c
    +++ b/fs/fat/namei_msdos.c
    @@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_CTIME);
    
  • fs/fat/namei_vfat.c+6 2 modified
    diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
    index e69e2a4f99b928..d8e328e390d6b8 100644
    --- a/fs/fat/namei_vfat.c
    +++ b/fs/fat/namei_vfat.c
    @@ -808,7 +808,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
    -- 
    cgit 1.3-korg
    
    
    
cd569b87378b

fat: avoid parent link count underflow in rmdir

2 files changed · +12 3
  • fs/fat/namei_msdos.c+6 1 modified
    diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
    index efba301d68aec8..fba3a07d39478d 100644
    --- a/fs/fat/namei_msdos.c
    +++ b/fs/fat/namei_msdos.c
    @@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_CTIME);
    
  • fs/fat/namei_vfat.c+6 2 modified
    diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
    index 93fa8ddcf41452..3a9a849bc7885a 100644
    --- a/fs/fat/namei_vfat.c
    +++ b/fs/fat/namei_vfat.c
    @@ -806,7 +806,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
    -- 
    cgit 1.3-korg
    
    
    
d3b7ffa90f61

fat: avoid parent link count underflow in rmdir

2 files changed · +12 3
  • fs/fat/namei_msdos.c+6 1 modified
    diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
    index 2116c486843b7d..e189fbf95fcace 100644
    --- a/fs/fat/namei_msdos.c
    +++ b/fs/fat/namei_msdos.c
    @@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_CTIME);
    
  • fs/fat/namei_vfat.c+6 2 modified
    diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
    index 3cf22a6727f1b6..7d7ac30c6eff81 100644
    --- a/fs/fat/namei_vfat.c
    +++ b/fs/fat/namei_vfat.c
    @@ -806,7 +806,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
    -- 
    cgit 1.3-korg
    
    
    
955c5d670b5a

fat: avoid parent link count underflow in rmdir

2 files changed · +12 3
  • fs/fat/namei_msdos.c+6 1 modified
    diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
    index f06f6ba643cc8c..c75e3791514a23 100644
    --- a/fs/fat/namei_msdos.c
    +++ b/fs/fat/namei_msdos.c
    @@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_CTIME);
    
  • fs/fat/namei_vfat.c+6 2 modified
    diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
    index 15bf32c21ac0db..31d37e2b4f6793 100644
    --- a/fs/fat/namei_vfat.c
    +++ b/fs/fat/namei_vfat.c
    @@ -806,7 +806,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
    -- 
    cgit 1.3-korg
    
    
    
17866f8a0822

fat: avoid parent link count underflow in rmdir

2 files changed · +12 3
  • fs/fat/namei_msdos.c+6 1 modified
    diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
    index 0b920ee40a7f9f..262ec1b790b560 100644
    --- a/fs/fat/namei_msdos.c
    +++ b/fs/fat/namei_msdos.c
    @@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_CTIME);
    
  • fs/fat/namei_vfat.c+6 2 modified
    diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
    index 5dbc4cbb8fce3d..47ff083cfc7e66 100644
    --- a/fs/fat/namei_vfat.c
    +++ b/fs/fat/namei_vfat.c
    @@ -803,7 +803,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
    -- 
    cgit 1.3-korg
    
    
    
d0bb592fa9de

fat: avoid parent link count underflow in rmdir

2 files changed · +12 3
  • fs/fat/namei_msdos.c+6 1 modified
    diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
    index 0b920ee40a7f9f..262ec1b790b560 100644
    --- a/fs/fat/namei_msdos.c
    +++ b/fs/fat/namei_msdos.c
    @@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_CTIME);
    
  • fs/fat/namei_vfat.c+6 2 modified
    diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
    index 5dbc4cbb8fce3d..47ff083cfc7e66 100644
    --- a/fs/fat/namei_vfat.c
    +++ b/fs/fat/namei_vfat.c
    @@ -803,7 +803,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
    -- 
    cgit 1.3-korg
    
    
    
8cafcb881364

fat: avoid parent link count underflow in rmdir

2 files changed · +12 3
  • fs/fat/namei_msdos.c+6 1 modified
    diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
    index 0b920ee40a7f9f..262ec1b790b560 100644
    --- a/fs/fat/namei_msdos.c
    +++ b/fs/fat/namei_msdos.c
    @@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_CTIME);
    
  • fs/fat/namei_vfat.c+6 2 modified
    diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
    index 4f3cc2b3089e74..8bf5f7a9fd2382 100644
    --- a/fs/fat/namei_vfat.c
    +++ b/fs/fat/namei_vfat.c
    @@ -804,7 +804,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
    -- 
    cgit 1.3-korg
    
    
    
17866f8a0822

fat: avoid parent link count underflow in rmdir

2 files changed · +12 3
  • fs/fat/namei_msdos.c+6 1 modified
    diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
    index 0b920ee40a7f9f..262ec1b790b560 100644
    --- a/fs/fat/namei_msdos.c
    +++ b/fs/fat/namei_msdos.c
    @@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_CTIME);
    
  • fs/fat/namei_vfat.c+6 2 modified
    diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
    index 5dbc4cbb8fce3d..47ff083cfc7e66 100644
    --- a/fs/fat/namei_vfat.c
    +++ b/fs/fat/namei_vfat.c
    @@ -803,7 +803,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
    -- 
    cgit 1.3-korg
    
    
    
7fe0de287e93

fat: avoid parent link count underflow in rmdir

2 files changed · +12 3
  • fs/fat/namei_msdos.c+6 1 modified
    diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
    index 9d062886fbc19d..63a323be9179df 100644
    --- a/fs/fat/namei_msdos.c
    +++ b/fs/fat/namei_msdos.c
    @@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_CTIME);
    
  • fs/fat/namei_vfat.c+6 2 modified
    diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
    index 9bc7d1602c15b7..2a0d9a9c2c8f79 100644
    --- a/fs/fat/namei_vfat.c
    +++ b/fs/fat/namei_vfat.c
    @@ -808,7 +808,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
    -- 
    cgit 1.3-korg
    
    
    
955c5d670b5a

fat: avoid parent link count underflow in rmdir

2 files changed · +12 3
  • fs/fat/namei_msdos.c+6 1 modified
    diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
    index f06f6ba643cc8c..c75e3791514a23 100644
    --- a/fs/fat/namei_msdos.c
    +++ b/fs/fat/namei_msdos.c
    @@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_CTIME);
    
  • fs/fat/namei_vfat.c+6 2 modified
    diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
    index 15bf32c21ac0db..31d37e2b4f6793 100644
    --- a/fs/fat/namei_vfat.c
    +++ b/fs/fat/namei_vfat.c
    @@ -806,7 +806,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
    -- 
    cgit 1.3-korg
    
    
    
9894c79fd946

fat: avoid parent link count underflow in rmdir

2 files changed · +12 3
  • fs/fat/namei_msdos.c+6 1 modified
    diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
    index efba301d68aec8..fba3a07d39478d 100644
    --- a/fs/fat/namei_msdos.c
    +++ b/fs/fat/namei_msdos.c
    @@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_CTIME);
    
  • fs/fat/namei_vfat.c+6 2 modified
    diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
    index e69e2a4f99b928..d8e328e390d6b8 100644
    --- a/fs/fat/namei_vfat.c
    +++ b/fs/fat/namei_vfat.c
    @@ -808,7 +808,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
    -- 
    cgit 1.3-korg
    
    
    
cd569b87378b

fat: avoid parent link count underflow in rmdir

2 files changed · +12 3
  • fs/fat/namei_msdos.c+6 1 modified
    diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
    index efba301d68aec8..fba3a07d39478d 100644
    --- a/fs/fat/namei_msdos.c
    +++ b/fs/fat/namei_msdos.c
    @@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_CTIME);
    
  • fs/fat/namei_vfat.c+6 2 modified
    diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
    index 93fa8ddcf41452..3a9a849bc7885a 100644
    --- a/fs/fat/namei_vfat.c
    +++ b/fs/fat/namei_vfat.c
    @@ -806,7 +806,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
    -- 
    cgit 1.3-korg
    
    
    
d0bb592fa9de

fat: avoid parent link count underflow in rmdir

2 files changed · +12 3
  • fs/fat/namei_msdos.c+6 1 modified
    diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
    index 0b920ee40a7f9f..262ec1b790b560 100644
    --- a/fs/fat/namei_msdos.c
    +++ b/fs/fat/namei_msdos.c
    @@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_CTIME);
    
  • fs/fat/namei_vfat.c+6 2 modified
    diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
    index 5dbc4cbb8fce3d..47ff083cfc7e66 100644
    --- a/fs/fat/namei_vfat.c
    +++ b/fs/fat/namei_vfat.c
    @@ -803,7 +803,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
    -- 
    cgit 1.3-korg
    
    
    
d3b7ffa90f61

fat: avoid parent link count underflow in rmdir

2 files changed · +12 3
  • fs/fat/namei_msdos.c+6 1 modified
    diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
    index 2116c486843b7d..e189fbf95fcace 100644
    --- a/fs/fat/namei_msdos.c
    +++ b/fs/fat/namei_msdos.c
    @@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_CTIME);
    
  • fs/fat/namei_vfat.c+6 2 modified
    diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
    index 3cf22a6727f1b6..7d7ac30c6eff81 100644
    --- a/fs/fat/namei_vfat.c
    +++ b/fs/fat/namei_vfat.c
    @@ -806,7 +806,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
     	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
     	if (err)
     		goto out;
    -	drop_nlink(dir);
    +	if (dir->i_nlink >= 3)
    +		drop_nlink(dir);
    +	else {
    +		fat_fs_error(sb, "parent dir link count too low (%u)",
    +			dir->i_nlink);
    +	}
     
     	clear_nlink(inode);
     	fat_truncate_time(inode, NULL, S_ATIME|S_MTIME);
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Missing sanity check on parent directory i_nlink before decrementing in rmdir allows link count underflow on corrupted FAT images."

Attack vector

An attacker who can mount a corrupted FAT image (or provide one to a victim) can craft the filesystem metadata so that a directory inode has an incorrect `i_nlink` value — for example, `i_nlink` is 2 even though subdirectories exist [patch_id=2661399]. When a user or process calls `rmdir` on a subdirectory under that parent, the kernel unconditionally calls `drop_nlink(dir)`, which decrements `i_nlink` from 2 to 1, and then on a subsequent rmdir from 1 to 0, triggering the `WARN_ON` in `drop_nlink()` and potentially causing a kernel crash or denial of service. No special privileges beyond the ability to mount a crafted FAT filesystem are required.

Affected code

The vulnerability is in the `vfat_rmdir()` function in `fs/fat/namei_vfat.c` and the `msdos_rmdir()` function in `fs/fat/namei_msdos.c` [patch_id=2661399]. Both functions unconditionally called `drop_nlink(dir)` after removing directory entries, without checking whether the parent directory's `i_nlink` was already too low.

What the fix does

The patch adds a guard in both `vfat_rmdir()` and `msdos_rmdir()`: `drop_nlink(dir)` is only called when `dir->i_nlink >= 3` [patch_id=2661399]. If the link count is below 3 (the minimum expected for a directory with a `..` entry and at least one subdirectory), the code instead calls `fat_fs_error(sb, "parent dir link count too low (%u)", dir->i_nlink)` to log a filesystem error and skips the decrement. This prevents the `i_nlink` underflow that would trigger the `WARN_ON` in `drop_nlink()`.

Preconditions

  • inputAttacker must supply a corrupted FAT filesystem image where a directory inode has an i_nlink value less than 3 (e.g., 2) despite having subdirectories
  • configThe victim must mount the crafted FAT image and perform rmdir operations on subdirectories

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.