Hello, The ISO-8859-14 charset2lower table maps byte 0xa4 (U+010A) to 0xab, the lowercase byte for U+1E0A. VFAT uses this table for its default case-insensitive lookup, so a lookup for the distinct, absent byte 0xa4 can open a file displayed by readdir() as 0xa6. The PoC mounts a small VFAT image with iocharset=iso8859-14 and uni_xlate=1, creates only :1e0a, and then looks up byte 0xa4. On the unmodified kernel, readdir() lists 0xa6 while stat(0xa4) succeeds and returns the created file. Remounting with check=s makes 0xa4 return ENOENT. The issue is in charset2lower[] and is independent of the page01/page1e conversion-table fix in 8e17b922aff0. git tree: upstream torvalds/linux master source version: Linux 7.3.0-17714-gc339f4010dea architecture: x86-64 compiler: gcc 11.4.0, GNU ld 2.38 QEMU: 6.2.0 console output: https://gist.githubusercontent.com/shaos26/4115e33780bb1ea2a0e042aa6e6588da/raw/9dfb43bd1f99e1d9138cf8147fe29eb0f131978b/console-output.txt kernel config: https://gist.githubusercontent.com/shaos26/4115e33780bb1ea2a0e042aa6e6588da/raw/7481bf507133c8dcb3549ed04ef4abe8de69cd48/kernel-config.txt reproducer: https://gist.githubusercontent.com/shaos26/4115e33780bb1ea2a0e042aa6e6588da/raw/54bd8643014abaaa49a24cbc7ff8c93329dd62b6/poc.c Build the kernel from HEAD with the attached config, then build the PoC and initramfs: mkdir -p build rootfs/dev rootfs/mnt rootfs/proc rootfs/sys cp kernel-config.txt build/.config make O=build olddefconfig make O=build -j"$(nproc)" bzImage gcc -O2 -static -Wall -Wextra -o reproducer poc.c cp reproducer rootfs/init (cd rootfs && find . -print0 | cpio --null -o -H newc | \ gzip -9n) > initramfs.cpio.gz truncate -s 32M fat.img mkfs.vfat -F 16 fat.img Run the guest with: timeout 120s qemu-system-x86_64 -machine q35 \ -accel tcg,thread=multi -cpu max -smp 1 -m 2048 \ -kernel build/arch/x86/boot/bzImage \ -initrd initramfs.cpio.gz \ -append "console=ttyS0 rdinit=/init" \ -drive file=fat.img,format=raw,if=virtio,snapshot=on \ -display none -serial stdio -monitor none -no-reboot The same PoC prints RESULT: VULNERABLE on the original tree and RESULT: FIXED after the change below. In both cases, the check=s control returns ENOENT for 0xa4 while byte 0xa6 remains accessible. Mounting VFAT requires the privilege needed to mount a filesystem. This report demonstrates filename lookup confusion, not privilege escalation or memory corruption. Link: https://lore.kernel.org/20260909121032.76647-1-aliamemis@disroot.org Signed-off-by: shaos --- Validation: Fresh QEMU runs on unmodified c339f401 and on the patched kernel, both with one vCPU and the same configuration, printed RESULT: VULNERABLE and RESULT: FIXED respectively. scripts/checkpatch.pl --no-tree --strict reports no errors or warnings, and git apply --check succeeds. diff --git a/fs/nls/nls_iso8859-14.c b/fs/nls/nls_iso8859-14.c index 60b9400f915af..c9bc4e0d30603 100644 --- a/fs/nls/nls_iso8859-14.c +++ b/fs/nls/nls_iso8859-14.c @@ -240,7 +240,7 @@ static const unsigned char charset2lower[256] = { 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */ - 0xa0, 0xa2, 0xa2, 0xa3, 0xab, 0xab, 0xab, 0xa7, /* 0xa0-0xa7 */ + 0xa0, 0xa2, 0xa2, 0xa3, 0xa5, 0xa5, 0xab, 0xa7, /* 0xa0-0xa7 */ 0xb8, 0xa9, 0xba, 0xab, 0xbc, 0xad, 0xae, 0xff, /* 0xa8-0xaf */ 0xb1, 0xb1, 0xb3, 0xb3, 0xb5, 0xb5, 0xb6, 0xb9, /* 0xb0-0xb7 */ 0xb8, 0xb9, 0xba, 0xbf, 0xbc, 0xbe, 0xbe, 0xbf, /* 0xb8-0xbf */ Thanks, shaos