mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] jfs: fix general protection fault in dbDiscardAG
@ 2026-10-03  4:26 Rohinthan P
  0 siblings, 0 replies; only message in thread
From: Rohinthan P @ 2026-10-03  4:26 UTC (permalink / raw)
  To: Dave Kleikamp; +Cc: jfs-discussion, linux-kernel, syzbot+0266f74df36ca702aedf

syzbot reported a general protection fault in dbDiscardAG():

    Oops: general protection fault, probably for non-canonical
    address 0xdffffc0000000003: 0000 [#1] SMP KASAN PTI
    KASAN: null-ptr-deref in range
    [0x0000000000000018-0x000000000000001f]
    RIP: 0010:dbDiscardAG+0x648/0x8f0 fs/jfs/jfs_dmap.c:1726
    ...
    Call Trace:
     <TASK>
     jfs_ioc_trim+0x436/0x680 fs/jfs/jfs_discard.c:106
     jfs_ioctl+0x291/0x3b0 fs/jfs/ioctl.c:131

In dbDiscardAG(), nblocks is initialized from bmp->db_agfree[agno].
On a corrupted filesystem or when free block accounting underflows,
nblocks can be negative (e.g. -1LL / 0xffffffffffffffff).
When max_ranges is assigned from nblocks (as u64) and divided by minlen,
max_ranges retains 0xffffffffffffffff. Then max_ranges + 1 overflows to 0,
causing min_t(u64, max_ranges + 1, 32 * 1024) to evaluate to 0.

As a result, kmalloc_objs(struct range2trim, 0, GFP_NOFS) returns
ZERO_SIZE_PTR (0x10). The check (totrim == NULL) does not catch this,
the loop while (nblocks >= minlen) is skipped because nblocks is negative,
and tt->nblocks = 0 dereferences offset 8 of ZERO_SIZE_PTR (0x18),
triggering a KASAN null-ptr-deref / general protection fault.

Fix this by:
1. Validating agno against bmp->db_numag and minlen > 0.
2. Checking if nblocks < minlen before allocation; if an AG does not have
   enough free blocks to satisfy minlen, there are no extents to discard,
   so return 0 immediately.
3. Clamping nblocks to bmp->db_agsize.
4. Checking ZERO_OR_NULL_PTR(totrim) instead of just NULL.
5. In jfs_ioc_trim(), clamping agno and agno_end to db_numag - 1 and
   handling potential integer overflow in the range calculation.

Fixes: b40c2e665cd5 ("fs/jfs: TRIM support for JFS Filesystem")
Reported-by: syzbot+0266f74df36ca702aedf@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=0266f74df36ca702aedf
Signed-off-by: Rohinthan P <rokinthanp03@gmail.com>
---
 fs/jfs/jfs_discard.c |  9 ++++++++-
 fs/jfs/jfs_dmap.c    | 15 ++++++++++++++-
 2 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/fs/jfs/jfs_discard.c b/fs/jfs/jfs_discard.c
index 4b66029..3cf8d84 100644
--- a/fs/jfs/jfs_discard.c
+++ b/fs/jfs/jfs_discard.c
@@ -94,7 +94,7 @@ int jfs_ioc_trim(struct inode *ip, struct fstrim_range *range)
 		return -EINVAL;
 	}
 
-	if (end >= bmp->db_mapsize)
+	if (end >= bmp->db_mapsize || end < start)
 		end = bmp->db_mapsize - 1;
 
 	/**
@@ -102,6 +102,13 @@ int jfs_ioc_trim(struct inode *ip, struct fstrim_range *range)
 	 */
 	agno = BLKTOAG(start, JFS_SBI(ip->i_sb));
 	agno_end = BLKTOAG(end, JFS_SBI(ip->i_sb));
+	if (agno >= bmp->db_numag) {
+		up_read(&sb->s_umount);
+		return 0;
+	}
+	if (agno_end >= bmp->db_numag)
+		agno_end = bmp->db_numag - 1;
+
 	while (agno <= agno_end) {
 		trimmed += dbDiscardAG(ip, agno, minlen);
 		agno++;
diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
index a841cf2..cee0b8e 100644
--- a/fs/jfs/jfs_dmap.c
+++ b/fs/jfs/jfs_dmap.c
@@ -1673,15 +1673,26 @@ s64 dbDiscardAG(struct inode *ip, int agno, s64 minlen)
 	int count = 0, range_cnt;
 	u64 max_ranges;
 
+	if (agno < 0 || agno >= bmp->db_numag || minlen <= 0)
+		return 0;
+
 	/* prevent others from writing new stuff here, while trimming */
 	IWRITE_LOCK(ipbmap, RDWRLOCK_DMAP);
 
 	nblocks = bmp->db_agfree[agno];
+	if (nblocks < minlen) {
+		IWRITE_UNLOCK(ipbmap);
+		return 0;
+	}
+
+	if (nblocks > bmp->db_agsize)
+		nblocks = bmp->db_agsize;
+
 	max_ranges = nblocks;
 	do_div(max_ranges, minlen);
 	range_cnt = min_t(u64, max_ranges + 1, 32 * 1024);
 	totrim = kmalloc_objs(struct range2trim, range_cnt, GFP_NOFS);
-	if (totrim == NULL) {
+	if (ZERO_OR_NULL_PTR(totrim)) {
 		jfs_error(bmp->db_ipbmap->i_sb, "no memory for trim array\n");
 		IWRITE_UNLOCK(ipbmap);
 		return 0;
@@ -1704,6 +1715,8 @@ s64 dbDiscardAG(struct inode *ip, int agno, s64 minlen)
 
 			/* give a hint for the next while */
 			nblocks = bmp->db_agfree[agno];
+			if (nblocks > bmp->db_agsize)
+				nblocks = bmp->db_agsize;
 			continue;
 		} else if (rc == -ENOSPC) {
 			/* search for next smaller log2 block */
-- 
2.53.0


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-10-03  4:26 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-03  4:26 [PATCH] jfs: fix general protection fault in dbDiscardAG Rohinthan P

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®