[PATCH v2] gfs2: reject an over-long directory entry name in gfs2_check_dirent

Michael Bommarito posted 1 patch 1 week, 4 days ago
fs/gfs2/dir.c | 4 ++++
1 file changed, 4 insertions(+)
[PATCH v2] gfs2: reject an over-long directory entry name in gfs2_check_dirent
Posted by Michael Bommarito 1 week, 4 days ago
gfs2_check_dirent() validates a directory entry against the space in its
dirent record but never bounds de_name_len to GFS2_FNAMESIZE. A dirent
with a large de_rec_len can therefore carry a de_name_len far greater than
GFS2_FNAMESIZE and still pass the check. That length flows unclamped
through do_filldir_main() -> dir_emit() to the ctx->actor. In the
NFS-export get_name path get_name_filldir() copies the name into the
fixed NAME_MAX+1 byte buffer nbuf[] on the exportfs_decode_fh_raw() stack
with memcpy(gnfd->name, name, length), so a crafted or corrupted on-disk
directory overflows that buffer.

Impact: an out-of-bounds write past the NAME_MAX+1 byte name buffer
(KASAN), reachable when a gfs2 filesystem carrying a crafted directory
entry is re-exported over NFS and a file handle is resolved.

Reject dirents whose name length exceeds GFS2_FNAMESIZE at the read-path
check, so no over-long name reaches any dirent consumer.

Fixes: b3b94faa5fe5 ("[GFS2] The core of GFS2")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
v2: move the validation from get_name_filldir() into gfs2_check_dirent()
    on the dirent read path, per Andrew Price's review, so every dirent
    consumer is protected rather than only the NFS get_name path; and
    correct the buffer description (the destination is the NAME_MAX+1
    byte nbuf[] in exportfs_decode_fh_raw(), not a GFS2_FNAMESIZE buffer).
v1: https://lore.kernel.org/gfs2/20260711150845.gfs2-getname-v1@bommarito/

v1: https://lore.kernel.org/gfs2/20260711150808.2919076-1-michael.bommarito@gmail.com/

Evidence: a crafted gfs2 image with a directory dirent whose de_name_len
is 300 (record length left valid so the existing size check passes),
resolved with open_by_handle_at() on a KASAN + KASAN_STACK kernel.  Stock:
BUG: KASAN: stack-out-of-bounds in get_name_filldir, Write of size 300 into
the NAME_MAX+1 byte nbuf[] object of the exportfs_decode_fh_raw() stack
frame, via open_by_handle_at -> exportfs_decode_fh_raw -> reconnect_path ->
gfs2_get_name -> do_filldir_main -> get_name_filldir.  Patched:
gfs2_check_dirent() rejects the entry (name length exceeds GFS2_FNAMESIZE)
and the handle resolves to ESTALE; a valid name still reconnects.  Built
clean, no new warnings.

 fs/gfs2/dir.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index 0237b36b9eb16..905b658a05177 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -521,6 +521,10 @@ static int gfs2_check_dirent(struct gfs2_sbd *sdp,
 	    unlikely(sizeof(struct gfs2_dirent)+be16_to_cpu(dent->de_name_len) >
 		     size))
 		goto error;
+	msg = "name length exceeds GFS2_FNAMESIZE";
+	if (!gfs2_dirent_sentinel(dent) &&
+	    unlikely(be16_to_cpu(dent->de_name_len) > GFS2_FNAMESIZE))
+		goto error;
 	return 0;
 error:
 	fs_warn(sdp, "%s: %s (%s)\n",
-- 
2.53.0