syzbot
WARNING in shmem_initxattrs
Status:
upstream: reported syz repro on 2026/11/05 11:10
Subsystems:
mm
fuse
Labels:
prio:low
[Documentation on labels]
Reported-by: syzbot+4324f771847cecb27c54@syzkaller.appspotmail.com
First crash: 22h47m, last: 13h50m
✨ AI-generated root cause summary:
In mm/shmem.c:shmem_initxattrs(), the initial reservation of inode xattr space (ispace) from sbinfo->free_ispace is gated by 'if (sbinfo->max_inodes)', and subsequent per-xattr subtraction from ispace is also gated by 'if (sbinfo->max_inodes)'. Because sbinfo->max_inodes is accessed locklessly and can be concurrently modified to 0 during a remount (shmem_reconfigure() with nr_inodes=0), ispace can be reserved when max_inodes is non-zero, but fail to be decremented when max_inodes becomes 0. Consequently, after all security xattrs are successfully allocated and added, ispace remains non-zero, tripping the WARN_ON(ispace) assertion.
▶
▼
✨ Full AI root cause analysis
Root Cause Analysis
1. Relevant Files, Functions, and Data Structures
- File:
mm/shmem.c
- Functions:
shmem_initxattrs(struct inode *inode, const struct xattr *xattr_array, void *fs_info) (lines 4400–4454)
shmem_reconfigure(struct fs_context *fc) (lines 4959–5045)
- Structures and Fields:
struct shmem_sb_info (sbinfo):
unsigned long max_inodes: Maximum number of inodes allowed, or 0 for unlimited.
unsigned long free_ispace: Inode space available in bytes.
raw_spinlock_t stat_lock: Lock protecting filesystem accounting fields.
- Local variable
size_t ispace: Tracks the temporary xattr space reservation during inode xattr initialization.
2. Technical Mechanism and Trigger Conditions
When a new inode is created on a tmpfs filesystem (e.g., via vfs_mkdir() -> shmem_mkdir() -> shmem_mknod() -> security_inode_init_security()), shmem_initxattrs() is invoked to allocate and attach the LSM security xattrs.
-
Initial Space Reservation (mm/shmem.c:4408):
if (sbinfo->max_inodes) {
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
ispace += simple_xattr_space(xattr->name,
xattr->value_len + XATTR_SECURITY_PREFIX_LEN);
}
if (ispace) {
raw_spin_lock(&sbinfo->stat_lock);
if (sbinfo->free_ispace < ispace)
ispace = 0;
else
sbinfo->free_ispace -= ispace;
raw_spin_unlock(&sbinfo->stat_lock);
if (!ispace)
return -ENOSPC;
}
}
If the filesystem is mounted with an inode limit (sbinfo->max_inodes > 0), the required memory for each xattr is calculated via simple_xattr_space(...) and deducted from sbinfo->free_ispace. For a typical SELinux context (security.selinux), this requires 83 bytes (0x53), setting local ispace = 0x53.
-
Concurrent Reconfiguration:
The subsequent loop performs memory allocations (CLASS(simple_xattr, new_xattr) and kasprintf(GFP_KERNEL_ACCOUNT, ...)), which may sleep or be preempted. During this time, shmem_initxattrs() does not hold any lock. A concurrent remount operation (shmem_reconfigure()) specifying nr_inodes=0 can run on another CPU:
if (ctx->seen & SHMEM_SEEN_INODES) {
sbinfo->max_inodes = ctx->inodes; // set to 0 (unlimited inodes)
sbinfo->free_ispace = ctx->inodes * BOGO_INODE_SIZE - used_isp;
}
This clears sbinfo->max_inodes to 0.
-
Inconsistent Gating on sbinfo->max_inodes (mm/shmem.c:4438):
When shmem_initxattrs() processes the allocated xattrs and adds them via simple_xattr_add():
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
...
if (simple_xattr_add(&sbinfo->xa_cache, &info->xattrs, new_xattr))
break;
if (sbinfo->max_inodes)
ispace -= simple_xattr_space(new_xattr->name, new_xattr->size);
retain_and_null_ptr(new_xattr);
}
Because sbinfo->max_inodes is re-read locklessly and has changed to 0, the condition if (sbinfo->max_inodes) evaluates to false. As a result, ispace -= simple_xattr_space(...) is skipped, leaving ispace un-decremented (at 0x53).
-
Assertion Failure (mm/shmem.c:4451):
All xattrs in xattr_array are successfully committed, so the error check if (xattr->name != NULL) is false. Execution reaches:
WARN_ON(ispace);
Because ispace was never decremented due to the racy check on sbinfo->max_inodes, WARN_ON(ispace) fires with ispace == 0x53.
Sample crash report:
------------[ cut here ]------------
ispace
WARNING: mm/shmem.c:4451 at shmem_initxattrs+0x3f5/0x650 mm/shmem.c:4451, CPU#3: syz.1.18954/2882
Modules linked in:
CPU: 3 UID: 0 PID: 2882 Comm: syz.1.18954 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:shmem_initxattrs+0x3f5/0x650 mm/shmem.c:4451
Code: 03 80 3c 02 00 0f 85 5d 02 00 00 48 83 3b 00 0f 85 fd fc ff ff e8 6b 3b ae ff 4c 89 ee 31 ff e8 01 36 ae ff e8 5c 3b ae ff 90 <0f> 0b 90 e9 dd fe ff ff e8 4e 3b ae ff 4d 8d 7e 78 4c 89 ff e8 52
RSP: 0018:ffffc90007eefbb0 EFLAGS: 00010293
RAX: 0000000000000000 RBX: ffff888032b8d818 RCX: 0000000000000000
RDX: ffff888030c3cb00 RSI: ffffffff82602984 RDI: 0000000000000007
RBP: ffff888023be9000 R08: 0000000000000000 R09: 0000000000000053
R10: ffff888037378973 R11: 0000000000000000 R12: dffffc0000000000
R13: 0000000000000053 R14: ffff888054123400 R15: ffff888023be9030
FS: 00007f7a3de3d6c0(0000) GS:ffff8880d477c000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f7a3ddfaff8 CR3: 000000004e80f000 CR4: 0000000000352ef0
Call Trace:
<TASK>
security_inode_init_security+0x242/0x370 security/security.c:1384
shmem_mknod+0x2ce/0x480 mm/shmem.c:4039
shmem_mkdir+0x31/0x80 mm/shmem.c:4094
vfs_mkdir+0x36d/0x8a0 fs/namei.c:5428
filename_mkdirat+0x4c4/0x630 fs/namei.c:5461
__do_sys_mkdirat fs/namei.c:5482 [inline]
__se_sys_mkdirat fs/namei.c:5479 [inline]
__x64_sys_mkdirat+0x89/0xc0 fs/namei.c:5479
do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
do_syscall_64+0x123/0x790 arch/x86/entry/syscall_64.c:84
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f7a3cf9fb19
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f7a3de3d028 EFLAGS: 00000246 ORIG_RAX: 0000000000000102
RAX: ffffffffffffffda RBX: 00007f7a3d226270 RCX: 00007f7a3cf9fb19
RDX: 00000000000001ff RSI: 0000200000000800 RDI: ffffffffffffff9c
RBP: 00007f7a3d037b70 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f7a3d226308 R14: 00007f7a3d226270 R15: 00007ffe3809a618
</TASK>
* Struck through repros no longer work on HEAD.