]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
fat: reject name longer than NAME_MAX in msdos_format_name()
authorZizhi Wo <wozizhi@huawei.com>
Mon, 29 Jun 2026 13:09:43 +0000 (21:09 +0800)
committerChristian Brauner <brauner@kernel.org>
Tue, 30 Jun 2026 11:01:05 +0000 (13:01 +0200)
msdos_format_name() performs no upper-bound check on the input name length.
It silently truncates an arbitrarily long name into the 8.3 form (11 bytes)
and returns success. The subsequent fat_scan() then matches only against
these 11 truncated bytes, so it returns an inode as long as any entry with
the same 8.3 name exists on disk.

For example, passing a 300-byte name of all 'A's returns 0 with res set to
"AAAAAAAA" (8 'A's + 3 padding spaces), reporting success for a name far
longer than NAME_MAX.

As a result, when a user calls open() on a path component longer than
NAME_MAX (255) bytes, the VFS only enforces PATH_MAX, not the length of an
individual component. The dentry keeps the original long name but gets an
inode attached and becomes positive. Later in vfs_open() -> fsnotify_open()
-> fanotify_info_copy_name() triggers WARN_ON_ONCE(), and the event is
reported to userspace with an empty name.

vfat is not affected, as create goes through xlate_to_uni() which refuses
names longer than FAT_LFN_LEN.

Fix this by checking 'len > NAME_MAX' at the entry of msdos_format_name(),
the single entry point for all msdos name handling, aligning with the
NAME_MAX check that xfs/9p/ceph/simple_lookup() perform at lookup.

Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
Link: https://patch.msgid.link/20260629130943.3671939-1-wozizhi@huaweicloud.com
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
fs/fat/namei_msdos.c

index 0fd2971ad4b13143a5b4fa1e1e403904fa6bf38a..c93e05d35ef8995d416feef4d01827d2fc4e8036 100644 (file)
@@ -29,6 +29,9 @@ static int msdos_format_name(const unsigned char *name, int len,
        unsigned char c;
        int space;
 
+       if (len > NAME_MAX)
+               return -ENAMETOOLONG;
+
        if (name[0] == '.') {   /* dotfile because . and .. already done */
                if (opts->dotsOK) {
                        /* Get rid of dot - test for it elsewhere */