mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Guikun Yang <yangguikun19@qq.com>
To: Andreas Gruenbacher <agruenba@redhat.com>
Cc: gfs2@lists.linux.dev, linux-kernel@vger.kernel.org
Subject: [Bug report] gfs2: unvalidated lf_depth causes shift-out-of-bounds in dir_e_read, dir_split_leaf and gfs2_dir_exhash_dealloc
Date: Tue, 03 Nov 2026 10:00:00 +0800	[thread overview]
Message-ID: <tencent_B367A2067C2EA3845289BED747449C264D09@qq.com> (raw)

Hi,

Mounting a gfs2 image whose leaf blocks carry an lf_depth larger than the
inode's i_depth triggers an undefined shift in three places. i_depth is
validated when the dinode is read; lf_depth is never compared against it.

  fs/gfs2/dir.c:1560  dir_e_read()
      len = BIT(dip->i_depth - depth);        /* depth = lf->lf_depth */

  fs/gfs2/dir.c:1050  dir_split_leaf()
      if (dip->i_depth == be16_to_cpu(oleaf->lf_depth))   /* equality only */
      ...
      len = BIT(dip->i_depth - be16_to_cpu(oleaf->lf_depth));
      half_len = len >> 1;
      if (!half_len)                     /* checked after the shift */

  fs/gfs2/dir.c:2137  gfs2_dir_exhash_dealloc()
      len = BIT(dip->i_depth - be16_to_cpu(leaf->lf_depth));

Observed with a 4K-block image (mkfs.gfs2 -J 8) whose root directory was
grown to exhash with 400 long-named files, i_depth 8, 16 leaf blocks at
lf_depth 3. After a clean umount, lf_depth in every LF block (block offset
+24) is raised by 12, then the image is mounted and getdents64() issued:

  UBSAN: shift-out-of-bounds in fs/gfs2/dir.c:1557:9
  shift exponent 4294967289 is too large for 64-bit type 'long unsigned int'
  Kernel panic - not syncing: UBSAN: panic_on_warn set ...

4294967289 is (unsigned)(8 - 15). Changing the lf_depth delta changes the
exponent by the same amount, so the chain is unambiguous.

One detail worth noting: BIT() yields unsigned long and the destination in
all three functions is u32, so the truncated len is 0. In dir_e_read()
that makes index = (index & ~(len - 1)) + len evaluate to 0 while
index < hsize is still true; in gfs2_dir_exhash_dealloc() next_index also
stays 0. I have not measured the behaviour with panic_on_warn disabled.

gfs2 metadata blocks carry no checksum, so the image is built with
mkfs.gfs2 plus direct edits at two fixed offsets and needs no checksum
recomputation. A single forged image reaches all three sites depending on
the operation performed afterwards: getdents64() for dir_e_read, creating
an entry in a full leaf for dir_split_leaf, and rmdir of an emptied exhash
directory for gfs2_dir_exhash_dealloc (gfs2_dir_del() decrements
lf_entries but leaves GFS2_DIF_EXHASH and the leaves in place, and
evict_unlinked_inode() calls gfs2_dir_exhash_dealloc() when the flag is
still set).

All three sites obtain their buffer through get_leaf(), which has the
directory inode at hand, so one check there covers them. After the
existing gfs2_metatype_check(), when no error was returned:

  struct gfs2_leaf *lf = (struct gfs2_leaf *)(*bhp)->b_data;

  if (be16_to_cpu(lf->lf_depth) > dip->i_depth) {
          gfs2_consist_inode(dip);
          error = -EIO;
  }

lf_depth <= i_depth holds for every directory built by the kernel:
dir_split_leaf() returns early when the two are equal, so a leaf is never
given a depth above the inode's.

The reproducer (image builder, VM harness, crash log) is available on
request.

Thanks,
Guikun Yang


                 reply	other threads:[~2026-11-03  6:35 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=tencent_B367A2067C2EA3845289BED747449C264D09@qq.com \
    --to=yangguikun19@qq.com \
    --cc=agruenba@redhat.com \
    --cc=gfs2@lists.linux.dev \
    --cc=linux-kernel@vger.kernel.org \
    /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®