syzbot
WARNING in shmem_initxattrs
Status:
upstream: reported C 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: 3d07h, last: 22h00m
✨ 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.
▶
▼
Last patch testing requests (2)
Sample crash report:
------------[ cut here ]------------
ispace
WARNING: mm/shmem.c:4499 at shmem_initxattrs+0x3f5/0x650 mm/shmem.c:4499, CPU#3: syz.3.20/6999
Modules linked in:
CPU: 3 UID: 0 PID: 6999 Comm: syz.3.20 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:4499
Code: 03 80 3c 02 00 0f 85 5d 02 00 00 48 83 3b 00 0f 85 fd fc ff ff e8 eb 3b ae ff 4c 89 ee 31 ff e8 81 36 ae ff e8 dc 3b ae ff 90 <0f> 0b 90 e9 dd fe ff ff e8 ce 3b ae ff 4d 8d 7e 78 4c 89 ff e8 12
RSP: 0018:ffffc90004fa7bb0 EFLAGS: 00010293
RAX: 0000000000000000 RBX: ffff888031a28818 RCX: 0000000000000000
RDX: ffff88803397a580 RSI: ffffffff82603cc4 RDI: 0000000000000007
RBP: ffff888037912b00 R08: 0000000000000000 R09: 0000000000000053
R10: ffff88805aa5a973 R11: 0000000000000000 R12: dffffc0000000000
R13: 0000000000000053 R14: ffff88805aa58000 R15: ffff888037912b30
FS: 00007fd3ae9026c0(0000) GS:ffff8880d4775000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fcb0d7556c8 CR3: 000000001d7a3000 CR4: 0000000000352ef0
Call Trace:
<TASK>
security_inode_init_security+0x242/0x370 security/security.c:1384
shmem_mknod+0x2ce/0x480 mm/shmem.c:4087
shmem_mkdir+0x31/0x80 mm/shmem.c:4142
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:0x7fd3ad99fb19
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:00007fd3ae902028 EFLAGS: 00000246 ORIG_RAX: 0000000000000102
RAX: ffffffffffffffda RBX: 00007fd3adc26090 RCX: 00007fd3ad99fb19
RDX: 00000000000001ff RSI: 0000200000000800 RDI: ffffffffffffff9c
RBP: 00007fd3ada37b70 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007fd3adc26128 R14: 00007fd3adc26090 R15: 00007ffcae78f808
</TASK>
* Struck through repros no longer work on HEAD.