[PATCH AUTOSEL 7.0-5.10] jfs: add dmapctl integrity check to prevent invalid operations

Sasha Levin
2026-04-20T13:16:45Z

From: Yun Zhou <yun.zhou@windriver.com>

[ Upstream commit cce219b203c4b9cb445e910c7090d1f58af847c5 ]

Add check_dmapctl() to validate dmapctl structure integrity, focusing on
preventing invalid operations caused by on-disk corruption.

Key checks:
- nleafs bounded by [0, LPERCTL] (maximum leaf nodes per dmapctl).
- l2nleafs bounded by [0, L2LPERCTL] and consistent with nleafs
(nleafs must be 2^l2nleafs).
- leafidx must be exactly CTLLEAFIND (expected leaf index position).
- height bounded by [0, L2LPERCTL >> 1] (valid tree height range).
- budmin validity: NOFREE only if nleafs=0; otherwise >= BUDMIN.
- Leaf nodes fit within stree array (leafidx + nleafs <= CTLTREESIZE).
- Leaf node values are either non-negative or NOFREE.

Invoked in dbAllocAG(), dbFindCtl(), dbAdjCtl() and dbExtendFS() when
accessing dmapctl pages, catching corruption early before dmap operations
trigger invalid memory access or logic errors.

This fixes the following UBSAN warning.

[58245.668090][T14017] ------------[ cut here ]------------
[58245.668103][T14017] UBSAN: shift-out-of-bounds in fs/jfs/jfs_dmap.c:2641:11
[58245.668119][T14017] shift exponent 110 is too large for 32-bit type 'int'
[58245.668137][T14017] CPU: 0 UID: 0 PID: 14017 Comm: 4c1966e88c28fa9 Tainted: G E 6.18.0-rc4-00253-g21ce5d4ba045-dirty #124 PREEMPT_{RT,(full)}
[58245.668174][T14017] Tainted: [E]=UNSIGNED_MODULE
[58245.668176][T14017] Hardware name: QEMU Ubuntu 25.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[58245.668184][T14017] Call Trace:
[58245.668200][T14017] <TASK>
[58245.668208][T14017] dump_stack_lvl+0x189/0x250
[58245.668288][T14017] ? __pfx_dump_stack_lvl+0x10/0x10
[58245.668301][T14017] ? __pfx__printk+0x10/0x10
[58245.668315][T14017] ? lock_metapage+0x303/0x400 [jfs]
[58245.668406][T14017] ubsan_epilogue+0xa/0x40
[58245.668422][T14017] __ubsan_handle_shift_out_of_bounds+0x386/0x410
[58245.668462][T14017] dbSplit+0x1f8/0x200 [jfs]
[58245.668543][T14017] dbAdjCtl+0x34c/0xa20 [jfs]
[58245.668628][T14017] dbAllocNear+0x2ee/0x3d0 [jfs]
[58245.668710][T14017] dbAlloc+0x933/0xba0 [jfs]
[58245.668797][T14017] ea_write+0x374/0xdd0 [jfs]
[58245.668888][T14017] ? __pfx_ea_write+0x10/0x10 [jfs]
[58245.668966][T14017] ? __jfs_setxattr+0x76e/0x1120 [jfs]
[58245.669046][T14017] __jfs_setxattr+0xa01/0x1120 [jfs]
[58245.669135][T14017] ? __pfx___jfs_setxattr+0x10/0x10 [jfs]
[58245.669216][T14017] ? mutex_lock_nested+0x154/0x1d0
[58245.669252][T14017] ? __jfs_xattr_set+0xb9/0x170 [jfs]
[58245.669333][T14017] __jfs_xattr_set+0xda/0x170 [jfs]
[58245.669430][T14017] ? __pfx___jfs_xattr_set+0x10/0x10 [jfs]
[58245.669509][T14017] ? xattr_full_name+0x6f/0x90
[58245.669546][T14017] ? jfs_xattr_set+0x33/0x60 [jfs]
[58245.669636][T14017] ? __pfx_jfs_xattr_set+0x10/0x10 [jfs]
[58245.669726][T14017] __vfs_setxattr+0x43c/0x480
[58245.669743][T14017] __vfs_setxattr_noperm+0x12d/0x660
[58245.669756][T14017] vfs_setxattr+0x16b/0x2f0
[58245.669768][T14017] ? __pfx_vfs_setxattr+0x10/0x10
[58245.669782][T14017] filename_setxattr+0x274/0x600
[58245.669795][T14017] ? __pfx_filename_setxattr+0x10/0x10
[58245.669806][T14017] ? getname_flags+0x1e5/0x540
[58245.669829][T14017] path_setxattrat+0x364/0x3a0
[58245.669840][T14017] ? __pfx_path_setxattrat+0x10/0x10
[58245.669859][T14017] ? __se_sys_chdir+0x1b9/0x280
[58245.669876][T14017] __x64_sys_lsetxattr+0xbf/0xe0
[58245.669888][T14017] do_syscall_64+0xfa/0xfa0
[58245.669901][T14017] ? lockdep_hardirqs_on+0x9c/0x150
[58245.669913][T14017] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f
[58245.669927][T14017] ? exc_page_fault+0xab/0x100
[58245.669937][T14017] entry_SYSCALL_64_after_hwframe+0x77/0x7f

Reported-by: syzbot+4c1966e88c28fa96e053@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=4c1966e88c28fa96e053
Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
Signed-off-by: Dave Kleikamp <dave.kleikamp@oracle.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---

LLM Generated explanations, may be completely bogus:

Now let me compile the full analysis.

---

## PHASE 1: COMMIT MESSAGE FORENSICS

### Step 1.1: PARSE THE SUBJECT LINE
Record: [jfs] [add integrity check] - Adds `check_dmapctl()` function to
validate dmapctl structure integrity to prevent invalid operations from
on-disk corruption.

### Step 1.2: PARSE ALL COMMIT MESSAGE TAGS
- **Reported-by:** syzbot+4c1966e88c28fa96e053@syzkaller.appspotmail.com
(fuzzer-found bug, strong signal)
- **Closes:**
https://syzkaller.appspot.com/bug?extid=4c1966e88c28fa96e053
- **Signed-off-by:** Yun Zhou <yun.zhou@windriver.com> (author)
- **Signed-off-by:** Dave Kleikamp <dave.kleikamp@oracle.com> (JFS
maintainer applied it)
- No Fixes: tag (expected for autosel candidates)
- No Cc: stable (expected for autosel candidates)

### Step 1.3: ANALYZE THE COMMIT BODY TEXT
The commit describes a UBSAN shift-out-of-bounds at
`fs/jfs/jfs_dmap.c:2641:11` in `dbSplit()` where a corrupt `budmin`
value from an on-disk dmapctl structure leads to a shift exponent of 110
(far exceeding the 32-bit limit). The call chain is: `lsetxattr` syscall
-> `__jfs_setxattr` -> `ea_write` -> `dbAlloc` -> `dbAllocNear` ->
`dbAdjCtl` -> `dbSplit`.

Record: Bug is a UBSAN shift-out-of-bounds triggered by corrupt dmapctl
on-disk data. Failure mode is undefined behavior from invalid shift.
Root cause is lack of comprehensive validation of dmapctl fields read
from disk.

### Step 1.4: DETECT HIDDEN BUG FIXES
This is not a hidden bug fix - it's an explicitly declared fix for a
syzbot-reported UBSAN issue. The commit directly fixes undefined
behavior caused by corrupt on-disk data.

---

## PHASE 2: DIFF ANALYSIS - LINE BY LINE

### Step 2.1: INVENTORY THE CHANGES
- **File:** `fs/jfs/jfs_dmap.c` only (single file)
- **New function:** `check_dmapctl()` (~87 lines added)
- **3 replacement sites:** `dbAllocAG()`, `dbFindCtl()`, `dbAdjCtl()` -
each replaces `if (dcp->leafidx != cpu_to_le32(CTLLEAFIND))` with `if
(unlikely(!check_dmapctl(dcp)))`
- **5 new check sites in dbExtendFS():** Adds `check_dmapctl()` calls
after reading L2, L1, and L0 dmapctl pages where no checks existed
before
- Total: ~87 lines for the new function + 8 call sites (3 replacements +
5 additions) = ~110 lines net change

### Step 2.2: UNDERSTAND THE CODE FLOW CHANGE
**Before:** Only `leafidx == CTLLEAFIND` was validated when reading
dmapctl pages in 3 functions. `budmin`, `nleafs`, `l2nleafs`, `height`,
and leaf values were NOT validated. In `dbExtendFS()`, no dmapctl
validation at all.

**After:** All 7 key fields of dmapctl are validated before any
arithmetic is performed on them. This catches corrupt values (like the
`budmin` that led to shift exponent 110) before they flow into
`BUDSIZE()`, `BLKTOCTLLEAF()`, or array index operations.

### Step 2.3: IDENTIFY THE BUG MECHANISM
Category: **Input validation / sanitizer-reported undefined behavior**

The `BUDSIZE(s,m)` macro at line 275 of `jfs_dmap.h` is `(1 << ((s) -
(m)))`. When `leaf[leafno]` is corrupt (e.g., 115) and `budmin` is 5
(BUDMIN), `cursz = 115 - 1 = 114`, and `BUDSIZE(114, 5) = (1 << 109)`
which is shift-out-of-bounds for a 32-bit int. The `check_dmapctl()`
validates `budmin >= BUDMIN` and leaf values are within `[NOFREE, 31]`,
preventing such overflows.

### Step 2.4: ASSESS THE FIX QUALITY
- The fix is well-structured with clear boundary checks against known
constants
- It replaces weaker checks with comprehensive validation
- Each check maps to a specific invariant of the dmapctl structure
- Error paths return -EIO which is the existing pattern
- The leaf value range check `val > 31` matches the fact that
`BUDSIZE(31, 5) = (1 << 26)` is the maximum valid shift
- Risk of regression is low - it only makes validation stricter, all
valid dmapctl pages will pass

---

## PHASE 3: GIT HISTORY INVESTIGATION

### Step 3.1: BLAME THE CHANGED LINES
The 3 existing `leafidx != cpu_to_le32(CTLLEAFIND)` checks being
replaced were all introduced in the initial Linux git import (v2.6.12,
commit 1da177e4c3f41). This is ancient code present in ALL stable trees.

### Step 3.2: FOLLOW THE FIXES: TAG
No Fixes: tag present. The bug is in the fundamental lack of validation
of on-disk dmapctl data, which dates back to the initial JFS
implementation.

### Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES
JFS has seen a steady stream of similar corruption-defense fixes, all
syzbot-driven:
- `a5f5e4698f8ab` - fix shift-out-of-bounds in dbSplit (similar bug,
budmin < 0)
- `d64ff0d230671` - check if leafidx greater than num leaves per dmap
tree
- `a174706ba4dad` - check to prevent array-index-out-of-bounds in
dbAdjTree
- Many others addressing the same class of "corrupt on-disk data →
UBSAN/crash"

This commit is standalone - it does not require other patches in a
series.

### Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS
Yun Zhou (Wind River) is not a regular JFS contributor. However, the
patch was reviewed by Li Lingfeng and applied by Dave Kleikamp
(shaggy@kernel.org), who is the JFS maintainer.

### Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS
The patch replaces `dcp->leafidx != cpu_to_le32(CTLLEAFIND)` checks that
have existed since v2.6.12. The code context (function signatures, data
structures) is stable and unchanged. No dependencies on recent commits.

---

## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH

### Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION
- b4 dig found the match: https://patch.msgid.link/20251128155150.149398
6-1-yun.zhou@windriver.com
- This is v2 of the patch (v1 was submitted 2025/11/20, v2 on
2025/11/28)
- Dave Kleikamp (JFS maintainer) replied: "This is finally tested and
applied." (2026/03/16)
- Li Lingfeng reviewed and pinged twice for application

### Step 4.2: CHECK WHO REVIEWED THE PATCH
The JFS maintainer (Dave Kleikamp/shaggy) directly tested and applied.
Li Lingfeng reviewed.

### Step 4.3: SEARCH FOR THE BUG REPORT
The syzbot page confirms:
- Bug is reproducible with a C reproducer
- First crash: 354 days ago, last: 36 days ago (persistent bug)
- The bug has **similar bugs** on linux-6.1 and linux-5.15 that are
**NOT patched** (0/3 patched on 6.1, 0/3 on 5.15)
- This confirms the bug exists and is unpatched in stable trees

### Step 4.4-4.5: Related patches
This is a standalone patch. No explicit stable discussion found, but the
syzbot similar bugs table shows the same class of issue exists in stable
trees (linux-6.1, linux-5.15) and is NOT yet fixed there.

---

## PHASE 5: CODE SEMANTIC ANALYSIS

### Step 5.1-5.2: KEY FUNCTIONS AND CALLERS
The `check_dmapctl()` is called in:
1. **`dbAllocAG()`** - called from `dbAlloc()`, the primary block
allocation path
2. **`dbFindCtl()`** - called from `dbAllocCtl()` and `dbAllocAG()`
3. **`dbAdjCtl()`** - called from `dbFreeBits()`, `dbAllocDmap()`,
`dbAllocDmapBU()`, and recursively
4. **`dbExtendFS()`** - called during filesystem extension

### Step 5.3-5.4: CALL CHAIN (reachability)
The syzbot crash trace confirms reachability from userspace:
`lsetxattr` (syscall) → `__jfs_setxattr` → `ea_write` → `dbAlloc` →
`dbAllocNear` → `dbAdjCtl` → `dbSplit`

This is triggered by a standard `setxattr` syscall on a JFS filesystem.
Any unprivileged user who can write to a JFS filesystem can trigger
this.

---

## PHASE 6: CROSS-REFERENCING AND STABLE TREE ANALYSIS

### Step 6.1: DOES THE BUGGY CODE EXIST IN STABLE?
Yes. The code being modified (the leafidx checks in dbAllocAG,
dbFindCtl, dbAdjCtl) has existed since v2.6.12. The bug exists in ALL
stable trees. Syzbot confirms it reproduces on linux-6.1 and linux-5.15.

### Step 6.2: CHECK FOR BACKPORT COMPLICATIONS
The patch should apply cleanly to most stable trees. The three
replacement sites have had the same `dcp->leafidx !=
cpu_to_le32(CTLLEAFIND)` check since the initial git import. The
`dbExtendFS()` additions are to code that is also largely unchanged.

There may be minor context conflicts depending on surrounding changes in
each stable tree (the many JFS syzbot fixes), but the core code
structure is the same.

### Step 6.3: RELATED FIXES IN STABLE
The earlier fix `a5f5e4698f8ab` ("jfs: fix shift-out-of-bounds in
dbSplit") addressed the same syzbot bug family (#2) by checking
`dp->tree.budmin < 0` in `dbAllocCtl`. This commit provides a more
comprehensive fix at the dmapctl level, catching corruption earlier and
more broadly.

---

## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT

### Step 7.1: SUBSYSTEM CRITICALITY
- **Subsystem:** fs/jfs (JFS filesystem)
- **Criticality:** IMPORTANT - JFS is a mature filesystem used in
enterprise environments, particularly on older IBM/AIX-heritage
systems and some embedded environments
- The fix is in the block allocation map code, which is core to
filesystem operations

### Step 7.2: SUBSYSTEM ACTIVITY
JFS is in maintenance mode - no new features, but receives a steady
stream of corruption-defense fixes (mostly syzbot-driven). This is
exactly the kind of fix that goes to stable.

---

## PHASE 8: IMPACT AND RISK ASSESSMENT

### Step 8.1: WHO IS AFFECTED
All users of JFS filesystems. Trigger requires a corrupt JFS filesystem
image (could be from disk corruption, or crafted by an attacker with
local access).

### Step 8.2: TRIGGER CONDITIONS
- Requires mounting or using a JFS filesystem with corrupt dmapctl pages
- Syzbot reproduces this via `lsetxattr` syscall on a crafted image
- Can be triggered by unprivileged user operations on a mounted JFS
filesystem
- Corrupt filesystem images can come from disk errors, USB drives, or
malicious crafting

### Step 8.3: FAILURE MODE SEVERITY
- **UBSAN shift-out-of-bounds** = undefined behavior
- Without UBSAN, this could lead to incorrect memory access patterns,
potential memory corruption
- The corrupt budmin/leaf values flow into array indexing and shift
operations
- Severity: **HIGH** (undefined behavior, potential for memory
corruption or crash)

### Step 8.4: RISK-BENEFIT RATIO
- **BENEFIT:** High - fixes a reproducible syzbot bug, prevents
undefined behavior from corrupt on-disk data, protects against a class
of bugs (not just one instance)
- **RISK:** Low-Medium - the fix is ~87 lines of new validation code
(moderate size), but all checks are straightforward bounds checks
against well-defined constants. The existing error return pattern is
preserved. The fix only adds stricter validation - it cannot break
valid filesystems.
- **Ratio:** Favorable for backport

---

## PHASE 9: FINAL SYNTHESIS

### Step 9.1: COMPILE THE EVIDENCE

**Evidence FOR backporting:**
- Fixes a real, reproducible syzbot bug (UBSAN shift-out-of-bounds in
dbSplit)
- Syzbot confirms the same bug exists on linux-6.1 and linux-5.15
(unpatched)
- Protects against undefined behavior from corrupt on-disk data
- Applied and tested by the JFS maintainer (Dave Kleikamp)
- Standalone patch, no dependencies
- Code being modified has existed since v2.6.12 (present in all stable
trees)
- Bug is triggerable from unprivileged userspace syscalls
- Pattern is consistent with dozens of similar JFS fixes that go to
stable

**Evidence AGAINST backporting:**
- Somewhat large (~110 lines of net change) for a stable fix
- Adds a new function rather than a minimal surgical fix
- Could theoretically reject valid dmapctl pages if the checks are too
strict (but checks match documented invariants)
- The `dbExtendFS()` additions are new check sites (not replacements),
adding code to paths that had none before

### Step 9.2: STABLE RULES CHECKLIST
1. **Obviously correct and tested?** YES - maintainer tested and
applied; checks are against documented constants
2. **Fixes a real bug?** YES - syzbot-reported UBSAN, reproducible with
C reproducer
3. **Important issue?** YES - undefined behavior (shift-out-of-bounds),
potential memory corruption
4. **Small and contained?** BORDERLINE - ~110 lines but all in one file,
one new function with clear purpose
5. **No new features or APIs?** YES - purely defensive validation
6. **Can apply to stable?** YES - code structure is stable across all
trees

### Step 9.3: EXCEPTION CATEGORIES
Not an exception category, but fits the standard "fixes real bug found
by fuzzer" pattern.

### Step 9.4: DECISION
The fix addresses a real, reproducible syzbot bug that exists in all
stable trees. While the patch is somewhat larger than ideal for stable
(adding a complete validation function), it is well-contained within a
single file and function, all checks are obviously correct bounds
checks, and it was tested by the subsystem maintainer. The undefined
behavior it prevents could lead to memory corruption or crashes on
corrupt JFS images. The syzbot data shows similar bugs are actively
reproducing on stable kernels (linux-6.1, linux-5.15). The benefit
clearly outweighs the moderate risk.

---

## Verification

- [Phase 1] Parsed tags: Reported-by syzbot+4c1966e88c28fa96e053,
Closes: syzkaller link, SOBs from author + JFS maintainer
- [Phase 2] Diff analysis: ~87 lines new validation function, 3
replacement sites, 5 new check sites, all in `fs/jfs/jfs_dmap.c`
- [Phase 2] Root cause: `BUDSIZE(cursz, budmin)` macro does `1 << (s -
m)`, corrupt leaf value (e.g. 115) → shift exponent 109
- [Phase 3] git blame: All 3 existing leafidx checks date to
`1da177e4c3f41` (v2.6.12, 2005), present in all stable trees
- [Phase 3] git log: File has extensive syzbot-driven fix history (20+
commits addressing similar corruption issues)
- [Phase 3] Author: Yun Zhou (Wind River), not regular JFS contributor
but patch applied by maintainer Dave Kleikamp
- [Phase 4] b4 dig: Found match at https://patch.msgid.link/202511281551
50.1493986-1-yun.zhou@windriver.com
- [Phase 4] b4 dig -a: v1 (2025/11/20), v2 (2025/11/28) - committed
version is v2 (latest)
- [Phase 4] b4 dig -w: JFS maintainer (shaggy@kernel.org) was included,
tested and applied
- [Phase 4] syzbot page: Bug first crash 354d ago, last 36d ago;
reproducer available; similar bugs on linux-6.1 (0/3 patched) and
linux-5.15 (0/3 patched)
- [Phase 4] Maintainer comment: "This is finally tested and applied"
(Dave Kleikamp, 2026/03/16)
- [Phase 5] Call chain verified from syzbot trace: lsetxattr →
__jfs_setxattr → ea_write → dbAlloc → dbAllocNear → dbAdjCtl → dbSplit
(userspace reachable)
- [Phase 6] Buggy code present in all stable trees (since v2.6.12)
- [Phase 6] Backport: should apply cleanly, no significant structural
changes in the 3 replacement sites
- [Phase 8] Severity: HIGH (undefined behavior from shift-out-of-bounds,
potential memory corruption)
- [Phase 8] Trigger: unprivileged user setxattr on corrupt JFS
filesystem

**YES**

fs/jfs/jfs_dmap.c | 114 ++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 111 insertions(+), 3 deletions(-)

diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
index 2abe8cc02ee6f..a841cf21da7de 100644
--- a/fs/jfs/jfs_dmap.c
+++ b/fs/jfs/jfs_dmap.c
@@ -133,6 +133,93 @@ static const s8 budtab[256] = {
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1
};

+/*
+ * check_dmapctl - Validate integrity of a dmapctl structure
+ * @dcp: Pointer to the dmapctl structure to check
+ *
+ * Return: true if valid, false if corrupted
+ */
+static bool check_dmapctl(struct dmapctl *dcp)
+{
+ s8 budmin = dcp->budmin;
+ u32 nleafs, l2nleafs, leafidx, height;
+ int i;
+
+ nleafs = le32_to_cpu(dcp->nleafs);
+ /* Check basic field ranges */
+ if (unlikely(nleafs > LPERCTL)) {
+ jfs_err("dmapctl: invalid nleafs %u (max %u)",
+ nleafs, LPERCTL);
+ return false;
+ }
+
+ l2nleafs = le32_to_cpu(dcp->l2nleafs);
+ if (unlikely(l2nleafs > L2LPERCTL)) {
+ jfs_err("dmapctl: invalid l2nleafs %u (max %u)",
+ l2nleafs, L2LPERCTL);
+ return false;
+ }
+
+ /* Verify nleafs matches l2nleafs (must be power of two) */
+ if (unlikely((1U << l2nleafs) != nleafs)) {
+ jfs_err("dmapctl: nleafs %u != 2^%u",
+ nleafs, l2nleafs);
+ return false;
+ }
+
+ leafidx = le32_to_cpu(dcp->leafidx);
+ /* Check leaf index matches expected position */
+ if (unlikely(leafidx != CTLLEAFIND)) {
+ jfs_err("dmapctl: invalid leafidx %u (expected %u)",
+ leafidx, CTLLEAFIND);
+ return false;
+ }
+
+ height = le32_to_cpu(dcp->height);
+ /* Check tree height is within valid range */
+ if (unlikely(height > (L2LPERCTL >> 1))) {
+ jfs_err("dmapctl: invalid height %u (max %u)",
+ height, L2LPERCTL >> 1);
+ return false;
+ }
+
+ /* Check budmin is valid (cannot be NOFREE for non-empty tree) */
+ if (budmin == NOFREE) {
+ if (unlikely(nleafs > 0)) {
+ jfs_err("dmapctl: budmin is NOFREE but nleafs %u",
+ nleafs);
+ return false;
+ }
+ } else if (unlikely(budmin < BUDMIN)) {
+ jfs_err("dmapctl: invalid budmin %d (min %d)",
+ budmin, BUDMIN);
+ return false;
+ }
+
+ /* Check leaf nodes fit within stree array */
+ if (unlikely(leafidx + nleafs > CTLTREESIZE)) {
+ jfs_err("dmapctl: leaf range exceeds stree size (end %u > %u)",
+ leafidx + nleafs, CTLTREESIZE);
+ return false;
+ }
+
+ /* Check leaf nodes have valid values */
+ for (i = leafidx; i < leafidx + nleafs; i++) {
+ s8 val = dcp->stree[i];
+
+ if (unlikely(val < NOFREE)) {
+ jfs_err("dmapctl: invalid leaf value %d at index %d",
+ val, i);
+ return false;
+ } else if (unlikely(val > 31)) {
+ jfs_err("dmapctl: leaf value %d too large at index %d", val, i);
+ return false;
+ }
+ }
+
+ return true;
+}
+
/*
* NAME: dbMount()
*
@@ -1372,7 +1459,7 @@ dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results)
dcp = (struct dmapctl *) mp->data;
budmin = dcp->budmin;

- if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
+ if (unlikely(!check_dmapctl(dcp))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
release_metapage(mp);
return -EIO;
@@ -1702,7 +1789,7 @@ static int dbFindCtl(struct bmap * bmp, int l2nb, int level, s64 * blkno)
dcp = (struct dmapctl *) mp->data;
budmin = dcp->budmin;

- if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
+ if (unlikely(!check_dmapctl(dcp))) {
jfs_error(bmp->db_ipbmap->i_sb,
"Corrupt dmapctl page\n");
release_metapage(mp);
@@ -2485,7 +2572,7 @@ dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc, int level)
return -EIO;
dcp = (struct dmapctl *) mp->data;

- if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
+ if (unlikely(!check_dmapctl(dcp))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
release_metapage(mp);
return -EIO;
@@ -3454,6 +3541,11 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
return -EIO;
}
l2dcp = (struct dmapctl *) l2mp->data;
+ if (unlikely(!check_dmapctl(l2dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ release_metapage(l2mp);
+ return -EIO;
+ }

/* compute start L1 */
k = blkno >> L2MAXL1SIZE;
@@ -3471,6 +3563,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
if (l1mp == NULL)
goto errout;
l1dcp = (struct dmapctl *) l1mp->data;
+ if (unlikely(!check_dmapctl(l1dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ goto errout;
+ }

/* compute start L0 */
j = (blkno & (MAXL1SIZE - 1)) >> L2MAXL0SIZE;
@@ -3484,6 +3580,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
goto errout;

l1dcp = (struct dmapctl *) l1mp->data;
+ if (unlikely(!check_dmapctl(l1dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ goto errout;
+ }

/* compute start L0 */
j = 0;
@@ -3503,6 +3603,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
if (l0mp == NULL)
goto errout;
l0dcp = (struct dmapctl *) l0mp->data;
+ if (unlikely(!check_dmapctl(l0dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ goto errout;
+ }

/* compute start dmap */
i = (blkno & (MAXL0SIZE - 1)) >>
@@ -3518,6 +3622,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
goto errout;

l0dcp = (struct dmapctl *) l0mp->data;
+ if (unlikely(!check_dmapctl(l0dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ goto errout;
+ }

/* compute start dmap */
i = 0;
--
2.53.0