mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: syzbot <syzbot+a2748ba908c108e7e525@syzkaller.appspotmail.com>
To: linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com
Subject: Forwarded: [PATCH] jfs: fix slab-out-of-bounds write in jfs_readdir() with multibyte names
Date: Mon, 05 Oct 2026 22:53:27 -0700	[thread overview]
Message-ID: <6ac48cd7.ae2c267a.265a6c.000e.GAE@google.com> (raw)
In-Reply-To: <6ac3be67.34119e79.2f92f3.0035.GAE@google.com>

For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.

***

Subject: [PATCH] jfs: fix slab-out-of-bounds write in jfs_readdir() with multibyte names
Author: kartikey406@gmail.com

#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master


syzbot reported a KASAN slab-out-of-bounds write in utf32_to_utf8(),
reached via jfs_strfromUCS_le() from jfs_readdir() while handling
getdents64() on a crafted JFS image.

jfs_readdir() converts on-disk UTF-16 directory entry names into
dirent_buf, a PAGE_SIZE buffer. Before converting an entry, it checks
that the name fits by assuming one output byte per UTF-16 unit:

    jfs_dirent + d->namlen + 1 > dirent_buf + PAGE_SIZE

With iocharset=utf8 a single UTF-16 unit can expand to up to three
bytes, so a name can be approved for space it does not have.

jfs_strfromUCS_le() makes this worse: it has no destination size and
always passes NLS_MAX_CHARSET_SIZE as the output bound to uni2char(),
so the converter believes it has room regardless of how much of the
buffer is actually left. The conversion then writes past the end of
dirent_buf, and the trailing NUL can land one byte out of bounds too.

Fix this in two places:

 - In jfs_readdir(), reserve the worst case of NLS_MAX_CHARSET_SIZE
   bytes per UTF-16 unit when checking whether an entry fits.

 - Give jfs_strfromUCS_le() a destination size (tolen) and pass the
   real remaining space to uni2char() instead of the constant, keeping
   one byte for the terminating NUL. Clamp the length in the
   non-codepage path in the same way. Callers pass the distance to the
   end of dirent_buf.

Reported-by: syzbot+a2748ba908c108e7e525@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=a2748ba908c108e7e525
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
 fs/jfs/jfs_dtree.c   |  8 +++++---
 fs/jfs/jfs_unicode.c | 15 ++++++++++++---
 fs/jfs/jfs_unicode.h |  2 +-
 3 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c
index 8ce6e4458cc2..5ccc90e3c068 100644
--- a/fs/jfs/jfs_dtree.c
+++ b/fs/jfs/jfs_dtree.c
@@ -2738,6 +2738,7 @@ int jfs_readdir(struct file *file, struct dir_context *ctx)
 	int jfs_dirents;
 	int overflow, fix_page, page_fixed = 0;
 	static int unique_pos = 2;	/* If we can't fix broken index */
+	char *buf_end;
 
 	if (ctx->pos == DIREND)
 		return 0;
@@ -2892,6 +2893,7 @@ int jfs_readdir(struct file *file, struct dir_context *ctx)
 		return -ENOMEM;
 	}
 
+	buf_end = (char *)dirent_buf + PAGE_SIZE;
 	while (1) {
 		jfs_dirent = dirent_buf;
 		jfs_dirents = 0;
@@ -2910,7 +2912,7 @@ int jfs_readdir(struct file *file, struct dir_context *ctx)
 
 			d = (struct ldtentry *) & p->slot[stbl[i]];
 
-			if (((long) jfs_dirent + d->namlen + 1) >
+			if (((long) jfs_dirent + (long)d->namlen * NLS_MAX_CHARSET_SIZE + 1) >
 			    ((long)dirent_buf + PAGE_SIZE)) {
 				/* DBCS codepages could overrun dirent_buf */
 				index = i;
@@ -2961,7 +2963,7 @@ int jfs_readdir(struct file *file, struct dir_context *ctx)
 			}
 
 			/* copy the name of head/only segment */
-			outlen = jfs_strfromUCS_le(name_ptr, d->name, len,
+			outlen = jfs_strfromUCS_le(name_ptr, buf_end - name_ptr, d->name, len,
 						   codepage);
 			jfs_dirent->name_len = outlen;
 
@@ -2981,7 +2983,7 @@ int jfs_readdir(struct file *file, struct dir_context *ctx)
 					goto skip_one;
 				}
 				len = min(d_namleft, DTSLOTDATALEN);
-				outlen = jfs_strfromUCS_le(name_ptr, t->name,
+				outlen = jfs_strfromUCS_le(name_ptr, buf_end - name_ptr , t->name,
 							   len, codepage);
 				jfs_dirent->name_len += outlen;
 
diff --git a/fs/jfs/jfs_unicode.c b/fs/jfs/jfs_unicode.c
index 0c1e9027245a..bcff7e0031b2 100644
--- a/fs/jfs/jfs_unicode.c
+++ b/fs/jfs/jfs_unicode.c
@@ -16,27 +16,36 @@
  * FUNCTION:	Convert little-endian unicode string to character string
  *
  */
-int jfs_strfromUCS_le(char *to, const __le16 * from,
+int jfs_strfromUCS_le(char *to, int tolen, const __le16 * from,
 		      int len, struct nls_table *codepage)
 {
-	int i;
+	int i, room;
 	int outlen = 0;
 	static int warn_again = 5;	/* Only warn up to 5 times total */
 	int warn = !!warn_again;	/* once per string */
 
+	if(tolen <= 0)
+		return 0;
+
 	if (codepage) {
 		for (i = 0; (i < len) && from[i]; i++) {
 			int charlen;
+			room = tolen - outlen - 1;
+			if(room <= 0)
+				break;
 			charlen =
 			    codepage->uni2char(le16_to_cpu(from[i]),
 					       &to[outlen],
-					       NLS_MAX_CHARSET_SIZE);
+					       room);
 			if (charlen > 0)
 				outlen += charlen;
 			else
 				to[outlen++] = '?';
 		}
 	} else {
+		if(len > tolen -1)
+			len = tolen - 1;
+
 		for (i = 0; (i < len) && from[i]; i++) {
 			if (unlikely(le16_to_cpu(from[i]) & 0xff00)) {
 				to[i] = '?';
diff --git a/fs/jfs/jfs_unicode.h b/fs/jfs/jfs_unicode.h
index b6a78d4aef1b..ea03dd5a0e0c 100644
--- a/fs/jfs/jfs_unicode.h
+++ b/fs/jfs/jfs_unicode.h
@@ -12,7 +12,7 @@
 #include "jfs_types.h"
 
 extern int get_UCSname(struct component_name *, struct dentry *);
-extern int jfs_strfromUCS_le(char *, const __le16 *, int, struct nls_table *);
+extern int jfs_strfromUCS_le(char *, int, const __le16 *, int, struct nls_table *);
 
 #define free_UCSname(COMP) kfree((COMP)->name)
 
-- 
2.43.0


      reply	other threads:[~2026-10-06  5:53 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-05 15:12 [syzbot] KASAN: slab-out-of-bounds Write in utf32_to_utf8 syzbot
2026-10-06  5:53 ` syzbot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=6ac48cd7.ae2c267a.265a6c.000e.GAE@google.com \
    --to=syzbot+a2748ba908c108e7e525@syzkaller.appspotmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=syzkaller-bugs@googlegroups.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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®