mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] nls: fix ISO-8859-14 case folding
@ 2026-10-29  9:14 shaos
  2026-10-29 10:10 ` Ali Ahmet Memiş
  2026-10-29 10:21 ` OGAWA Hirofumi
  0 siblings, 2 replies; 3+ messages in thread
From: shaos @ 2026-10-29  9:14 UTC (permalink / raw)
  To: brauner, aliamemis; +Cc: OGAWA Hirofumi, linux-fsdevel, linux-kernel

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 <shawnq1@163.com>
---

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


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] nls: fix ISO-8859-14 case folding
  2026-10-29  9:14 [PATCH] nls: fix ISO-8859-14 case folding shaos
@ 2026-10-29 10:10 ` Ali Ahmet Memiş
  2026-10-29 10:21 ` OGAWA Hirofumi
  1 sibling, 0 replies; 3+ messages in thread
From: Ali Ahmet Memiş @ 2026-10-29 10:10 UTC (permalink / raw)
  To: shaos; +Cc: brauner, OGAWA Hirofumi, linux-fsdevel, linux-kernel

On Thu, Oct 29, 2026 at 09:14:43AM +0000, shaos wrote:
> Build the kernel from HEAD with the attached config, then build the
> PoC and initramfs:

This and the rest of the reproducer doesn't belong in commit
message, please move it below the --- line.

> -	0xa0, 0xa2, 0xa2, 0xa3, 0xab, 0xab, 0xab, 0xa7, /* 0xa0-0xa7 */
> +	0xa0, 0xa2, 0xa2, 0xa3, 0xa5, 0xa5, 0xab, 0xa7, /* 0xa0-0xa7 */

charset2upper has the same problem (0xa4 and 0xa5 both map to 0xa6
there).  It should be

	0xa4, 0xa4.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] nls: fix ISO-8859-14 case folding
  2026-10-29  9:14 [PATCH] nls: fix ISO-8859-14 case folding shaos
  2026-10-29 10:10 ` Ali Ahmet Memiş
@ 2026-10-29 10:21 ` OGAWA Hirofumi
  1 sibling, 0 replies; 3+ messages in thread
From: OGAWA Hirofumi @ 2026-10-29 10:21 UTC (permalink / raw)
  To: Andrew Morton; +Cc: shaos, brauner, aliamemis, linux-fsdevel, linux-kernel

shaos <shawnq1@163.com> writes:

> 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 <shawnq1@163.com>

Looks like the bug of this table.

Acked-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>

> ---
>
> 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
>

-- 
OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-10-29 10:22 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-29  9:14 [PATCH] nls: fix ISO-8859-14 case folding shaos
2026-10-29 10:10 ` Ali Ahmet Memiş
2026-10-29 10:21 ` OGAWA Hirofumi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®