[PATCH v2] jfs: validate active AG before updating db_active

From: Kyle Zeng

Date: Fri Jun 12 2026 - 19:42:32 EST


When an empty regular file is opened for write, jfs_open() tracks a
single active file per allocation group. The allocation group is derived
from ji->ixpxd, which is copied from the on-disk inode in
copy_from_dinode().

A corrupted image can set di_ixpxd to an address that maps beyond the
mounted bmap's db_numag. The existing code stores that unchecked result
in signed char active_ag and then uses it to index db_active[]. For
example, an AG value of 249 wraps to -7 before the atomic increment,
causing a write before db_active and corrupting adjacent struct bmap
state.

Compute the AG in an unsigned type and reject values outside db_numag
before storing active_ag or indexing db_active[]. dbMount() already
validates db_numag <= MAXAG, so accepted values fit in active_ag and in
the db_active[] array.

Fixes: d31b53e3cd06 ("JFS: Don't save agno in the inode")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: Codex:gpt-5.5
Signed-off-by: Kyle Zeng <kylebot@xxxxxxxxxx>
---

Changes in v2:
- Fix build issues.
- Avoid unnecessary type casts.

fs/jfs/file.c | 32 +++++++++++++++++++++++++++++---
1 file changed, 29 insertions(+), 3 deletions(-)

diff --git a/fs/jfs/file.c b/fs/jfs/file.c
index 81556da507b9..d34a4e95a809 100644
--- a/fs/jfs/file.c
+++ b/fs/jfs/file.c
@@ -12,6 +12,7 @@
#include "jfs_incore.h"
#include "jfs_inode.h"
#include "jfs_dmap.h"
+#include "jfs_superblock.h"
#include "jfs_txnmgr.h"
#include "jfs_xattr.h"
#include "jfs_acl.h"
@@ -38,6 +38,24 @@ int jfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
return rc ? -EIO : 0;
}

+static int jfs_get_active_ag(struct inode *inode, int *agp)
+{
+ struct jfs_inode_info *ji = JFS_IP(inode);
+ struct jfs_sb_info *sbi = JFS_SBI(inode->i_sb);
+ struct bmap *bmap = sbi->bmap;
+ u64 ag = BLKTOAG(addressPXD(&ji->ixpxd), sbi);
+
+ if (ag >= bmap->db_numag) {
+ jfs_error(inode->i_sb,
+ "inode %llu has invalid active ag %llu\n",
+ inode->i_ino, ag);
+ return -EIO;
+ }
+
+ *agp = ag;
+ return 0;
+}
+
static int jfs_open(struct inode *inode, struct file *file)
{
int rc;
@@ -63,11 +82,18 @@ static int jfs_open(struct inode *inode, struct file *file)
if (S_ISREG(inode->i_mode) && file->f_mode & FMODE_WRITE &&
(inode->i_size == 0)) {
struct jfs_inode_info *ji = JFS_IP(inode);
+ struct bmap *bmap;
+ int active_ag;
+
+ rc = jfs_get_active_ag(inode, &active_ag);
+ if (rc)
+ return rc;
+
spin_lock_irq(&ji->ag_lock);
if (ji->active_ag == -1) {
- struct jfs_sb_info *jfs_sb = JFS_SBI(inode->i_sb);
- ji->active_ag = BLKTOAG(addressPXD(&ji->ixpxd), jfs_sb);
- atomic_inc(&jfs_sb->bmap->db_active[ji->active_ag]);
+ bmap = JFS_SBI(inode->i_sb)->bmap;
+ ji->active_ag = active_ag;
+ atomic_inc(&bmap->db_active[active_ag]);
}
spin_unlock_irq(&ji->ag_lock);
}
--
2.43.0