summaryrefslogtreecommitdiff
path: root/fs/ocfs2
AgeCommit message (Collapse)Author
9 hoursMerge branch 'mm-nonmm-unstable' of ↵Mark Brown
https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
9 hoursMerge branch 'mm-nonmm-stable' of ↵Mark Brown
https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm # Conflicts: # arch/s390/kernel/traps.c
21 hoursocfs2: cache the mount context for dentry lock callbacksCen Zhang
ocfs2_drop_dentry_lock() drops the inode reference before releasing the DENTRY lock. This lets inode eviction release the local inode and open locks before a remote unlink can proceed past the DENTRY lock. A DLM callback can still arrive before the DENTRY lock has been dropped. ocfs2_get_dentry_osb() obtains the mount context through dl_inode->i_sb, which can access a freed inode when the downconvert thread drops the last inode reference in its post-unlock callback: Downconvert thread DLM callback thread ------------------ ------------------- ocfs2_process_blocked_lock() ocfs2_dentry_post_unlock() ocfs2_dentry_lock_put() ocfs2_drop_dentry_lock() iput(dl->dl_inode) [inode reclaimed after RCU] ocfs2_blocking_ast() ocfs2_get_dentry_osb() dl->dl_inode->i_sb ocfs2_simple_drop_lockres() Store the mount context in the DENTRY lock resource's l_priv field and use the default mount-context lookup. Recover the containing dentry lock with container_of(), as already done for refcount locks, and remove ocfs2_get_dentry_osb(). ocfs2_dismount_volume() shuts down the DLM before freeing the mount context, so callbacks can use it without accessing the inode. Preserve the existing iput-before-unlock order to avoid delaying local inode and open lock release until after the DENTRY lock is released. Link: https://lore.kernel.org/pm-ocfs2-objects-candidate-0012-v9-eeb9084fb29577971229@gmail.com Fixes: 8ed6b23709b3 ("ocfs2: revert iput deferring code in ocfs2_drop_dentry_lock") Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Suggested-by: Joseph Qi <joseph.qi@linux.alibaba.com> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Jia-Ju Bai <baijiaju1990@gmail.com> Cc: <jjzuming@gmail.com> Cc: <stable@vger.kernel.org>
21 hoursocfs2: wait for filecheck kobject release after a failed addCen Zhang
The filecheck kobject and its release completion are embedded in ocfs2_super, so the osb must outlive the kobject. On a failed kobject_init_and_add(), creation clears fs_fcheck and calls kobject_put(), but returns without waiting for the release callback. With CONFIG_DEBUG_KOBJECT_RELEASE, the last put queues delayed cleanup. Mount failure can then free the containing osb before the embedded release timer expires: Mount failure Delayed cleanup ------------- --------------- ocfs2_filecheck_create_sysfs() kobject_init_and_add() fails fs_fcheck = NULL kobject_put() queue delayed release return to mount error cleanup ocfs2_dismount_volume() remove_sysfs() skips the wait kfree(osb) expire embedded release timer access freed osb The normal removal path waits for fs_kobj_unregister, but it returns early when fs_fcheck is NULL. The failed-add path therefore leaves the delayed work and its completion in freed storage. Wait for the existing release completion after the failed-add kobject_put(), before returning to mount error cleanup. This keeps the enclosing osb alive until the kobject has finished releasing, matching the normal teardown path. KASAN report as below: BUG: KASAN: slab-use-after-free in __run_timers+0xa6d/0xa90 Write of size 8 at addr ffff8881178a5658 by task swapper/0/0 CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted 7.3.0-rc4-next-20260921-pmb-ocfs2-functional-v1+ #1 PREEMPT(lazy) Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: <IRQ> dump_stack_lvl+0x93/0xd0 print_report+0xce/0x630 ? __run_timers+0xa6d/0xa90 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x20e/0x420 ? __run_timers+0xa6d/0xa90 kasan_report+0xe0/0x110 ? __run_timers+0xa6d/0xa90 __run_timers+0xa6d/0xa90 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx___run_timers+0x10/0x10 ? __lock_acquire+0x5a3/0x2140 ? __pfx_do_raw_spin_lock+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 timer_expire_remote+0xfa/0x160 ? __pfx_timer_expire_remote+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? trace_hardirqs_on+0x18/0x160 tmigr_handle_remote_up+0x4ba/0x9d0 ? __pfx_tmigr_handle_remote_up+0x10/0x10 ? tmigr_handle_remote+0x21a/0x340 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_release+0xc8/0x290 ? __pfx_tmigr_handle_remote_up+0x10/0x10 __walk_groups_from+0x5a/0x160 tmigr_handle_remote+0x24f/0x340 ? __pfx_tmigr_handle_remote+0x10/0x10 ? lock_acquire+0x190/0x300 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_release+0xc8/0x290 run_timer_softirq+0x222/0x270 ? __pfx_run_timer_softirq+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? lockdep_hardirqs_on_prepare+0xb8/0x1a0 ? srso_alias_return_thunk+0x5/0xfbef5 handle_softirqs+0x1fa/0x8d0 ? __pfx_handle_softirqs+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? _raw_spin_unlock+0x23/0x40 ? srso_alias_return_thunk+0x5/0xfbef5 ? __hrtimer_rearm_deferred+0x1fb/0x5d0 __irq_exit_rcu+0x113/0x1e0 irq_exit_rcu+0xe/0x20 sysvec_apic_timer_interrupt+0x6c/0x80 </IRQ> <TASK> asm_sysvec_apic_timer_interrupt+0x1a/0x20 RIP: 0010:pv_native_safe_halt+0xf/0x20 Code: fa 18 01 e9 ee 37 ff fa 0f 1f 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa eb 07 0f 00 2d 23 c7 14 00 fb f4 <e9> c7 37 ff fa 66 2e 0f 1f 84 00 00 00 00 00 66 90 90 90 90 90 90 RSP: 0018:ffffffff87c07cf8 EFLAGS: 00000206 RAX: 0000000000045d0f RBX: ffffffff87c2d4c0 RCX: ffffffff86111e95 RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffffffff815b508e RBP: fffffbfff0f85a98 R08: 0000000000000001 R09: ffffed10310c68ba R10: ffff8881886345d3 R11: 0000000000000000 R12: 0000000000000001 R13: 0000000000000000 R14: 1ffffffff0f80fa4 R15: dffffc0000000000 ? ct_kernel_exit.constprop.0+0xc5/0xf0 ? do_idle+0x3ae/0x650 default_idle+0x9/0x10 default_idle_call+0x6c/0xb0 do_idle+0x3ae/0x650 ? __pfx___schedule+0x10/0x10 ? __pfx_do_idle+0x10/0x10 ? rest_init+0x1cd/0x330 ? srso_alias_return_thunk+0x5/0xfbef5 cpu_startup_entry+0x54/0x60 rest_init+0x1e1/0x330 ? __pfx_acpi_read_bit_register+0x10/0x10 ? __pfx_rest_init+0x10/0x10 ? acpi_os_write_port+0x23/0xb0 ? __pfx_kernel_init+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? acpi_ut_status_exit+0xd6/0x180 ? srso_alias_return_thunk+0x5/0xfbef5 ? acpi_ut_status_exit+0xd6/0x180 ? srso_alias_return_thunk+0x5/0xfbef5 ? acpi_enable_subsystem+0x2b0/0x4e0 ? __pfx_x86_late_time_init+0x10/0x10 start_kernel+0x417/0x4b0 x86_64_start_reservations+0x18/0x30 x86_64_start_kernel+0x111/0x120 common_startup_64+0x13e/0x158 </TASK> Allocated by task 494: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 __kmalloc_cache_noprof+0x28d/0x610 ocfs2_initialize_super.isra.0+0xab/0x3070 ocfs2_fill_super+0x801/0x3920 get_tree_bdev_flags+0x35d/0x5f0 vfs_get_tree+0x93/0x320 fc_mount+0x1d/0x200 path_mount+0x592/0x1e00 __x64_sys_mount+0x24b/0x2d0 do_syscall_64+0x114/0x620 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 494: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x308/0x580 ocfs2_dismount_volume+0x43c/0x9b0 ocfs2_fill_super+0x258f/0x3920 get_tree_bdev_flags+0x35d/0x5f0 vfs_get_tree+0x93/0x320 fc_mount+0x1d/0x200 path_mount+0x592/0x1e00 __x64_sys_mount+0x24b/0x2d0 do_syscall_64+0x114/0x620 entry_SYSCALL_64_after_hwframe+0x77/0x7f The buggy address belongs to the object at ffff8881178a4000 which belongs to the cache kmalloc-8k of size 8192 The buggy address is located 5720 bytes inside of freed 8192-byte region [ffff8881178a4000, ffff8881178a6000) The buggy address belongs to the physical page: page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x1178a0 head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0 flags: 0x200000000000040(head|node=0|zone=2) page_type: f5(slab) raw: 0200000000000040 ffff888100043880 dead000000000122 0000000000000000 raw: 0000000000000000 0000000000020002 00000000f5000000 0000000000000000 head: 0200000000000040 ffff888100043880 dead000000000122 0000000000000000 head: 0000000000000000 0000000000020002 00000000f5000000 0000000000000000 head: 0200000000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff head: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff8881178a5500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff8881178a5580: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb >ffff8881178a5600: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff8881178a5680: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff8881178a5700: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ================================================================== Link: https://lore.kernel.org/pm-ocfs2-objects-candidate-0087-v6-78f3c3d6d70887d6e0cc@gmail.com Fixes: b9fba67b3806 ("ocfs2: fix error path kobject memory leak") Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Jia-Ju Bai <baijiaju1990@gmail.com> Cc: <jjzuming@gmail.com> Cc: <stable@vger.kernel.org>
21 hoursocfs2: validate move extents against a stable EOFCen Zhang
The move-extents range must be bounded by the inode size used for extent movement. ocfs2_ioctl_move_extents() checks and clamps the request before taking the local inode and cluster locks in ocfs2_move_extents(). It also uses separate i_size_read() calls for the range check and subtraction. A concurrent truncate can invalidate the first check and make the later unsigned subtraction underflow: Move-extents ioctl Truncate ------------------ -------- check me_start <= i_size take inode and cluster locks shrink EOF below me_start release locks compare me_start + me_len with EOF set me_len = EOF - me_start [unsigned underflow] acquire inode and cluster locks select extents using the bad range A truncate after clamping can also leave the range extending beyond EOF. Conversely, the cached size may be smaller than the size refreshed when the cluster inode lock is acquired. Early validation can then reject a valid start or permanently shorten the requested length and threshold. The me_start + me_len comparison can also wrap for a large input length. Validate and adjust the original request once, after inode_lock(), the exclusive rw lock and ocfs2_inode_lock() have stabilized and refreshed the size. Read EOF once, reject a start beyond it, and bound the length by EOF minus start. Normalize the threshold using the same EOF snapshot. This keeps zero as the default-threshold sentinel until validation uses the size that will govern extent movement. Move goal validation after range adjustment as well. Its allocation-group capacity check uses me_len and must see the EOF-clamped length. Both validation failures use the existing inode-lock unwind path before any extent movement or transaction starts. Range errors now reach the ioctl's normal copyout path, as goal errors already did. Unlike the old early rejection, a failed copyout can therefore override -EINVAL with -EFAULT. Link: https://lore.kernel.org/pm-ocfs2-move-extents-isize-rrw-v6-395bdee84ea81149068e@gmail.com Fixes: 53069d4e7695 ("Ocfs2/move_extents: move/defrag extents within a certain range.") Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Suggested-by: Joseph Qi <joseph.qi@linux.alibaba.com> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Jia-Ju Bai <baijiaju1990@gmail.com> Cc: <jjzuming@gmail.com> Cc: <stable@vger.kernel.org>
21 hoursocfs2: do not read the entry array of an empty xattr blockJoseph Qi
ocfs2_cp_xattr_block_to_bucket() sizes the name and value area it has to carry over from the block's last entry, so an empty block makes it read xh_entries[-1]. That lands on the header, where xe_name_offset overlaps xh_name_value_len, and block storage never maintains that field, so it reads as 0 and the copy runs from the header to the end of the block. The bucket that comes out has xh_count 0 but a name_value_len of blocksize less the header, and a matching xh_free_start that claims those bytes are in use -- nearly the whole bucket with the common 4K block size -- while the last block of the bucket holds a copy of the old block's bytes. Nothing but a trace event reads xh_name_value_len, the copied bytes came from the same inode and were wiped before the entry was removed, and ocfs2_defrag_xattr_bucket() recomputes xh_free_start, so this corrupts the header rather than leaking anything. Leave the bucket empty instead, carrying over the hash the block was keyed by, since ocfs2_add_new_xattr_bucket() and ocfs2_xattr_bucket_find() both read xh_entries[0] of an empty bucket. That read is the exception ocfs2_xa_remove_entry() documents rather than a new out of bounds access. A block reaches the conversion empty when truncating an external value back fails partway through: the "reusing" and "overwriting" callers of ocfs2_xa_cleanup_value_truncate() remove the entry without setting ctxt->set_abort, so ocfs2_xattr_block_set() sees a plain -ENOSPC and converts. The "growing" caller does set it, and the block form makes a single attempt with no defragment and retry in between, so that is the only route here. Link: https://lore.kernel.org/20261024151012.1059683-7-joseph.qi@linux.alibaba.com Fixes: 012255961c9e ("ocfs2: Enable xattr set in index btree") Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: <stable@vger.kernel.org>
21 hoursocfs2: do not index before the entry array when dividing an empty bucketJoseph Qi
ocfs2_divide_xattr_bucket() appends an empty bucket carrying a sentinel hash when the source bucket has no divide position, taking that hash from xh_entries[start - 1]. ocfs2_xattr_find_divide_pos() returns count when every entry shares one hash, and its loop does not run at all for count < 2, so an empty bucket comes back with start == count == 0 and the read is xh_entries[-1]. That is outside the __counted_by_le(xh_count) bounds of xh_entries[] and lands on the header, so the value picked up is xh_count | xh_free_start << 16. One above it is written into the new bucket's xh_entries[0] and committed, which leaves a bucket whose first hash does not follow the bucket before it. ocfs2_xattr_bucket_find() binary searches on xh_entries[0], so the broken key hides the range the sentinel was meant to cover: getxattr() answers -ENODATA for an attribute that is stored, and an insert can go to the wrong bucket and leave two copies of a name. The cross cluster divide hands the same value back as a tree record's e_cpos, which then claims a hash range it does not cover. Read xh_entries[0] instead. The branch is only reached when every entry shares one hash, so for a non-empty bucket it holds the same value, and for an empty one it is the slot that carries the bucket key. An empty bucket reaches the divide from ocfs2_check_xattr_bucket_collision(), which reports no collision for one, through ocfs2_add_new_xattr_bucket() and ocfs2_extend_xattr_bucket(). It does not take a failed set to get there: a divide that finds no position is itself what leaves an empty sentinel bucket behind, and a later extend can pick it as the target. Link: https://lore.kernel.org/20261024151012.1059683-6-joseph.qi@linux.alibaba.com Fixes: 80bcaf3469b8 ("ocfs2/xattr: Proper hash collision handle in bucket division") Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: <stable@vger.kernel.org>
21 hoursocfs2: do not read the entry array of an empty xattr bucketJoseph Qi
ocfs2_check_xattr_bucket_collision() decides whether splitting a bucket can make room by comparing the hash being inserted against the first and the last entry. xh_count comes off disk and is never checked, and a bucket is allowed to hold no entries: ocfs2_xa_remove_entry() keeps the last slot when it removes the only one, so that an empty bucket still carries the hash it is keyed by. xh_count then reads back as 0 while xh_entries[0] is still meaningful, and the second comparison indexes xh_entries[-1]. That is outside the __counted_by_le(xh_count) bounds of xh_entries[] and lands on the header. An entry is 16 bytes and xe_name_hash is its first field, so the value read is xh_count | xh_free_start << 16. It stays inside the block, but the answer is arbitrary, and for an empty bucket it says there is no collision, so a new bucket gets added on the strength of it. An empty bucket holds no entries to collide with. Return before reading the array. One way to reach the test with an empty bucket is a set that removes the bucket's only entry during cleanup and then fails for space again, since it is that second -ENOSPC which calls this. Growing a value past OCFS2_XATTR_INLINE_SIZE is the easy route: the cluster allocation fails, the "!orig_clusters" arm of ocfs2_xa_cleanup_value_truncate() removes the entry, and although the "growing" caller does set ctxt->set_abort, only ocfs2_xattr_block_set() and __ocfs2_xattr_set_handle() look at it, so ocfs2_xattr_set_entry_index_block() goes straight on to the collision check. This sits on commit a9ea8b397864 ("ocfs2: use the stored hash when checking xattr bucket collision"), which gave the collision check its two argument form and is marked for 6.2 and later. The read fixed here is present in the three argument version too, but a tree without that commit needs the hunk adapted by hand. Link: https://lore.kernel.org/20261024151012.1059683-5-joseph.qi@linux.alibaba.com Fixes: 012255961c9e ("ocfs2: Enable xattr set in index btree") Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: <stable@vger.kernel.org>
21 hoursocfs2: find the xattr entry again after converting a block to a bucketJoseph Qi
ocfs2_xattr_create_index_block() turns a non-indexed xattr block into a tree by copying it into a bucket, and the copy sorts the entries: a block holds them in insertion order, a bucket by name hash. ocfs2_xattr_update_xattr_search() nevertheless relocates the caller's search by the index the entry had in the block, so unless the two orders agreed it points at an unrelated entry, and nothing between there and the write compares that entry's name with the one being set. Setting an xattr whose new value no longer fits the block arrives here with xs->not_found clear. The block gave up because ocfs2_xa_block_can_reuse() wants an exact namevalue size match, while ocfs2_xa_bucket_can_reuse() only wants an entry at least as large as the new one, so the bucket accepts a pair the block rejected and the update lands on whichever entry the sort left at that index. Equal padded name lengths get that other attribute's value replaced in place and its name left alone, so setxattr returns success while the attribute asked for keeps its old value. A longer one trips ocfs2_xa_reuse_entry()'s BUG_ON, and a shorter one is refused by the bucket as well, so ocfs2_xa_prepare_entry() wipes the entry and writes the new name over it, leaving xe_name_hash describing a name that is no longer there. Find the entry again by the name being set, matching the type as well. The copy carries every entry and every name over unchanged, and (type, name) pairs are unique within a block, so the match is exact. The type matters because the handlers strip their prefix before calling ocfs2_xattr_set(), so user.foo and trusted.foo share a stored name, a stored length and a hash, and both can sit in the same block. The name has to come from xi rather than from xs->here: a failed set can remove the entry xs->here names, and only the "growing" caller sets ctxt->set_abort, which is what stops the conversion. Scan every entry rather than narrowing on the hash the way ocfs2_find_xe_in_bucket() does, since an existing entry keeps the hash it was stored under, which a kernel that sign-extended the name bytes made different from the one computed now. An empty result means the entry did not survive the failed set and this has become an insert; leaving xs->here alone would keep it pointing into the block, which is rewritten as a tree root before the caller reads it again. Link: https://lore.kernel.org/20261024151012.1059683-4-joseph.qi@linux.alibaba.com Fixes: 012255961c9e ("ocfs2: Enable xattr set in index btree") Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: <stable@vger.kernel.org>
21 hoursocfs2: find the xattr entry again after defragmenting a bucketJoseph Qi
ocfs2_xattr_set_entry_bucket() hands loc.xl_entry to ocfs2_xa_set() twice: once for the bucket as it stands, and, if that reports -ENOSPC, once more after ocfs2_defrag_xattr_bucket() has packed the name/value pairs down. Defragmenting ends by sorting the entry array on xe_name_hash. Buckets are kept sorted, so the sort only permutes entries that share a hash, but lib/sort.c is an unstable heapsort and it does permute them. loc.xl_entry is an address inside xh_entries[], so afterwards it denotes whichever entry landed in that slot rather than the one the search found. The second ocfs2_xa_set() then works on the wrong entry. Nothing on that path compares the entry's name with xi->xi_name, so what happens depends on how the two entries compare in size: ocfs2_xa_reuse_entry() trips its BUG_ON() when the padded name sizes differ, and otherwise the new value is written into the neighbouring attribute's value slot while the attribute being set is left untouched. setxattr() reports success either way. Remember the entry's hash before defragmenting and look the entry up again afterwards, the way ocfs2_xattr_bucket_find() does. An entry that cannot be found again means the bucket no longer holds what the search found, so fail with -EFSCORRUPTED rather than writing through the stale pointer. Link: https://lore.kernel.org/20261024151012.1059683-3-joseph.qi@linux.alibaba.com Fixes: 012255961c9e ("ocfs2: Enable xattr set in index btree") Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: <stable@vger.kernel.org>
21 hoursocfs2: read the xattr entry hash before defragmenting the bucketJoseph Qi
Patch series "ocfs2: fix stale xattr entry pointers and empty header reads", v1. Five defects in the ocfs2 extended attribute code, plus one robustness measure that belongs with them, all on the paths that move entries between a block, a bucket and an index tree. They fall into two groups. Stale entry pointers (patches 1 to 3) ocfs2_xa_set() writes through a pointer into xh_entries[] that two of its callers invalidate first. Defragmenting a bucket ends in a sort() on xe_name_hash, and lib/sort.c is an unstable heapsort, so entries that share a hash can trade slots. Copying a block into a bucket changes the order outright, since a block holds its entries in insertion order while a bucket holds them by hash, yet ocfs2_xattr_update_xattr_search() relocates the search by the index the entry had in the block. Neither path compares the entry's name with the one being set, so setxattr() can replace a neighbouring attribute's value and still report success, or trip ocfs2_xa_reuse_entry()'s BUG_ON. Patch 1 takes the hash before the sort; patches 2 and 3 look the entry up again afterwards. Reads of an empty entry array (patches 4 to 6) A block and a bucket may both hold no entries: ocfs2_xa_remove_entry() keeps the last slot when it removes the only one, so an empty bucket still carries the hash it is keyed by. Three sites index xh_entries[] without checking xh_count and so read xh_entries[-1], which lands on the header and comes back as xh_count | xh_free_start << 16. Nothing faults, but the value is acted on: the collision check reports no collision for an empty bucket, the divide commits a sentinel hash that hides part of the key space from ocfs2_xattr_bucket_find(), and the block to bucket copy claims storage the bucket does not hold. Patches 4 to 6 guard each site and keep the one read of xh_entries[0] that ocfs2_xa_remove_entry() documents as intentional. This patch (of 6): ocfs2_xattr_set_entry_index_block() takes the hash a bucket split has to work with out of xs->here, but only after ocfs2_xattr_set_entry_bucket() has tried and failed to fit the entry. That call defragments the bucket when it runs out of room, and ocfs2_defrag_xattr_bucket() finishes by sorting the entry array by name hash. lib/sort.c is not stable, so two entries sharing a hash may trade slots and xs->here is left naming an entry other than the one the search found. The hash value itself survives the permutation -- the array is already sorted, so a slot can only exchange with an equal-hash neighbour -- but there is no reason to depend on that. Pick the hash up before the set, where xs->here is unambiguously the entry the search landed on. No functional change intended. Link: https://lore.kernel.org/20261024151012.1059683-1-joseph.qi@linux.alibaba.com Link: https://lore.kernel.org/20261024151012.1059683-2-joseph.qi@linux.alibaba.com Fixes: e39baff18b85 ("ocfs2: use the stored hash when checking xattr bucket collision") Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org>
21 hoursocfs2/heartbeat: serialize concurrent writes to a region's dev attributeEklavya Singh
o2hb_region_dev_store() checks reg->hr_bdev_file to see if the region is already started, but nothing stops two writers from doing that at the same time. configfs only serializes writes through the same open file, so two processes that each open the dev attribute can both get past the check: A: hr_bdev_file is NULL, opens file A, sets hr_bdev_file = A B: hr_bdev_file is NULL, opens file B, sets hr_bdev_file = B A: start fails, fput(hr_bdev_file) drops B, sets it to NULL B: start fails, fput(NULL) Both calls also use the same hr_slots, hr_slot_data and hr_tmp_block, so the error paths can free the slot data twice. syzbot hit the fput(NULL) with an unbound loop device, where every start fails with -EIO. With four threads doing the same thing on a KASAN kernel I get both crashes within a second: BUG: KASAN: double-free in o2hb_unmap_slot_data+0x14d/0x2f0 BUG: kernel NULL pointer dereference, address: 0000000000000158 Add a mutex to the region and hold it in dev_store(), in the block_bytes, start_block and blocks stores, and in dev_show(), since they all look at hr_bdev_file. dev_store() cleans up with goto, so it takes the mutex in a small wrapper rather than with guard(). The mutex is dropped while dev_store() waits for the heartbeat to become steady, which takes seconds, so a read of dev doesn't stall behind a start. hr_bdev_file is already set at that point, so other writers still get -EINVAL, and the cleanup after the wait retakes the mutex. With the patch the same test ran for 10 minutes with lockdep on and nothing was reported. So did a second one, with a working loop device, that interrupts starts during the wait while other threads read and write the attributes. Link: https://lore.kernel.org/20261020212339.46591-1-eklavyasingh2106@gmail.com Fixes: a7f6a5fb4bde ("[PATCH] OCFS2: The Second Oracle Cluster Filesystem") Signed-off-by: Eklavya Singh <eklavyasingh2106@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reported-by: <syzbot+5d4e96acd0febccb5e87@syzkaller.appspotmail.com> Closes: https://syzkaller.appspot.com/bug?extid=5d4e96acd0febccb5e87 Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Deepanshu Kartikey <kartikey406@gmail.com> Cc: Cen Zhang <zzzccc427@gmail.com> Cc: <stable@vger.kernel.org>
4 daysocfs2/cluster: remove zero-address nodes from IP tree on teardownCen Zhang
o2nm_node_ipv4_address_store() accepts 0.0.0.0. It sets O2NM_NODE_ATTR_ADDRESS and links nd_ip_node into cl_node_ip_tree under cl_nodes_lock, but o2nm_node_group_drop_item() uses nd_ipv4_address as a membership test. A zero address therefore leaves the rbtree node linked when config_item_put() releases and frees struct o2nm_node. A later address store or IP lookup can then walk the stale node and dereference freed memory. During teardown, drop_item takes cl_nodes_lock, skips rb_erase() for the zero key, and then calls config_item_put(). A later o2nm_node_ipv4_address_store() duplicate check or o2nm_get_node_by_ip() lookup takes the same lock and walks the stale cl_node_ip_tree entry. Use O2NM_NODE_ATTR_ADDRESS for teardown so every published nd_ip_node is erased before configfs can release the node, without rejecting 0.0.0.0 or changing lookup semantics. Link: https://lore.kernel.org/pm-ocfs2-node-ip-rbtree-zero-address-uaf-v5-e7f64be16b7c3b0988d1@gmail.com Fixes: 0c83ed8eeb28 ("[PATCH] OCFS2: The Second Oracle Cluster Filesystem") Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Jia-Ju Bai <baijiaju1990@gmail.com> Cc: <jjzuming@gmail.com> Cc: <stable@vger.kernel.org>
4 daysocfs2: snapshot the dentry name while tracing file releaseCen Zhang
ocfs2_file_release() passes the live dentry name and length to a trace event while holding oi->ip_lock. This lock protects the open count and delays RCU reclamation of external names, but it does not serialize name changes during rename. The dentry can switch between inline and external names, and a separately loaded length can describe a different name. The ocfs2__file_ops event sizes and copies its __string separately. A concurrently rewritten inline name need not remain consistent between those operations. External names can also be selected at this call site; the existing spinlock critical section protects their allocation lifetime, while a snapshot additionally pairs a stable name with its length. Take a name snapshot before acquiring oi->ip_lock and release it after dropping the lock. Keep the trace call and open-count argument inside the original critical section. Cache the event-enabled check so the snapshot stays paired even if tracing is toggled meanwhile. Link: https://lore.kernel.org/pm-ocfs2-objects-candidate-0088-v7-6324344256f0c3b1c3d8-2@gmail.com Fixes: 468eedde23d6 ("ocfs2: Remove mlog(0) from fs/ocfs2/file.c") Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Jia-Ju Bai <baijiaju1990@gmail.com> Cc: <jjzuming@gmail.com> Cc: <stable@vger.kernel.org>
4 daysocfs2: snapshot dentry names in file and setattr tracesCen Zhang
Patch series "ocfs2: stabilize dentry names in file and setattr traces", v4. Snapshot names at all six users of ocfs2__file_ops and at the separate setattr event. Patch 1 handles the five ordinary file-operation callers and setattr. Patch 2 handles release separately to preserve its locked open-count sample and take the snapshot outside that critical section. The release explanation distinguishes its existing RCU lifetime coverage from name/length consistency. It does not claim that the ordinary callers' external-name UAF interleaving crosses the held inode spinlock. The release source change is unchanged from v3. Patch 1 also removes the read callback's redundant NULL-inode check. The two patches apply independently and leave trace formats unchanged. This patch (of 2): The open, fsync, read, write and splice-read trace calls pass the live dentry name and length to the shared ocfs2__file_ops event class. ocfs2_setattr() passes the same unprotected arguments to its own event. Both events size and copy a __string field separately. A reference to the dentry does not pin the external storage backing an individual name. With an event enabled, a caller can be preempted after loading the name pointer. A long-to-short rename can then replace the name and reclaim its old external allocation before the trace callback sizes or copies it, provided no other snapshot retains that allocation. The setattr inode lock does not exclude every rename: a same-directory rename of a source directory need not take that directory's inode lock. It can therefore change the dentry name while setattr records its event. Inline names and separately loaded lengths can also change during trace record sizing and copying. Take a dentry name snapshot around each of these six trace calls and pass both the string and length from the snapshot. Check event enablement before taking a snapshot. Keep the event classes and formats unchanged. Also remove the redundant NULL-inode check in the read callback. file_inode() returns the open file's inode, which is already used by the trace call above the check. The historical report below is from the read path. KASAN report as below: BUG: KASAN: slab-use-after-free in strlen+0x85/0xa0 Read of size 1 at addr ffff88810a5b2b18 by task python3/496 [CPU and host details omitted.] Call Trace: <TASK> dump_stack_lvl+0x93/0xd0 print_report+0xce/0x630 ? strlen+0x85/0xa0 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x20e/0x420 ? strlen+0x85/0xa0 kasan_report+0xe0/0x110 ? strlen+0x85/0xa0 strlen+0x85/0xa0 trace_event_raw_event_ocfs2__file_ops+0xf0/0x480 ? __pfx_trace_event_raw_event_ocfs2__file_ops+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? pmbd_probe_hit_cookie+0xe7/0x1c0 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_pmbd_probe_hit_cookie+0x10/0x10 ocfs2_file_read_iter+0x6cd/0xb30 ? __pfx_ocfs2_file_read_iter+0x10/0x10 ? selinux_file_permission+0x11d/0x540 ? srso_alias_return_thunk+0x5/0xfbef5 ? security_file_permission+0x26/0x80 vfs_read+0x7d2/0xc20 ? __pfx_vfs_read+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_do_sys_openat2+0x10/0x10 ksys_read+0x111/0x200 ? __pfx_ksys_read+0x10/0x10 ? irqentry_exit+0xbe/0x820 ? srso_alias_return_thunk+0x5/0xfbef5 ? trace_hardirqs_on_prepare+0x11d/0x150 ? srso_alias_return_thunk+0x5/0xfbef5 do_syscall_64+0x114/0x620 entry_SYSCALL_64_after_hwframe+0x77/0x7f [User register dump omitted.] </TASK> Allocated by task 489: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 __kmalloc_noprof+0x2de/0x780 __d_alloc+0x5fc/0x8c0 d_alloc_parallel+0x106/0x11d0 lookup_open+0x3ff/0x13e0 path_openat+0x95e/0x2020 do_file_open+0x21e/0x470 do_sys_openat2+0xf4/0x1b0 __x64_sys_openat+0x136/0x1e0 do_syscall_64+0x114/0x620 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 0: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 __rcu_free_sheaf_prepare+0x65/0x2a0 rcu_free_sheaf_nobarn+0x1d/0x50 rcu_core+0x6be/0x1ed0 handle_softirqs+0x1fa/0x8d0 __irq_exit_rcu+0x113/0x1e0 irq_exit_rcu+0xe/0x20 sysvec_apic_timer_interrupt+0x6c/0x80 asm_sysvec_apic_timer_interrupt+0x1a/0x20 The buggy address belongs to the object at ffff88810a5b2b00 which belongs to the cache kmalloc-rcl-192 of size 192 The buggy address is located 24 bytes inside of freed 192-byte region [ffff88810a5b2b00, ffff88810a5b2bc0) [Page and shadow-memory dumps omitted.] Link: https://lore.kernel.org/pm-ocfs2-objects-candidate-0088-v7-6324344256f0c3b1c3d8-0@gmail.com Link: https://lore.kernel.org/pm-ocfs2-objects-candidate-0088-v7-6324344256f0c3b1c3d8-1@gmail.com Fixes: 468eedde23d6 ("ocfs2: Remove mlog(0) from fs/ocfs2/file.c") Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Jia-Ju Bai <baijiaju1990@gmail.com> Cc: <jjzuming@gmail.com> Cc: <stable@vger.kernel.org>
4 daysocfs2/dlm: drain join handlers before releasing module textCen Zhang
The code for a registered DLM handler must remain resident until its last invocation finishes. dlm_unregister_net_handlers() only removes dlm_join_handlers from the o2net registry. o2net_handler_get() takes a kref on the wrapper, but that reference does not pin ocfs2_dlm text. o2net_process_message() calls nh_func after dropping o2net_handler_lock. With CONFIG_OCFS2_FS_O2CB=m, ocfs2_dlm can be loaded independently of ocfs2_stack_o2cb while nodemanager continues handling messages from a configured, heartbeating peer. If no dependent module or module reference prevents normal unload, the following ordering is possible: Receive worker Module unload -------------- ------------- o2net_handler_get() take wrapper kref drop handler lock <preempted> dlm_exit() unregister handlers release DLM module text call saved nh_func in released module text Use o2net_unregister_and_flush_handler_list() in the shared DLM unregister helper. It first prevents new lookups, then waits for the ordered o2net workqueue to drain, including a concurrent destruction of that queue. The acquired invocation therefore finishes before dlm_exit() returns and the loader releases the callback code. The same helper covers partial registration and dlm_init() error unwind before module memory is released. Skip the drain when the private join-handler list is empty. Early dlm_init() failures can reach this helper before any handler has been published. Partial-registration failure also reaches it a second time after the first call has already unregistered and drained the list. Neither case needs to take o2net_wq_mutex or drain unrelated work. The list is changed only by serialized module init/exit paths; a nonempty list still uses the full unregister-and-flush helper. Oops report as below: Oops: int3: 0000 [#1] SMP KASAN NOPTI CPU: 2 UID: 0 PID: 54 Comm: kworker/u16:3 Not tainted 7.3.0-rc4-next-20260921-pmb-ocfs2-functional-v1+ #1 PREEMPT(lazy) Hardware identification omitted. Workqueue: o2net o2net_rx_until_empty [ocfs2_nodemanager] RIP: 0010:0xffffffffc0423871 [Instruction byte dump omitted.] Register dump omitted. Call Trace: <TASK> ? o2net_rx_until_empty+0x19b1/0x3300 [ocfs2_nodemanager] ? __pfx_o2net_rx_until_empty+0x10/0x10 [ocfs2_nodemanager] ? lock_acquire+0x190/0x300 ? process_one_work+0x935/0x1b40 ? process_one_work+0x834/0x1b40 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_release+0xc8/0x290 ? srso_alias_return_thunk+0x5/0xfbef5 ? process_one_work+0x9a8/0x1b40 ? __pfx_process_one_work+0x10/0x10 ? lock_acquire+0x190/0x300 ? lock_is_held_type+0x8f/0x100 ? srso_alias_return_thunk+0x5/0xfbef5 ? worker_thread+0x65c/0xe40 ? srso_alias_return_thunk+0x5/0xfbef5 ? __kthread_parkme+0x177/0x220 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_worker_thread+0x10/0x10 ? kthread+0x351/0x460 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_kthread+0x10/0x10 ? ret_from_fork+0x659/0x940 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x74f/0xf70 ? __pfx_kthread+0x10/0x10 ? ret_from_fork_asm+0x1a/0x30 </TASK> Modules linked in: ocfs2_nodemanager [last unloaded: ocfs2_dlm] ---[ end trace 0000000000000000 ]--- Link: https://lore.kernel.org/pm-ocfs2-objects-candidate-0076-v6-12de5f4628f57fba3be3@gmail.com Fixes: 6714d8e86bf4 ("[PATCH] OCFS2: The Second Oracle Cluster Filesystem") Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Jia-Ju Bai <baijiaju1990@gmail.com> Cc: <jjzuming@gmail.com> Cc: <stable@vger.kernel.org>
4 daysocfs2: drain quorum work before removing the clusterCen Zhang
The configured cluster must remain available until an in-flight quorum decision has finished using its fencing method. o2quo_make_decision() drops qs_lock before calling o2quo_fence_self(), which marks heartbeat regions for unclean stop and then reads o2nm_single_cluster->cl_fence_method. Cluster removal clears the singleton without waiting for this independent system-workqueue item. When a failed-quorum decision overlaps removal of the heartbeat regions, nodes and cluster through configfs, the worker can be preempted after marking the regions and resume after the singleton has been cleared: Quorum worker Configfs removal select fence; drop qs_lock o2hb_stop_all_regions() remove regions; join heartbeat threads remove nodes; stop o2net clear singleton; release cluster read cl_fence_method The resumed read dereferences NULL instead of applying the configured fencing method. The heartbeat thread's dependency protects the local node only until the thread exits, and draining o2net does not wait for qs_work. Add o2quo_flush_work() to drain pending or running quorum decisions before clearing the singleton in o2nm_cluster_group_drop_item(). By this point the heartbeat regions and nodes have been removed, so their work publishers are stopped. The worker can finish while the cluster and its configured fencing method remain available. It does not acquire the configfs mutex held by the removal path. Keep o2quo_exit() as the counterpart of o2quo_init() for module teardown and initialization failure, and have it call the same drain helper. Cluster removal uses only the explicitly named drain operation. KASAN report as below: Oops: general protection fault, probably for non-canonical address 0xdffffc000000011b: 0000 [#1] SMP KASAN NOPTI KASAN: null-ptr-deref in range [0x00000000000008d8-0x00000000000008df] CPU: 0 UID: 0 PID: 72 Comm: kworker/0:2 Not tainted 7.3.0-rc4-next-20260921-pmb-ocfs2-functional-v1+ #1 PREEMPT(lazy) Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Workqueue: events o2quo_make_decision RIP: 0010:o2quo_fence_self+0x65/0x200 Register dump omitted. Call Trace: <TASK> o2quo_make_decision+0x345/0x3a0 ? __pfx_o2quo_make_decision+0x10/0x10 ? process_one_work+0x834/0x1b40 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_release+0xc8/0x290 ? srso_alias_return_thunk+0x5/0xfbef5 process_one_work+0x9a8/0x1b40 ? __pfx_process_one_work+0x10/0x10 ? lock_acquire+0x190/0x300 ? lock_is_held_type+0x8f/0x100 ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x65c/0xe40 ? srso_alias_return_thunk+0x5/0xfbef5 ? __kthread_parkme+0x177/0x220 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_worker_thread+0x10/0x10 kthread+0x351/0x460 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x659/0x940 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x74f/0xf70 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 </TASK> Modules linked in: ---[ end trace 0000000000000000 ]--- Link: https://lore.kernel.org/pm-ocfs2-objects-candidate-0064-v5-e3870bf6571b80b07b52@gmail.com Fixes: f6656d26d17b ("ocfs2/cluster: Make fence method configurable - v2") Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Jia-Ju Bai <baijiaju1990@gmail.com> Cc: <jjzuming@gmail.com> Cc: <stable@vger.kernel.org>
4 daysMerge tag 'modules-7.4-rc1' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/modules/linux Pull module updates from Petr Pavlu: - Ignore local labels and mapping symbols during module load These symbols were already ignored during kallsyms address-to-symbol lookups. Filter them out earlier during module loading to reduce lookup overhead and prevent them from appearing in /proc/kallsyms. - Fix potentially incorrect access of SetOnce<T> when setting Rust module parameters. - Fix a potential read of an uninitialized count when printing taint information for unloaded modules. - Allocate the kmod argument array using kmalloc_array() in preparation for conversion to type-aware allocators. - Clean up includes in linux/kmod.h Add explicit includes of linux/umh.h to files that use usermode helpers. Then clean up the #include list in linux/kmod.h, notably removing the compatibility linux/umh.h include. - Remove the error-injection.h include from linux/module.h Add explicit includes of linux/error-injection.h to files that use the ALLOW_ERROR_INJECTION() macro and currently rely on it being provided indirectly via linux/module.h. Then remove the unnecessary linux/error-injection.h include from linux/module.h. - Replace __MODULE_STRING() with __stringify() in various drivers in preparation for removing the former macro. * tag 'modules-7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/modules/linux: drivers: Replace __MODULE_STRING() with __stringify() module: Fix uninitialized count read of unloaded taint tracking record rust: module_param: Fix potentially incorrect access of `SetOnce<T>` module: Remove the error-injection.h include from linux/module.h drm/xe: Include error-injection.h for ALLOW_ERROR_INJECTION() drm/i915: Include error-injection.h for ALLOW_ERROR_INJECTION() syscalls: Include error-injection.h for ALLOW_ERROR_INJECTION() net: Include error-injection.h for ALLOW_ERROR_INJECTION() block: Include error-injection.h for ALLOW_ERROR_INJECTION() module: Bring includes in linux/kmod.h up to date time/jiffies: Include linux/sysctl.h for proc_int_u2k_conv_uop(), ... umh, treewide: Explicitly include linux/umh.h where needed modules/kmod: Allocate argv with kmalloc_array() module/kallsyms: Ignore local labels and mapping symbols during module load module/kallsyms: Refactor current symbol filtering into is_core_symbol() module/kallsyms: Rename is_mapping_symbol() to is_ignored_kernel_symbol()
7 daysocfs2: pin owning modules for open debugfs filesCen Zhang
The OCFS2 debugfs file operation tables do not set owner. The full debugfs proxy obtains real_fops with fops_get() at open and keeps it until final release. Without an owner, this does not acquire a reference to the module containing the table and its callbacks. Removing the debugfs entries does not drain retained open files. An open connected_nodes file can therefore outlive ocfs2_nodemanager and make full_proxy_release() read real_fops->release from freed module memory. Heming also reproduced the issue with fs_state and asked for the other OCFS2 debugfs operation tables to be checked. Set owner to THIS_MODULE in all eleven explicit debugfs operation tables: five in ocfs2_nodemanager, four in ocfs2_dlm, and two in ocfs2. The existing debugfs get/put pair then holds the appropriate module until final release. Leave the open, read and release callbacks unchanged. The blockcheck table already sets owner through DEFINE_DEBUGFS_ATTRIBUTE(), so it needs no change. The original controlled test used an unloadable OCFS2 module and case-local probe filters without changing the candidate open, unload or release logic. Closing a retained connected_nodes file after normal unload produced: BUG: unable to handle page fault for address: fffffbfff804cfef Oops: Oops: 0000 [#1] SMP KASAN NOPTI RIP: 0010:full_proxy_release+0xbf/0x190 Call Trace: <TASK> __fput+0x39a/0xa60 fput_close_sync+0xff/0x200 __x64_sys_close+0x8c/0xf0 do_syscall_64+0x114/0x620 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> Modules linked in: [last unloaded: ocfs2_nodemanager] Link: https://lore.kernel.org/pm-ocfs2-objects-candidate-0089-v6-f7fda843f80887488145@gmail.com Fixes: 2309e9e040fe ("ocfs2/net: Add debug interface to o2net") Fixes: 87d3d3f3931f ("ocfs2/hb: Expose the list of heartbeating nodes via debugfs") Fixes: 3ba169ccec1c ("ocfs2/cluster: Add new function o2net_fill_node_map()") Fixes: 50397507e856 ("ocfs2: Expose the file system state via debugfs") Fixes: ccd979bdbce9 ("[PATCH] OCFS2: The Second Oracle Cluster Filesystem") Fixes: 7209300a9b98 ("ocfs2/dlm: Dumps the purgelist into a debugfs file") Fixes: d0129aceaecc ("ocfs2/dlm: Dumps the mles into a debugfs file") Fixes: 4e3d24ed1a12 ("ocfs2/dlm: Dumps the lockres' into a debugfs file") Fixes: 007dce53a29c ("ocfs2/dlm: Dump the dlm state in a debugfs file") Fixes: db02754c8a12 ("ocfs2/cluster: Show o2net timing statistics") Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Suggested-by: Heming Zhao <heming.zhao@suse.com> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Kees Cook <kees@kernel.org> Cc: Karl Mehltretter <kmehltretter@gmail.com> Cc: Christophe JAILLET <christophe.jaillet@wanadoo.fr> Cc: Yufan Chen <ericterminal@gmail.com> Cc: Mike Rapoport <rppt@kernel.org> Cc: Zhang Cen <rollkingzzc@gmail.com> Cc: Christian Brauner <brauner@kernel.org> Cc: Sunil Mushran <sunil.mushran@oracle.com> Cc: Kurt Hackel <kurt.hackel@oracle.com> Cc: Jan Kara <jack@suse.cz> Cc: Jia-Ju Bai <baijiaju1990@gmail.com> Cc: <jjzuming@gmail.com> Cc: <stable@vger.kernel.org>
7 daysocfs2/cluster: initialize heartbeat regions before publishing themCen Zhang
A region on o2hb_all_regions must have an initialized config item and name. o2hb_heartbeat_group_make_item() currently links the zeroed region under o2hb_live_lock, drops the lock, and only then initializes hr_item. The lock therefore protects the list without protecting initialization from readers of the published item. With global heartbeat enabled, a DLM_QUERY_REGION from the current joining node can pass the existing-domain and protocol checks while a configfs mkdir creates another region. The DLM locks are not taken by the constructor, and the network worker does not take the configfs subsystem mutex. The following ordering is possible: Configfs mkdir O2net receive worker -------------- -------------------- Allocate reg Lock o2hb_live_lock Link into o2hb_all_regions Unlock o2hb_live_lock dlm_query_region_handler() dlm_match_regions() o2hb_get_all_regions() Lock o2hb_live_lock Copy 32 bytes from NULL name Unlock o2hb_live_lock config_item_init_type_name() The query thus dereferences NULL. Initialize hr_item before linking the region so the existing list lock also orders name initialization before any reader. Reject a failed name allocation, since the initialization helper does not propagate that error. Use config_item_put() on initialized constructor failures to release the name and respect references retained by o2hb_region_pin(). Initialize the list heads, mutex and delayed works needed by o2hb_region_release() before any such put, and leave the list node self-linked after rollback. Keep the existing handler drain before removing a failed published item. KASAN report as below: ================================================================== BUG: KASAN: null-ptr-deref in o2hb_get_all_regions+0xbb/0x1f0 Read of size 32 at addr 0000000000000000 by task kworker/u16:1/14 CPU: 2 UID: 0 PID: 14 Comm: kworker/u16:1 Not tainted 7.3.0-rc4-next-20260921-pmb-ocfs2-functional-v1+ #1 PREEMPT(lazy) Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Workqueue: o2net o2net_rx_until_empty Call Trace: <TASK> dump_stack_lvl+0x93/0xd0 kasan_report+0xe0/0x110 ? o2hb_get_all_regions+0xbb/0x1f0 kasan_check_range+0x105/0x1b0 __asan_memcpy+0x23/0x60 o2hb_get_all_regions+0xbb/0x1f0 dlm_query_region_handler+0x4c2/0xc20 ? __pfx_dlm_query_region_handler+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? trace_hardirqs_on+0x18/0x160 ? srso_alias_return_thunk+0x5/0xfbef5 ? kvm_clock_get_cycles+0x31/0x60 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_dlm_query_region_handler+0x10/0x10 o2net_rx_until_empty+0x1a50/0x3200 ? reacquire_held_locks+0xdd/0x200 ? __pfx_o2net_rx_until_empty+0x10/0x10 ? lock_acquire+0x190/0x300 ? process_one_work+0x935/0x1b40 ? process_one_work+0x834/0x1b40 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_release+0xc8/0x290 ? srso_alias_return_thunk+0x5/0xfbef5 process_one_work+0x9a8/0x1b40 ? __pfx_process_one_work+0x10/0x10 ? lock_acquire+0x190/0x300 ? lock_is_held_type+0x8f/0x100 ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x65c/0xe40 ? srso_alias_return_thunk+0x5/0xfbef5 ? __kthread_parkme+0x177/0x220 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_worker_thread+0x10/0x10 kthread+0x351/0x460 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x659/0x940 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x74f/0xf70 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 </TASK> ================================================================== Link: https://lore.kernel.org/pm-ocfs2-objects-candidate-0079-v5-f931a29339cadc0a2a70@gmail.com Fixes: 536f0741f324 ("ocfs2/cluster: Track number of global heartbeat regions") Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Yufan Chen <ericterminal@gmail.com> Cc: Christophe JAILLET <christophe.jaillet@wanadoo.fr> Cc: Kees Cook <kees@kernel.org> Cc: Karl Mehltretter <kmehltretter@gmail.com> Cc: Sunil Mushran <sunil.mushran@oracle.com> Cc: Jia-Ju Bai <baijiaju1990@gmail.com> Cc: <jjzuming@gmail.com> Cc: <stable@vger.kernel.org>
7 daysocfs2: pin refcount trees before dropping the lookup lockCen Zhang
A refcount tree returned to ocfs2_lock_refcount_tree() must stay alive until that caller releases its operation reference. However, ocfs2_get_refcount_tree() returns the cached pointer after dropping osb_lock, and the caller only takes its kref afterwards. The registry reference does not protect this gap from concurrent removal. This can happen when another node frees a refcount root and reuses its block with a different generation while this mount retains the old cached tree. Two local operations on different inodes sharing the new root, such as data CoW and an external xattr update, can then look up the same stale tree. Their inode locks are independent, and both the REFCOUNT DLM lock and rf_sem are acquired after the first operation reference. The following ordering is possible, with no other operation references: Data CoW Xattr update -------- ------------ Look up the old tree under osb_lock, then drop osb_lock Look up the same tree and take a kref Acquire the tree locks and read the new on-disk generation Erase the old tree under osb_lock Unlock and drop the operation kref Drop the registry kref, freeing tree Take a kref through the freed tree pointer The final kref_get() accesses freed rf_getcnt storage, and subsequent accesses in ocfs2_lock_refcount_tree() also use the freed allocation. Take the operation reference in ocfs2_get_refcount_tree() before releasing osb_lock, including when an insertion loses to another lookup. Removal must acquire the same lock, so it cannot drop the registry reference before the lookup has pinned the tree. Keep the existing unlock and error path puts. The two callers that already hold the refcount tree lock keep their borrowed lookup behavior and do not acquire an extra reference. KASAN report as below: BUG: KASAN: slab-use-after-free in ocfs2_lock_refcount_tree+0x94a/0x9b0 Read of size 4 at addr ffff888117212820 by task python3/497 [Kernel and hardware identification omitted.] Call Trace: <TASK> dump_stack_lvl+0x93/0xd0 print_report+0xce/0x630 ? ocfs2_lock_refcount_tree+0x94a/0x9b0 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x20e/0x420 ? ocfs2_lock_refcount_tree+0x94a/0x9b0 kasan_report+0xe0/0x110 ? ocfs2_lock_refcount_tree+0x94a/0x9b0 ocfs2_lock_refcount_tree+0x94a/0x9b0 ? __pfx_ocfs2_lock_refcount_tree+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? ocfs2_refcount_cow+0x3b6/0xd90 ? srso_alias_return_thunk+0x5/0xfbef5 ? __kmalloc_cache_noprof+0x28d/0x610 ? ocfs2_refcount_cow+0x3b6/0xd90 ocfs2_refcount_cow+0x410/0xd90 ? __pfx_ocfs2_refcount_cow+0x10/0x10 ? __pfx___might_resched+0x10/0x10 ? down_write+0x11a/0x1f0 ? __pfx_down_write+0x10/0x10 ? ocfs2_inode_unlock+0x1a9/0x260 ? __pfx_ocfs2_inode_unlock+0x10/0x10 ? ocfs2_file_write_iter+0x1413/0x1d30 ocfs2_file_write_iter+0x157b/0x1d30 ? __pfx_ocfs2_file_write_iter+0x10/0x10 ? lock_acquire+0x190/0x300 ? __x64_sys_pwrite64+0x1b7/0x220 ? __pfx___might_resched+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? security_file_permission+0x26/0x80 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_is_held_type+0x8f/0x100 vfs_write+0x639/0x1010 ? __pfx_ocfs2_file_write_iter+0x10/0x10 ? lock_vma_under_rcu+0x154/0x4a0 ? __pfx_vfs_write+0x10/0x10 ? lock_release+0x121/0x290 ? srso_alias_return_thunk+0x5/0xfbef5 __x64_sys_pwrite64+0x1b7/0x220 ? __pfx___x64_sys_pwrite64+0x10/0x10 ? irqentry_exit+0xbe/0x820 ? srso_alias_return_thunk+0x5/0xfbef5 ? trace_hardirqs_on_prepare+0x11d/0x150 ? srso_alias_return_thunk+0x5/0xfbef5 do_syscall_64+0x114/0x620 entry_SYSCALL_64_after_hwframe+0x77/0x7f [Register dump omitted.] </TASK> Allocated by task 491: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 __kmalloc_cache_noprof+0x28d/0x610 ocfs2_allocate_refcount_tree+0x49/0x1a0 ocfs2_create_refcount_tree+0x5d7/0x1630 ocfs2_attach_refcount_tree+0x57d/0x1100 __ocfs2_reflink.isra.0+0x1c4/0x1940 ocfs2_reflink+0x501/0x9b0 ocfs2_reflink_ioctl+0x6b2/0x920 ocfs2_ioctl+0x538/0x640 __x64_sys_ioctl+0x163/0x1d0 do_syscall_64+0x114/0x620 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 498: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x308/0x580 ocfs2_lock_refcount_tree+0x672/0x9b0 ocfs2_xattr_set+0x72c/0x2530 ocfs2_xattr_user_set+0xaf/0xf0 __vfs_setxattr+0x143/0x1b0 __vfs_setxattr_noperm+0x112/0x5b0 __vfs_setxattr_locked+0xfe/0x2b0 vfs_setxattr+0x13c/0x370 do_setxattr+0x128/0x170 filename_setxattr+0x147/0x190 path_setxattrat+0x1ac/0x260 __x64_sys_setxattr+0xcb/0x140 do_syscall_64+0x114/0x620 entry_SYSCALL_64_after_hwframe+0x77/0x7f The buggy address belongs to the object at ffff888117212800 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 32 bytes inside of freed 1024-byte region [ffff888117212800, ffff888117212c00) [Page and memory dumps omitted.] Link: https://lore.kernel.org/pm-ocfs2-objects-candidate-0037-v4-9f1a261dfbc341721689@gmail.com Fixes: 374a263e790c ("ocfs2: Add refcount tree lock mechanism.") Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Tao Ma <tao.ma@oracle.com> Cc: Jia-Ju Bai <baijiaju1990@gmail.com> Cc: <jjzuming@gmail.com> Cc: <stable@vger.kernel.org>
7 daysocfs2: validate the xattr tree root of an indexed xattr blockJoseph Qi
ocfs2_validate_xattr_block() checks an xattr block's signature, blkno, generation and suballocator fields, and for a non-indexed block the entry array it carries. An indexed block is passed straight through, so the extent list in its xattr tree root is never looked at. Both fields that describe the list are bounded. The two places that create a root set l_count from ocfs2_xattr_recs_per_xb() and nothing rewrites it, and the insert path asserts in ocfs2_rotate_leaf() that l_next_free_rec stays within l_count. Check both when the block is first read, so a root that disagrees with the block size, or claims more records than it has room for, is caught before anything indexes into l_recs[]. An empty root is deliberately not rejected. Emptying the tree leaves it that way: ocfs2_rm_xattr_cluster() removes one extent per transaction, and the last one ends in ocfs2_truncate_rec(), whose closing ocfs2_rotate_tree_left() reaches ocfs2_remove_empty_extent() on the depth-zero root and takes l_next_free_rec to zero. That transaction commits before ocfs2_xattr_free_block() frees the block, and before ocfs2_xattr_remove() clears i_xattr_loc, in two later transactions, so a crash in between leaves an orphan inode pointing at an xattr block whose root has no extents. Both readers already cope with it: __ocfs2_xattr_index_block_find() answers -ENODATA, and ocfs2_iterate_xattr_index_block() returns without iterating. Link: https://lore.kernel.org/20261014105856.2909777-1-joseph.qi@linux.alibaba.com Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Heming Zhao <heming.zhao@suse.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org>
7 daysocfs2/dlm: hold the lookup reference across convert AST enqueueJoseph Qi
dlm_convert_lock_handler() drops the reference it took when it found the lock on res->granted at its leave label, one statement before it calls dlm_queue_ast(). dlm_free_dead_locks() takes res->spinlock and drops the list and base references, so a peer death landing in that gap leaves the handler's own put as the one that frees the lock, and __dlm_queue_ast() then reads lock->lockres from freed memory. The handler is registered next to dlm_create_lock_handler(), so a convert message buffered on a dying peer's socket arrives on the same o2net receive work that o2net_disconnect_node() does not wait for. The two can interleave as follows, with the reference counts as they are before this fix: Socket data-ready Node-down thread Receive worker ----------------- ---------------- -------------- o2net_data_ready() select sc under callback read lock o2net_disconnect_node() queue shutdown begin flush; new color queue sc_rx_work in the new color release callback lock shutdown socket flush returns read buffered convert_lock dlm_convert_lock_handler() find lock on res->granted dlm_lock_get() refs = 3 lock res->spinlock __dlmconvert_master() call_ast = 1 unlock res->spinlock DLM node-down cleanup lock res->spinlock dlm_free_dead_locks() unlink lock put list ref refs = 2 put base ref refs = 1 unlock res->spinlock dlm_lock_put() refs = 0 free lock dlm_queue_ast() __dlm_queue_ast() read lock->lockres (use-after-free) __dlmconvert_master() only moves the lock between lists, so the count entering the gap is base, list and lookup. Drop the lookup reference after the AST is queued instead of before it and the race ends with the one __dlm_queue_ast() took, which dlm_flush_asts() releases later with ast_pending cleared and ast_list unlinked. No extra get is needed, and the normal case ends with the same count as before. dlmconvert_master() has the same shape but is only reached from dlmlock() for a lock this node owns, which dlm_free_dead_locks() never matches. Link: https://lore.kernel.org/20261014112321.2955584-1-joseph.qi@linux.alibaba.com Fixes: 2c5c54aca9d0 ("ocfs2/dlm: Add missing dlm_lock_put()s") Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Heming Zhao <heming.zhao@suse.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: <stable@vger.kernel.org>
7 daysocfs2: cancel quota sync work before freeing quota infoCen Zhang
OCFS2 quota info owns dqi_sync_work and must outlive both its timer and qsync_work_fn(). ocfs2_local_free_info() frees that owner without cancelling the work, relying on the quota suspend/disable wrappers. Loading quotas on a read-only to read-write remount can successfully queue the work in ocfs2_global_read_info() and then fail while attaching a dquot to an existing inode in add_dquot_ref(). The quota core unwinds that failure directly through dquot_disable(), bypassing the OCFS2 wrapper: Remount task (s_umount write) Timer/workqueue ocfs2_global_read_info() schedules dqi_sync_work add_dquot_ref() returns an error dquot_disable() ocfs2_local_free_info() frees oinfo delayed-work timer expires qsync_work_fn() reads oinfo The timer core can then write to the freed embedded timer, or qsync_work_fn() can read oinfo->dqi_gqinode before its s_umount trylock. The outer ocfs2_disable_quotas() cleanup skips the failed type because dquot_disable() has already cleared its loaded state. Cancel dqi_sync_work synchronously at the start of ocfs2_local_free_info(), before dropping the global inode or freeing any owner state. The work is initialized whenever read_file_info() succeeds. dquot_disable() holds s_umount for write, so the callback skips its quota scan via down_read_trylock() and can finish without waiting for teardown. The synchronous cancel also handles its self-rearm. KASAN report as below: BUG: KASAN: slab-use-after-free in __run_timers+0x9e9/0xa10 Write of size 8 at addr ffff888111b0aad0 by task swapper/3/0 Call Trace: <IRQ> dump_stack_lvl+0x93/0xd0 print_report+0xce/0x630 ? __run_timers+0x9e9/0xa10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x20e/0x420 ? __run_timers+0x9e9/0xa10 kasan_report+0xe0/0x110 ? __run_timers+0x9e9/0xa10 __run_timers+0x9e9/0xa10 ? __pfx___run_timers+0x10/0x10 ? __lock_acquire+0x5a3/0x2140 ? __pfx_do_raw_spin_lock+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 timer_expire_remote+0xfa/0x160 ? __pfx_timer_expire_remote+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? trace_hardirqs_on+0x18/0x160 tmigr_handle_remote_up+0x4ba/0x9d0 ? __pfx_tmigr_handle_remote_up+0x10/0x10 ? tmigr_handle_remote+0x21a/0x340 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_release+0xc8/0x290 ? __pfx_tmigr_handle_remote_up+0x10/0x10 __walk_groups_from+0x5a/0x160 tmigr_handle_remote+0x24f/0x340 ? __pfx_tmigr_handle_remote+0x10/0x10 ? lock_acquire+0x190/0x300 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_release+0xc8/0x290 run_timer_softirq+0x222/0x270 ? __pfx_run_timer_softirq+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? lockdep_hardirqs_on_prepare+0xb8/0x1a0 ? srso_alias_return_thunk+0x5/0xfbef5 handle_softirqs+0x1fa/0x8d0 ? __pfx_handle_softirqs+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? _raw_spin_unlock+0x23/0x40 ? srso_alias_return_thunk+0x5/0xfbef5 ? __hrtimer_rearm_deferred+0x1fb/0x5d0 __irq_exit_rcu+0x113/0x1e0 irq_exit_rcu+0xe/0x20 sysvec_apic_timer_interrupt+0x6c/0x80 </IRQ> <TASK> asm_sysvec_apic_timer_interrupt+0x1a/0x20 ? ct_kernel_exit.constprop.0+0xc5/0xf0 ? do_idle+0x3ae/0x650 default_idle+0x9/0x10 default_idle_call+0x6c/0xb0 do_idle+0x3ae/0x650 ? cpuhp_online_idle+0x9c/0xb0 ? __pfx_do_idle+0x10/0x10 ? __entry_text_end+0x1020f6/0x1020f9 ? lockdep_hardirqs_on_prepare+0xeb/0x1a0 ? srso_alias_return_thunk+0x5/0xfbef5 ? trace_hardirqs_on+0x18/0x160 ? srso_alias_return_thunk+0x5/0xfbef5 cpu_startup_entry+0x54/0x60 start_secondary+0x223/0x2d0 ? __pfx_start_secondary+0x10/0x10 common_startup_64+0x13e/0x158 </TASK> Allocated by task 523: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 __kmalloc_cache_noprof+0x28d/0x610 ocfs2_local_read_info+0x181/0x1580 dquot_load_quota_sb+0x69e/0x11c0 dquot_load_quota_inode+0x227/0x3e0 ocfs2_enable_quotas+0x27c/0x520 ocfs2_reconfigure+0x964/0xd00 reconfigure_super+0x3ca/0xa70 path_mount+0x141e/0x1e80 __x64_sys_mount+0x24b/0x2d0 do_syscall_64+0x114/0x620 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 523: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x308/0x580 ocfs2_local_free_info+0x47b/0x800 dquot_disable+0x5d9/0x17a0 dquot_load_quota_sb+0xd3c/0x11c0 dquot_load_quota_inode+0x227/0x3e0 ocfs2_enable_quotas+0x27c/0x520 ocfs2_reconfigure+0x964/0xd00 reconfigure_super+0x3ca/0xa70 path_mount+0x141e/0x1e80 __x64_sys_mount+0x24b/0x2d0 do_syscall_64+0x114/0x620 entry_SYSCALL_64_after_hwframe+0x77/0x7f The buggy address belongs to the object at ffff888111b0a800 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 720 bytes inside of freed 1024-byte region [ffff888111b0a800, ffff888111b0ac00) Host details, register and page dumps omitted. Link: https://lore.kernel.org/pm-ocfs2-objects-candidate-0052-v3-b4904bd6c85daf033adf@gmail.com Fixes: 1a6152d36dee ("quota: propagate error from __dquot_initialize") Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Reviewed-by: Jan Kara <jack@suse.cz> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Chao Yu <chao@kernel.org> Cc: Jia-Ju Bai <baijiaju1990@gmail.com> Cc: <jjzuming@gmail.com> Cc: <stable@vger.kernel.org>
7 daysocfs2/dlm: hold a lock reference across grant AST enqueueCen Zhang
A newly granted remote lock must remain alive until its grant AST is queued. dlmlock_master() publishes the lock on res->granted with its base and queue references, then drops res->spinlock before calling dlm_queue_ast(). The resource's reserved AST count does not retain the lock itself. This can fail when a peer dies with an immediately grantable CREATE buffered on its socket. O2NET's node-down callback flushes receive work before DLM cleanup, but the flush only waits for work queued on entry. A data-ready callback that already selected the socket container can queue new receive work after the flush advances its work color. Socket shutdown does not discard bytes already queued for TCP receive. The late receive work and DLM node-down cleanup can interleave as follows (the lock reference operations shown are before this fix): Socket data-ready Node-down thread Receive worker ----------------- ---------------- -------------- o2net_data_ready() select sc under callback read lock o2net_disconnect_node() queue shutdown begin flush; new color queue sc_rx_work in the new color release callback lock shutdown socket flush returns read buffered CREATE dlmlock_master() lock res->spinlock add lock to granted unlock res->spinlock DLM node-down cleanup lock res->spinlock dlm_free_dead_locks() unlink lock put queue/base refs free lock unlock res->spinlock dlm_queue_ast() __dlm_queue_ast() read lock->lockres (use-after-free) DLM cleanup holds res->spinlock while dlm_free_dead_locks() drops the queue and base references. The CREATE handler no longer holds that spinlock when __dlm_queue_ast() reads the lock, before taking its AST list reference. Take a temporary lock reference under res->spinlock when a grant AST will be queued, and drop it after dlm_queue_ast(). This keeps the lock alive across cleanup until the AST path has taken its own reference, without reversing the existing AST/resource lock order. KASAN report as below: BUG: KASAN: slab-use-after-free in __dlm_queue_ast+0x8a4/0xc00 Read of size 8 at addr ffff88810ead5940 by task kworker/u16:3/52 CPU: 3 UID: 0 PID: 52 Comm: kworker/u16:3 Not tainted 7.3.0-rc4-next-20260921-pmb-bt-functional-v1+ #1 PREEMPT(lazy) Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Workqueue: o2net o2net_rx_until_empty Call Trace: <TASK> dump_stack_lvl+0x93/0xd0 print_report+0xce/0x630 ? __dlm_queue_ast+0x8a4/0xc00 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x20e/0x420 ? __dlm_queue_ast+0x8a4/0xc00 kasan_report+0xe0/0x110 ? __dlm_queue_ast+0x8a4/0xc00 __dlm_queue_ast+0x8a4/0xc00 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? do_raw_spin_lock+0x130/0x270 ? __pfx___dlm_queue_ast+0x10/0x10 ? __pfx_do_raw_spin_lock+0x10/0x10 ? _raw_spin_unlock_irqrestore+0x45/0x60 ? srso_alias_return_thunk+0x5/0xfbef5 ? lockdep_hardirqs_on_prepare+0xeb/0x1a0 dlm_queue_ast+0x46/0x70 dlmlock_master+0x51f/0xf50 ? __pfx_dlmlock_master+0x10/0x10 ? dlm_create_lock_handler+0x2b2/0x870 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_release+0xc8/0x290 dlm_create_lock_handler+0x35b/0x870 ? __pfx_dlm_create_lock_handler+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? trace_hardirqs_on+0x18/0x160 ? srso_alias_return_thunk+0x5/0xfbef5 ? kvm_clock_get_cycles+0x31/0x60 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_dlm_create_lock_handler+0x10/0x10 o2net_rx_until_empty+0x1dae/0x32c0 ? reacquire_held_locks+0xdd/0x200 ? __pfx_o2net_rx_until_empty+0x10/0x10 ? lock_acquire+0x190/0x300 ? process_one_work+0x935/0x1b40 ? process_one_work+0x834/0x1b40 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_release+0xc8/0x290 ? srso_alias_return_thunk+0x5/0xfbef5 process_one_work+0x9a8/0x1b40 ? __pfx_process_one_work+0x10/0x10 ? lock_acquire+0x190/0x300 ? lock_is_held_type+0x8f/0x100 ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x65c/0xe40 ? srso_alias_return_thunk+0x5/0xfbef5 ? __kthread_parkme+0x177/0x220 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_worker_thread+0x10/0x10 kthread+0x351/0x460 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x659/0x940 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x74f/0xf70 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 </TASK> Allocated by task 52: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_slab_alloc+0x89/0x90 kmem_cache_alloc_noprof+0x20b/0x5c0 dlm_new_lock+0x39/0x5f0 dlm_create_lock_handler+0x16e/0x870 o2net_rx_until_empty+0x1dae/0x32c0 process_one_work+0x9a8/0x1b40 worker_thread+0x65c/0xe40 kthread+0x351/0x460 ret_from_fork+0x659/0x940 ret_from_fork_asm+0x1a/0x30 Freed by task 603: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kmem_cache_free+0x242/0x580 dlm_lock_release+0x1df/0x2f0 dlm_lock_put+0x51/0x80 __dlm_hb_node_down+0x15be/0x2710 dlm_hb_node_down_cb+0x6c/0xa0 o2hb_run_event_list+0x34a/0x500 o2hb_do_disk_heartbeat+0x1a07/0x2950 o2hb_thread+0x1ed/0xdc0 kthread+0x351/0x460 ret_from_fork+0x659/0x940 ret_from_fork_asm+0x1a/0x30 The buggy address belongs to the object at ffff88810ead5900 which belongs to the cache o2dlm_lock of size 184 The buggy address is located 64 bytes inside of freed 184-byte region [ffff88810ead5900, ffff88810ead59b8) [Page and shadow memory dumps omitted.] Link: https://lore.kernel.org/pm-ocfs2-objects-candidate-0043-v5-1ad07df761ff49638789@gmail.com Fixes: 2c5c54aca9d0 ("ocfs2/dlm: Add missing dlm_lock_put()s") Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Kees Cook <kees@kernel.org> Cc: Sunil Mushran <sunil.mushran@oracle.com> Cc: Jia-Ju Bai <baijiaju1990@gmail.com> Cc: <jjzuming@gmail.com> Cc: <stable@vger.kernel.org>
7 daysocfs2: publish the filecheck owner before adding sysfs attributesCen Zhang
ocfs2_filecheck_create_sysfs() initializes the filecheck allocation before calling kobject_init_and_add(), but assigns entry->fs_fcheck only after that call returns. The default attribute group can already be visible while registration is in progress. A concurrent read can therefore enter ocfs2_filecheck_attr_show() with a NULL fs_fcheck and try to lock its fc_lock. Assign the initialized owner before adding the kobject, so every activated attribute has valid state. Clear the pointer when addition fails, preserving the existing teardown guard and error cleanup. A controlled probe-instrumented test kernel with the original OCFS2 registration ordering produced this Oops during a concurrent sysfs read: KASAN: null-ptr-deref in range [0x0000000000000028-0x000000000000002f] RIP: 0010:kasan_byte_accessible+0x15/0x30 Call Trace: <TASK> __kasan_check_byte+0x13/0x50 lock_acquire+0x113/0x300 _raw_spin_lock+0x31/0x40 ocfs2_filecheck_attr_show+0x138/0x5f0 ocfs2_filecheck_show+0x5d/0x90 sysfs_kf_seq_show+0x1d3/0x360 seq_read_iter+0x480/0x1110 kernfs_fop_read_iter+0x419/0x5a0 vfs_read+0x7d2/0xc20 ksys_read+0x111/0x200 do_syscall_64+0x114/0x620 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> Modules linked in: Link: https://lore.kernel.org/pm-ocfs2-objects-candidate-0086-v4-1b3a3c5fd05c1fb41df9@gmail.com Fixes: 5f483c4abb50 ("ocfs2: add kobject for online file check") Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Heming Zhao <heming.zhao@suse.com> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Gang He <ghe@suse.com> Cc: Jia-Ju Bai <baijiaju1990@gmail.com> Cc: <jjzuming@gmail.com> Cc: <stable@vger.kernel.org>
7 daysocfs2: initialize status waiters before publishing themCen Zhang
o2net_prep_nsw() publishes its stack waiter in nn_status_idr and nn_status_list under nn_lock, then initializes the wait queue and completion status after releasing the lock. A concurrent disconnect can find that waiter through o2net_complete_nodes_nsw() and call wake_up() before the wait queue has a valid lock and list head. The sender can also overwrite a completion status with its initial value. Initialize the wait queue and status before publishing the waiter. nn_lock then orders publication against completion, and no reader can observe a partially initialized object. Leave the IDR allocation and its failure handling unchanged. A controlled case-modified test kernel used a synthetic connection to enter the real send path and overlap publication with disconnect. The candidate completion and wake-up code were unchanged. Its Oops includes: KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] RIP: 0010:__wake_up_common+0xa0/0x1f0 Call Trace: <TASK> __wake_up+0x36/0x60 o2net_complete_nsw_locked+0x222/0x370 o2net_set_nn_state+0x917/0xea0 o2net_disconnect_node+0xd0/0x190 o2net_validate_control_write+0x454/0x500 full_proxy_write+0x11f/0x180 vfs_write+0x25a/0x1010 ksys_write+0x111/0x200 do_syscall_64+0x114/0x620 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> Modules linked in: Link: https://lore.kernel.org/pm-ocfs2-objects-candidate-0066-v1-437a0422801b292a2879@gmail.com Fixes: 98211489d414 ("[PATCH] OCFS2: The Second Oracle Cluster Filesystem") Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Kees Cook <kees@kernel.org> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Kurt Hackel <kurt.hackel@oracle.com> Cc: Jia-Ju Bai <baijiaju1990@gmail.com> Cc: <jjzuming@gmail.com>
7 daysocfs2: deal with legacy signed xattr name hash valuesJoseph Qi
Commit 3bc753c06dd0 ("kbuild: treat char as always unsigned") set -funsigned-char globally, which changed the result of the naked 'char' load in ocfs2_xattr_name_hash(): hash = (hash << OCFS2_HASH_SHIFT) ^ (hash >> (8*sizeof(hash) - OCFS2_HASH_SHIFT)) ^ *name++; A name byte >= 0x80 used to sign-extend and now zero-extends, so the hash no longer matches the xe_name_hash an older kernel stored. An indexed xattr tree is searched by that hash alone, and both the bucket binary search and the entry scan within it stop as soon as the wanted hash falls below an entry's, so the entry is never reached: getxattr, setxattr and removexattr return -ENODATA for a name that listxattr still lists. Search with the current unsigned hash and, on a miss, retry with the legacy signed one, as ext4 does in commit f3bbac32475b2 ("ext4: deal with legacy signed xattr name hash values"). New entries are always stored under the unsigned hash. Skip the retry when the two hashes are equal, so that a miss on an ASCII name does not walk the tree twice. A miss is not empty handed: ocfs2_xattr_bucket_find() leaves xs->bucket holding the bucket a new entry would go into. So after a double miss drop it and search once more with the unsigned hash, otherwise a new entry would be placed by its legacy hash and stored under its unsigned one, breaking the ordering the search relies on. Only indexed trees are affected; inline xattrs and non-indexed xattr blocks compare names with memcmp and never look at the hash. Also spell out the signedness instead of leaving the current hash to -funsigned-char, as commit 854f0912f813 ("ext4: make xattr char unsignedness in hash explicit") did, so that both variants stay correct if this is backported to a kernel without the flag. Link: https://lore.kernel.org/20261009083002.2621201-4-joseph.qi@linux.alibaba.com Fixes: 3bc753c06dd0 ("kbuild: treat char as always unsigned") Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Heming Zhao <heming.zhao@suse.com> Cc: Anderson Ferneda <anderson.ferneda@braza.com.br> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: <stable@vger.kernel.org> # 6.2+
7 daysocfs2: use the stored hash when checking xattr bucket collisionJoseph Qi
ocfs2_check_xattr_bucket_collision() decides whether splitting a full bucket can make room for the entry being set by recomputing the hash of the name: u32 name_hash = ocfs2_xattr_name_hash(inode, name, strlen(name)); if (name_hash != le32_to_cpu(xh->xh_entries[0].xe_name_hash)) return 0; For a new entry that is the hash it will be stored under, so comparing it is correct. An existing entry keeps the hash it already has: an update never rewrites xe_name_hash, since ocfs2_xa_add_entry() is the only writer of that field for a real entry and ocfs2_xa_prepare_entry() calls it only when loc->xl_entry is NULL. An entry stored by a kernel that sign-extended the name bytes when hashing can still be updated, because ocfs2_xattr_find_entry() searches a non-indexed xattr block by memcmp on the name and never looks at xe_name_hash. Growing it past the space left in the block converts the block into a tree, ocfs2_cp_xattr_block_to_bucket() fills the bucket in stored hash order, and the update runs out of room in the bucket too. The collision check then compares the unsigned hash against the legacy hashes in the bucket and reports no collision. ocfs2_xattr_set_entry_index_block() goes on to allocate a bucket that ocfs2_divide_xattr_bucket() cannot fill: a bucket whose entries all share one hash has no divide position, so all it does is append an empty bucket with a sentinel hash one above the last entry's. The re-search that follows depends on where the unsigned hash sorts. Below the legacy ones, it comes back to the full bucket and the set fails with -ENOSPC, having grown the tree for nothing. Above them, it lands on the new empty bucket, which ocfs2_xattr_bucket_find() handles explicitly and ocfs2_find_xe_in_bucket() scans zero times, so the set stores a second copy of the entry under the unsigned hash and returns success. The original stays in the full bucket, listxattr reports the name twice and getxattr returns the new copy. Pass the hash the entry is stored under instead: the stored one for an existing entry, the unsigned one for a new entry. A tree whose entries are all stored under the unsigned hash sees no change, since there the two are the same value. Link: https://lore.kernel.org/20261009083002.2621201-3-joseph.qi@linux.alibaba.com Fixes: 3bc753c06dd0 ("kbuild: treat char as always unsigned") Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Heming Zhao <heming.zhao@suse.com> Cc: Anderson Ferneda <anderson.ferneda@braza.com.br> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: <stable@vger.kernel.org> # 6.2+
7 daysocfs2: deal with legacy signed dir index name hash valuesJoseph Qi
Patch series "ocfs2: deal with legacy signed name hash values", v2. Commit 3bc753c06dd0 ("kbuild: treat char as always unsigned") turned on -funsigned-char globally, which changed the result of two naked 'char' loads in ocfs2 hashing code: str2hashbuf() val = msg[i] + (val << 8); ocfs2_xattr_name_hash() hash = (hash << 5) ^ (hash >> 27) ^ *name++; A name byte >= 0x80 used to sign-extend and now zero-extends. Both results are written to disk, the first into the dx leaf when a directory entry is indexed and the second into xe_name_hash when an xattr is stored, so anything written by a pre-6.2 kernel is looked up under a different hash and is no longer found. readdir and listxattr still list those names because neither of them hashes, which makes this look like the filesystem losing entries rather than like a lookup bug. Anderson Ferneda reported it that way for directories with non-ASCII names [1]. Patches 1 and 3 do what ext4 did in commit f3bbac32475b2 ("ext4: deal with legacy signed xattr name hash values"): look up with the current unsigned hash, retry with the legacy signed one on a miss, and always store new entries under the unsigned hash. The retry is skipped when the two hashes come out equal, so an ASCII name does not walk the index twice. Both hash functions now spell out the signedness instead of leaving it to -funsigned-char, as commit 854f0912f813 ("ext4: make xattr char unsignedness in hash explicit") did, so a backport to a kernel without the flag still computes both variants correctly. Patch 2 is a separate fix, placed before patch 3 so that every commit in the series is correct on its own. ocfs2_check_xattr_bucket_collision() recomputes the hash of the name to decide whether splitting a full bucket can make room for the entry being set. That is right for a new entry and wrong for one that already exists, since an update leaves xe_name_hash alone. Against a bucket holding legacy hashes the comparison does not match, so the split goes ahead and cannot help: a bucket whose entries all share one hash has no divide position. How that ends depends on where the unsigned hash sorts. Below the legacy ones, the set fails with -ENOSPC after growing the tree for nothing. Above them, it lands in the empty bucket the split just appended and stores a second copy of the entry there while the original stays behind, so listxattr reports the name twice. Using the stored hash closes both. Nothing retires a legacy hash at runtime. Deleting or updating an entry does not rewrite it, and the only code that rehashes existing dirents is ocfs2_expand_inline_dir() on the inline to extent conversion, so the retry stays for as long as the legacy entries do. Rebuilding the index offline is what removes it for good. This patch (of 3): Commit 3bc753c06dd0 ("kbuild: treat char as always unsigned") set -funsigned-char globally, which changed the result of the naked 'char' load in str2hashbuf(): val = msg[i] + (val << 8); A name byte >= 0x80 used to sign-extend and now zero-extends, so ocfs2_dx_dir_name_hash() computes a different hash pair for every name containing one. The pair is written into the dx leaf when the entry is created, so an index built by an older kernel no longer matches and ocfs2_dx_dir_search() returns -ENOENT for a name that readdir still lists. Search with the current unsigned hash and, on a miss, retry with the legacy signed one, as ext4 does in commit f3bbac32475b2 ("ext4: deal with legacy signed xattr name hash values"). New entries are always indexed under the unsigned hash. Skip the retry when the two hashes are equal, so that a miss on an ASCII name does not walk the index twice. The retry costs a second walk of the dx tree, so a negative lookup of a name containing a byte >= 0x80 does twice the work. That is bounded and worth it, and directories whose dx root is still inline pay nothing extra, since there the retry only rescans the root block that is already loaded. Nothing retires it at runtime: a legacy entry keeps its legacy hash, because neither deleting nor updating an entry rewrites the dx hash, and the only code that rehashes existing dirents is ocfs2_expand_inline_dir() on the inline to extent conversion. Rebuilding the index offline is what removes the cost for good. Also spell out the signedness in str2hashbuf() instead of leaving the current hash to -funsigned-char, as commit 854f0912f813 ("ext4: make xattr char unsignedness in hash explicit") did, so that both variants stay correct if this is backported to a kernel without the flag. Link: https://lore.kernel.org/20261009083002.2621201-1-joseph.qi@linux.alibaba.com Link: https://lore.kernel.org/20261009083002.2621201-2-joseph.qi@linux.alibaba.com Fixes: 3bc753c06dd0 ("kbuild: treat char as always unsigned") Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reported-by: Anderson Ferneda <anderson.ferneda@braza.com.br> Closes: https://lore.kernel.org/ocfs2-devel/CP5P284MB2780AC2C3C2AB2CF2B6CB7AFBE942@CP5P284MB2780.BRAP284.PROD.OUTLOOK.COM/ Reviewed-by: Heming Zhao <heming.zhao@suse.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: <stable@vger.kernel.org> # 6.2+
7 daysocfs2/heartbeat: unlink live slots when their node is missingCen Zhang
o2hb_shutdown_slot() returns immediately when its node is no longer in the configfs registry. A slot can still be linked in o2hb_live_slots when that lookup fails. Region teardown or startup-error cleanup then frees the slot array without removing its embedded list member. With the same peer live in another region, the next list update can follow a predecessor into the freed array. Reusing the node number can also make a later insertion encounter that stale member. Always remove the slot under o2hb_live_lock, even when node lookup fails. Preserve aggregate membership accounting and the last-region DOWN callback: the existing callback interface explicitly permits a NULL node for DOWN events. Only drop the node reference when lookup returned one. This closes both normal release and startup-abort paths without changing the publication or callback locking protocol. Also require a configured node before admitting a new live slot in o2hb_check_slot(). The early live-bitmap check is not held through admission: the last live slot in another region can be removed in between. Without a node, admission can then queue an UP event with a NULL argument and hit the event helper's BUG_ON. Existing live slots can still leave and generate NULL-node DOWN events. A controlled probe-instrumented test reproduced this while stopping one of two regions after removing the peer from configfs. Its KASAN report includes: BUG: KASAN: slab-use-after-free in __list_del_entry_valid_or_report+0x1e0/0x210 Read of size 8 at addr ffff888106a4ac60 by task o2hb-pmbd0058_b/497 Call Trace: <TASK> dump_stack_lvl+0x93/0xd0 print_report+0xce/0x630 kasan_report+0xe0/0x110 __list_del_entry_valid_or_report+0x1e0/0x210 o2hb_do_disk_heartbeat+0x1591/0x2c50 o2hb_thread+0x1ed/0xe70 kthread+0x351/0x460 ret_from_fork+0x659/0x940 ret_from_fork_asm+0x1a/0x30 </TASK> Allocated by task 491: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 __kmalloc_noprof+0x2de/0x780 o2hb_region_dev_store+0x7c2/0x1b60 configfs_write_iter+0x2f9/0x4f0 vfs_write+0x639/0x1010 ksys_write+0x111/0x200 do_syscall_64+0x114/0x620 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 491: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x308/0x580 o2hb_unmap_slot_data+0x1db/0x330 o2hb_region_release+0x14b/0x4f0 config_item_cleanup+0x14b/0x230 config_item_put+0x79/0x90 configfs_rmdir+0x58a/0x910 vfs_rmdir+0x2e8/0x820 filename_rmdir+0x3b1/0x520 __x64_sys_rmdir+0x4b/0x70 do_syscall_64+0x114/0x620 entry_SYSCALL_64_after_hwframe+0x77/0x7f Link: https://lore.kernel.org/pm-ocfs2-objects-candidate-0058-v2-9446113c2b16c817c538@gmail.com Fixes: a7f6a5fb4bde ("[PATCH] OCFS2: The Second Oracle Cluster Filesystem") Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Yufan Chen <ericterminal@gmail.com> Cc: Kees Cook <kees@kernel.org> Cc: Jia-Ju Bai <baijiaju1990@gmail.com> Cc: <jjzuming@gmail.com> Cc: <stable@vger.kernel.org>
7 daysocfs2: reserve xattr value tree metadata only onceJoseph Qi
ocfs2_calc_xattr_set_need() asks for the new value's extent tree twice when it replaces an inline value smaller than a value root with one large enough to be stored outside. The "stored outside" branch reserves the tree and then falls through to meta_guess instead of going out: if (!ocfs2_xattr_is_local(xe)) { ... xv = (struct ocfs2_xattr_value_root *) (base + name_offset + name_len); value_size = OCFS2_XATTR_ROOT_SIZE; } else xv = &def_xv.xv; if (old_clusters >= new_clusters) { ... goto out; } else { meta_add += ocfs2_extend_meta_needed(&xv->xr_list); ... if (value_size >= OCFS2_XATTR_ROOT_SIZE) goto out; } On the fall-through xv is def_xv, so what gets added here is the two blocks ocfs2_extend_meta_needed() wants for an empty value root -- and both sides of meta_guess reserve that same tree again. The create side has done so since commit 3ed2be719eb9 ("ocfs2: allow for more than one data extent when creating xattr"), which ported this reservation to the create case without noticing it was already on the way in, and the existing-block side since commit 0cdc7dde00ec ("ocfs2: fix missing metadata reservation for large xattrs"). Both were chasing the same RESTART_META failure in ocfs2_xattr_extend_allocation(), for xattr create and then for xattr update. ocfs2_init_xattr_set_ctxt() hands the total straight to ocfs2_reserve_new_metadata_blocks(), so the surplus is two blocks held for the whole transaction. A setxattr that would otherwise fit can come back -ENOSPC on a filesystem whose metadata allocator is nearly exhausted. Move the reservation into the branch that stops there, leaving meta_guess as the only place that reserves for a value tree the fall through is about to create. The paths that do go out keep reserving against the value root they already have, which for an external old value is the on-disk one and so can legitimately ask for more than two blocks. Both sides of meta_guess have to come out even on their own now that the copy covering for them is gone. The existing-block side only started reserving the value tree in the commit this one is tagged against. The create side has reserved the tree since 3ed2be719eb9, but only the tree: the block ocfs2_create_xattr_block() claims came out of the same two, and on the fall-through it was the duplicate removed here that made up the difference. So "ocfs2: reserve metadata for a new xattr block and its value tree" has to land first -- without it the create side reserves 2 where 3 are needed and the value tree is back to RESTART_META. That one fixes a shortfall of its own on the path that reaches meta_guess directly, where there was never a duplicate to cover it, so it is worth taking alone; this patch is not. Link: https://lore.kernel.org/20261010113454.1420939-4-joseph.qi@linux.alibaba.com Fixes: 0cdc7dde00ec ("ocfs2: fix missing metadata reservation for large xattrs") Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org>
7 daysocfs2: reserve value tree metadata when moving an xattr into the inodeJoseph Qi
ocfs2_calc_xattr_set_need() charges no metadata for the case where an xattr living in an xattr block or bucket moves back into the inode: if (old_in_xb) { if (ocfs2_xattr_can_be_in_inode(inode, xi, xis)) { clusters_add += new_clusters; credits += ...; if (!ocfs2_xattr_is_local(xe)) credits += ocfs2_calc_extend_credits(...); goto out; } } The credits for extending a value tree are there but the matching blocks are not, so meta_add stays 0, ocfs2_init_xattr_set_ctxt() skips ocfs2_reserve_new_metadata_blocks() and ctxt->meta_ac is left NULL. This is the shape commit 0cdc7dde00ec ("ocfs2: fix missing metadata reservation for large xattrs") fixed for the xattr block case, and it is reached the same way: namevalue_size() charges a value larger than OCFS2_XATTR_INLINE_SIZE as only OCFS2_XATTR_ROOT_SIZE, so ocfs2_xattr_can_be_in_inode() accepts a new value that will be stored outside. ocfs2_xa_prepare_entry() installs a value root from def_xv and grows it, and once the cluster allocator is fragmented enough to need a second extent ocfs2_add_clusters_in_btree() finds the root full with no meta_ac at all and answers RESTART_META. ocfs2_xattr_extend_allocation() turns that into -ENOSPC, or into a BUG_ON() without that commit, and ocfs2_xa_cleanup_value_truncate() takes its !orig_clusters arm, dropping the entry and leaking the clusters already handed out until fsck.ocfs2 reclaims them. Reserve for the value tree here too. The entry moves back into the inode rather than into a new xattr block, so unlike the create side of meta_guess this needs no block of its own. Link: https://lore.kernel.org/20261010113454.1420939-3-joseph.qi@linux.alibaba.com Fixes: 78f30c314a74 ("ocfs2/xattr: Reserve meta/data at the beginning of ocfs2_xattr_set.") Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: <stable@vger.kernel.org>
7 daysocfs2: reserve metadata for a new xattr block and its value treeJoseph Qi
Patch series "ocfs2: fix metadata reservation for xattr value trees". ocfs2_calc_xattr_set_need() reserves too little metadata for a new xattr value's extent tree on three paths. The reservation cannot be topped up once the transaction has started, so ocfs2_add_clusters_in_btree() gives up with RESTART_META when the value root fills and nothing is left. ocfs2_xattr_extend_allocation() turns that into -ENOSPC, after ocfs2_xa_cleanup_value_truncate() has dropped the entry and leaked the clusters already handed out; before commit 0cdc7dde00ec ("ocfs2: fix missing metadata reservation for large xattrs") it was a BUG_ON() instead. It takes a fragmented cluster allocator to get there, since the first extent always fits in the root. Patch 1 fixes the create side of meta_guess: commit 3ed2be719eb9 ("ocfs2: allow for more than one data extent when creating xattr") replaced the block reserved for the new xattr block with the two the value tree needs instead of adding them together. Patch 2 fixes the arm that moves an xattr back into the inode, which charges credits for a value tree but no metadata for one. Patch 3 drops a second copy of the same reservation on the path that falls through to meta_guess. That one is cleanup rather than a fix and is not for stable, and it must not be backported without patch 1: the duplicate it removes is what currently covers the shortfall patch 1 fixes. This patch (of 3): The create side of meta_guess in ocfs2_calc_xattr_set_need() reserves metadata for the new value's extent tree and nothing for the xattr block that will hold it: } else { credits += OCFS2_XATTR_BLOCK_CREATE_CREDITS; if (xi->xi_value_len > OCFS2_XATTR_INLINE_SIZE) { struct ocfs2_extent_list *el = &def_xv.xv.xr_list; meta_add += ocfs2_extend_meta_needed(el); ... Commit 3ed2be719eb9 ("ocfs2: allow for more than one data extent when creating xattr") meant to add that tree on top of the meta_add += 1 the branch already had, but it moved the 1 into the else arm instead, so the external-value case lost it. The 1 is not optional: it is what ocfs2_create_xattr_block() takes from meta_ac through ocfs2_claim_metadata(), and it is spent before the value tree exists. ocfs2_extend_meta_needed() on the empty def_xv list returns 2, so the branch reserves 2 where 3 are needed. The xattr block consumes one, and when the cluster allocator is fragmented enough that the value needs more than one extent, ocfs2_add_clusters_in_btree() comes back around with a full value root and ocfs2_alloc_context_bits_left() of 1 against a threshold of 2. It answers RESTART_META, which ocfs2_xattr_extend_allocation() turns into a BUG_ON(). Commit 0cdc7dde00ec ("ocfs2: fix missing metadata reservation for large xattrs") downgraded that to -ENOSPC, at the cost of leaking the clusters already handed out: ocfs2_xa_cleanup_value_truncate() takes its !orig_clusters arm, drops the entry and leaves the clusters for fsck.ocfs2 to reclaim. Reserve for the block and the tree separately. Link: https://lore.kernel.org/20261010113454.1420939-1-joseph.qi@linux.alibaba.com Link: https://lore.kernel.org/20261010113454.1420939-2-joseph.qi@linux.alibaba.com Fixes: 3ed2be719eb9 ("ocfs2: allow for more than one data extent when creating xattr") Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: <stable@vger.kernel.org>
7 daysMerge tag 'fs_for_v7.4-rc1' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs Pull ext2, isofs, udf and quota updates from Jan Kara: - ext2 changes to support clang context tracking - multiple isofs hardenings and cleanups - several udf hardening fixes - block use of fs-specific quota formats with incorrect filesystem * tag 'fs_for_v7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs: (26 commits) udf: fix return value for non-recorded-allocated extents in udf_map_block() isofs: shrink the name conversion buffer isofs: pass the name buffer size to get_rock_ridge_filename() isofs: report the Joliet name length in statfs isofs: return long Joliet names whole isofs: Simplify isofs_read_level3_size() isofs: bound empty directory blocks in isofs_read_level3_size() quota: fix shrinker scan budget accounting in dqcache_shrink_scan() ext2: drain in-flight DIO before buffered write fallback isofs: validate directory records in isofs_read_level3_size() once_lite: Simplify condition handling and fix context analysis isofs: convert the zisofs read path to use folios isofs: fix Rock Ridge CE extent validation on multisession media shmem: Refuse to enable tmpfs format for another filesystem ocfs2: Refuse to enable ocfs2 format for another filesystem ext2: enable context analysis support for ext2 filesystem ext2: annotate s_rsv_window_root as requiring s_rsv_window_lock ext2: annotate block-mapping helpers as requiring truncate_mutex ext2: annotate ext2_init_block_alloc_info() as requiring truncate_mutex ext2: mark s_mount_state as guarded by s_lock ...
8 daysMerge tag 'vfs-7.4-rc1.idmap' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull vfs idmap constification from Christian Brauner: "This makes struct mnt_idmap const throughout the tree. A mount's idmapping is immutable. The only thing modified after alloc_mnt_idmap() is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only reads from it. That is the same model struct cred uses and the idmapping is sensitive enough to deserve it. The conversion goes bottom up through the vfs{g,u}id and fs{g,u}id helpers, the privilege checks, acl, xattr and quota and then every inode operation that takes an idmap, so ->permission(), ->getattr(), ->setattr(), ->create(), ->mkdir(), ->mknod(), ->symlink(), ->rename(), ->tmpfile(), ->get_acl(), ->set_acl() and ->fileattr_set() with their implementations in every filesystem, plus the LSM hooks. struct vfsmount, struct mount_kattr and struct kstatmount store a const pointer and nop_mnt_idmap and invalid_mnt_idmap live in rodata, so anything that tries to modify them faults instead of silently corrupting the initial idmapping" * tag 'vfs-7.4-rc1.idmap' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (27 commits) fs: make nop_mnt_idmap and invalid_mnt_idmap const fs: port mnt_idmap() and file_mnt_idmap() to const mnt_idmap fs: port vfs_*() helpers to const mnt_idmap fs: port ->setattr() to pass const mnt_idmap fs: port ->getattr() to pass const mnt_idmap fs: port ->create() to pass const mnt_idmap fs: port ->symlink() to pass const mnt_idmap fs: port ->mkdir() to pass const mnt_idmap fs: port ->rename() to pass const mnt_idmap fs: port ->mknod() to pass const mnt_idmap fs: port ->tmpfile() to pass const mnt_idmap fs: port ->get_acl() to pass const mnt_idmap fs: port ->set_acl() to pass const mnt_idmap fs: port ->fileattr_set() to pass const mnt_idmap fs: port xattr to const mnt_idmap fs: port ->permission() to pass const mnt_idmap fs: port acl to const mnt_idmap fs: port inode_init_owner() to const mnt_idmap fs: port inode_owner_or_capable() to const mnt_idmap fs: port privilege checking helpers to const mnt_idmap ...
8 daysMerge tag 'vfs-7.4-rc1.bh' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull buffer_head updates from Christian Brauner: "This stops the buffer_head write completion handlers from clearing BH_Uptodate when a write fails. A buffer whose write failed still holds exactly the data the filesystem asked to be written. It's the disk that is stale, not the buffer. Claiming otherwise has consequences. mark_buffer_dirty() warns when a filesystem redirties the buffer to retry the write, a buffer that isn't up to date gets re-read from disk which silently replaces the data with the stale on-disk copy, and the window between the write completing and BH_Uptodate being cleared is visible to anyone holding the folio lock. BH_Write_EIO already records that the last write failed. Every consumer is moved over to buffer_write_io_error(): the two core helpers in fs/buffer.c, adfs, ext2, omfs, exfat, fat, ext4, ocfs2, gfs2 and jbd2. Then write completion stops touching BH_Uptodate in both directions and BH_Write_EIO is cleared on successful completion instead of at submission, so a resubmission no longer hides an error that hasn't been fixed yet. bforget() and invalidation drop the flag so a reused block doesn't inherit someone else's write error. The groundwork for this removes b_page from struct buffer_head and lets a buffer_head point at memory outside the page cache. jbd2's shadow buffers sit on a slab folio today. A slab folio overloads ->mapping so mark_buffer_write_io_error() couldn't be called on them at all. They now point at the frozen data directly with b_folio NULL. Also included are documentation updates from Matthew for sb_bread(), sb_breadahead() and mmb_mark_buffer_dirty() and the removal of stale references to unmap_buffer() and mark_buffer_dirty_inode()" * tag 'vfs-7.4-rc1.bh' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (27 commits) buffer: Rename label buffer: Refer to folios in comments buffer: Document mmb_mark_buffer_dirty() buffer: Document sb_bread(), sb_bread_unmovable() and sb_breadahead() fs: Remove references to mark_buffer_dirty_inode() buffer: clear BH_Write_EIO when a write succeeds, not when one starts buffer: Remove reference to unmap_buffer() buffer: stop touching BH_Uptodate on write completion jbd2: check for a fast commit write error with buffer_write_io_error() jbd2: say what jbd2_freeze_jh_data()'s assertion is actually checking jbd2: report journal write errors with BH_Write_EIO gfs2: check for a metadata write error with buffer_write_io_error() ocfs2: check for a stale write error before reusing a metadata buffer ocfs2: check for a metadata write error with buffer_write_io_error() ext4: check for a metadata write error with buffer_write_io_error() fat: check for a metadata write error with buffer_write_io_error() exfat: check for a directory write error with buffer_write_io_error() omfs: check for an inode write error with buffer_write_io_error() ext2: check for an xattr block write error with buffer_write_io_error() adfs: check for a directory write error with buffer_write_io_error() ...
13 daysocfs2/dlm: Protect the recovery list during debugfs readsCen Zhang
Recovery participant records must remain alive while debug_state_print() walks reco.node_data and formats their fields. The formatter holds dlm->spinlock, but the recovery participant list is protected by dlm_reco_state_lock. In particular, dlm_destroy_recovery_area() detaches the list under dlm_reco_state_lock and frees the detached records after releasing it. A concurrent debugfs open can therefore read freed memory. Protect the participant traversal and field reads in debug_state_print() with dlm_reco_state_lock, matching the existing list writers. This also serializes the traversal against list_add_tail() during recovery-area initialization. Make the existing lock available to the debug code through dlmcommon.h; the lock remains internal to the OCFS2 DLM module. Release dlm->spinlock after formatting the other domain state and before acquiring dlm_reco_state_lock, so the debug reader does not nest the two locks. Leave recovery list insertion, detachment and freeing unchanged. KASAN report as below: BUG: KASAN: slab-use-after-free in debug_state_open+0x1169/0x12d0 Read of size 4 at addr ffff88810662ef80 by task dlm-state-stres/896 CPU: 1 UID: 0 PID: 896 Comm: dlm-state-stres Not tainted 7.3.0-rc4-next-20260921-pmb-ocfs2-functional-v1+ #1 PREEMPT(lazy) Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: <TASK> dump_stack_lvl+0x93/0xd0 print_report+0xce/0x630 ? debug_state_open+0x1169/0x12d0 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x20e/0x420 ? debug_state_open+0x1169/0x12d0 kasan_report+0xe0/0x110 ? debug_state_open+0x1169/0x12d0 debug_state_open+0x1169/0x12d0 ? __pfx_debug_state_open+0x10/0x10 full_proxy_open_regular+0x193/0x310 do_dentry_open+0x595/0x12d0 ? __pfx_full_proxy_open_regular+0x10/0x10 vfs_open_consume+0xd1/0x400 ? srso_alias_return_thunk+0x5/0xfbef5 path_openat+0x18d0/0x2020 ? __pfx_path_openat+0x10/0x10 do_file_open+0x21e/0x470 ? __pfx_do_file_open+0x10/0x10 ? _raw_spin_unlock+0x23/0x40 ? srso_alias_return_thunk+0x5/0xfbef5 ? alloc_fd+0x3a6/0x6b0 do_sys_openat2+0xf4/0x1b0 ? __pfx_do_sys_openat2+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __fput+0x5b5/0xa60 __x64_sys_openat+0x136/0x1e0 ? __pfx___x64_sys_openat+0x10/0x10 do_syscall_64+0x114/0x620 entry_SYSCALL_64_after_hwframe+0x77/0x7f [Register dump omitted.] </TASK> Allocated by task 880: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 __kmalloc_cache_noprof+0x28d/0x610 dlm_remaster_locks+0x147/0x1d90 dlm_do_recovery+0xde1/0x1580 dlm_recovery_thread+0x109/0x300 kthread+0x351/0x460 ret_from_fork+0x659/0x940 ret_from_fork_asm+0x1a/0x30 Freed by task 880: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x308/0x580 dlm_destroy_recovery_area+0x285/0x460 dlm_remaster_locks+0x17c4/0x1d90 dlm_do_recovery+0xde1/0x1580 dlm_recovery_thread+0x109/0x300 kthread+0x351/0x460 ret_from_fork+0x659/0x940 ret_from_fork_asm+0x1a/0x30 The buggy address belongs to the object at ffff88810662ef80 which belongs to the cache kmalloc-32 of size 32 The buggy address is located 0 bytes inside of freed 32-byte region [ffff88810662ef80, ffff88810662efa0) [Page and memory-state dumps omitted.] Fixes: 007dce53a29c ("ocfs2/dlm: Dump the dlm state in a debugfs file") Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Link: https://patch.msgid.link/pm-ocfs2-objects-candidate-0049-v3-184e46c47f5283f06191@gmail.com Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
14 daysocfs2/cluster: hold a reference on the heartbeat threadKarl Mehltretter
Since commit 688bc88e2046 ("ocfs2/cluster: keep heartbeat local node stable"), o2hb_thread() leaves its loop and returns when the local node changes, for example after "echo 0 > node/<name>/local". The thread was started with kthread_run() and nothing holds a reference to its task_struct, so the task is freed once it exits, while reg->hr_task still points to it. Reading the region's pid attribute then reads the freed task, and removing the region calls kthread_stop() on it: BUG: KASAN: slab-use-after-free in o2hb_region_pid_show+0xb3/0xc0 refcount_t: addition on 0; use-after-free. Oops: Oops: 0000 [#1] SMP KASAN NOPTI RIP: 0010:kthread_stop+0xb1/0x390 The thread could already return by itself before, when heartbeat start was aborted or on an unclean stop, but the local node change makes it reachable from userspace at any time. Create the thread, take a reference on it and only then wake it, so the reference cannot race with the thread exiting. Drop it with kthread_stop_put(). Link: https://lore.kernel.org/20260923010149.14391-1-kmehltretter@gmail.com Fixes: 688bc88e2046 ("ocfs2/cluster: keep heartbeat local node stable") Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reported-by: Sashiko <sashiko-bot@kernel.org> Closes: https://sashiko.dev/#/patchset/20260616074931.3774929-1-zzzccc427%40gmail.com Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Cen Zhang <zzzccc427@gmail.com> Cc: Junxiao Bi <junxiao.bi@oracle.com> Cc: Heming Zhao <heming.zhao@suse.com>
14 daysocfs2: fix array out of bound access in __ocfs2_find_path()Giorgi Kobakhia
__ocfs2_find_path() walks down the extent tree and records path by calling find_path_ins(), which appends entry to path->p_node[]. It only has 5 spots. A corrupted ocfs2 image whose extent block is pointing to itself causes __ocfs2_find_path() descent endlessly, writing past the end of path->p_node[] array. UBSAN: array-index-out-of-bounds in fs/ocfs2/alloc.c:677:14 index 5 is out of range for type 'ocfs2_path_item [5]' Call Trace: <TASK> find_path_ins (fs/ocfs2/alloc.c:677 fs/ocfs2/alloc.c:1914) __ocfs2_find_path.constprop.0 (fs/ocfs2/alloc.c:1882) ocfs2_commit_truncate (fs/ocfs2/alloc.c:1924 fs/ocfs2/alloc.c:7286) ocfs2_truncate_file (fs/ocfs2/file.c:515) ocfs2_setattr (fs/ocfs2/file.c:1224) notify_change (fs/attr.c:556) do_truncate (fs/open.c:68) do_ftruncate (fs/open.c:194 (discriminator 1)) ksys_ftruncate (fs/open.c:206) __x64_sys_ftruncate (fs/open.c:211 fs/open.c:209 fs/open.c:209) Commit a406aff8c051 ("ocfs2: validate l_tree_depth to avoid out-of-bounds access") already restricts el->l_tree_depth to be less than OCFS2_MAX_PATH_DEPTH, which is equal to 5. However, does not handle the infinite descent case. Check if the el->l_tree_depth decreases on each descent. Maximum descents are restricted to 4 and the path->p_node[] array does not overflow. Link: https://lore.kernel.org/20260922202159.2421642-1-gkobakhi@asu.edu Fixes: dcd0538ff4e8 ("ocfs2: sparse b-tree support") Signed-off-by: Giorgi Kobakhia <gkobakhi@asu.edu> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Tested-by: Xiang Mei <xmei5@asu.edu> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: LLM Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: <stable@vger.kernel.org>
14 daysocfs2: validate chunk and block counts of the local quota fileNguyen Ngoc Thang
ocfs2_local_read_info() trusts dqi_chunks and dqi_blocks from the local quota file header. If dqi_blocks is too small, ocfs2_extend_local_quota_file() computes a bogus chunk length, extends the file, and maps logical block dqi_blocks, which is already in the inode's metadata cache (the header or a chunk header read at mount). sb_getblk() returns that cached buffer and ocfs2_set_new_buffer_uptodate() hits BUG_ON(ocfs2_buffer_cached()): kernel BUG at fs/ocfs2/uptodate.c:509! ocfs2_extend_local_quota_file+0x45c/0x1100 ocfs2_create_local_dquot+0x8ac/0xb50 ocfs2_acquire_dquot+0x614/0xae0 ocfs2_get_init_inode+0xe9/0x1b0 ocfs2_mkdir+0x174/0x430 ocfs2_local_quota_add_chunk() has the same exposure. Reject a header whose counts don't fit the chunk layout or exceed i_size. Reproduced with a crafted image (dqi_blocks = 1, chunk 0 dqc_free = 0); syzbot has no reproducer for this report. Link: https://lore.kernel.org/20260920140710.43938-1-ngocthang2710.1999@gmail.com Fixes: 9e33d69f553a ("ocfs2: Implementation of local and global quota file handling") Signed-off-by: Nguyen Ngoc Thang <ngocthang2710.1999@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reported-by: syzbot+03aaa576f1daa1c1f2f0@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=03aaa576f1daa1c1f2f0 Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Junxiao Bi <junxiao.bi@oracle.com> Cc: Heming Zhao <heming.zhao@suse.com>
14 daysocfs2: fix possible deadlock between nfs_sync_rwlock and fs_reclaimJoseph Qi
syzbot detected a circular locking dependency on &osb->nfs_sync_rwlock: CPU0 CPU1 ---- ---- lock(fs_reclaim); lock(&ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE]); lock(fs_reclaim); rlock(&osb->nfs_sync_rwlock); *** DEADLOCK *** Chain exists of: &osb->nfs_sync_rwlock --> &ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE] --> fs_reclaim CPU0 is kswapd. The dentry shrinker runs under fs_reclaim and reaches ocfs2_delete_inode() through ->evict_inode(): kswapd balance_pgdat shrink_node shrink_slab super_cache_scan prune_dcache_sb shrink_dentry_list dentry_kill ocfs2_dentry_iput evict ocfs2_evict_inode ocfs2_delete_inode ocfs2_nfs_sync_lock down_read(&osb->nfs_sync_rwlock) //C0: grabbing CPU1 is a task deleting an inode. It takes nfs_sync_rwlock first, then the orphan dir and inode alloc system inode i_rwsems, and allocates the metadata reservation with GFP_KERNEL while holding them: evict ocfs2_evict_inode ocfs2_delete_inode + ocfs2_nfs_sync_lock | down_read(&osb->nfs_sync_rwlock) //C1: hold + ocfs2_wipe_inode + inode_lock(orphan_dir_inode) //C1: hold + ocfs2_truncate_for_delete | ocfs2_commit_truncate | ocfs2_remove_btree_range | ocfs2_reserve_blocks_for_rec_trunc | ocfs2_reserve_new_metadata_blocks | kzalloc_obj() //C1: grabbing | // GFP_KERNEL -> might_alloc -> fs_reclaim_acquire + ocfs2_remove_inode inode_lock(inode_alloc_inode) jbd2 pins allocations to GFP_NOFS for the lifetime of a transaction handle, but these reservations are made before ocfs2_start_trans(), so fs_reclaim is acquired with ocfs2 locks still held and the cycle closes. The same cycle is reachable from ocfs2_get_dentry() and ocfs2_get_parent(), which hold nfs_sync_rwlock for write across ocfs2_test_inode_bit() and ocfs2_iget(). This is more than a lockdep artifact. A GFP_KERNEL allocation under the orphan dir i_rwsem enters direct reclaim, which runs the shrinkers, which can evict another ocfs2 inode and re-enter ocfs2_wipe_inode() on the same task; the second inode_lock() on the singleton orphan dir inode then self-deadlocks, since i_rwsem is not recursive. Establish a GFP_NOFS allocation context for the whole nfs_sync_rwlock critical section so that nothing holding it can recurse into filesystem reclaim. Do it in ocfs2_nfs_sync_lock()/ocfs2_nfs_sync_unlock() rather than at the call sites, so that future callers cannot forget it. Link: https://lore.kernel.org/20260922093437.954127-1-joseph.qi@linux.alibaba.com Fixes: 6ca497a83e59 ("ocfs2: fix rare stale inode errors when exporting via nfs") Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reported-by: syzbot+a68ce48df87b8e36e915@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=a68ce48df87b8e36e915 Cc: Heming Zhao <heming.zhao@suse.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Junxiao Bi <junxiao.bi@oracle.com>
14 daysocfs2: restore -ERANGE check in ocfs2_xattr_tree_list_index_block()Joseph Qi
ocfs2_xattr_list_entry() returns -ERANGE when the buffer handed in by the caller is too small to hold the whole xattr name list. That is the normal listxattr(2) protocol, userspace simply retries with a larger buffer, so it is not an ocfs2 error and must not be logged as one. Commit a46fa684fcb700 ("ocfs2: Don't printk the error when listing too many xattrs.") already knew this and silenced two sites, quoting (27738,0):ocfs2_iterate_xattr_buckets:3158 ERROR: status = -34 (27738,0):ocfs2_xattr_tree_list_index_block:3264 ERROR: status = -34 The refactoring in commit 47bca4950bc40f ("ocfs2: Abstract ocfs2 xattr tree extend rec iteration process.") then rewrote ocfs2_xattr_tree_list_index_block() around the new ocfs2_iterate_xattr_index_block() helper and dropped that guard, so the second message has been emitted ever since: ocfs2_xattr_tree_list_index_block:4513 ERROR: status = -34 The exemption in ocfs2_iterate_xattr_buckets() survived because that function was left alone, and the new helper does check for -ERANGE itself, which is why only the outermost caller still logs it. Restore the missing check. Only the log line changes: -ERANGE is still returned to the VFS untouched, so listxattr(2) behaves exactly as before. Link: https://lore.kernel.org/20260922094252.971631-1-joseph.qi@linux.alibaba.com Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Junxiao Bi <junxiao.bi@oracle.com>
14 daysocfs2: retain all security xattrs during inode creationJoseph Qi
When creating a new inode, ocfs2_init_security_get() stores the security xattr returned by the LSM framework in ocfs2_security_xattr_info so that it can be written later within the same transaction. But ocfs2_initxattrs() only ever looks at the first element of the xattr array, and it keeps the name as a bare pointer into memory owned by the security layer instead of copying it. The value, by contrast, is already duplicated with kmemdup(). With stacked LSMs, security_inode_init_security() provides one xattr per LSM, so all labels but the first are silently dropped and never written to disk, leaving newly created files without the security xattrs the other LSMs rely on. The borrowed name is not an outright bug with the LSMs in tree today: security_inode_init_security() frees only ->value once the callback returns, and documents ->name as the attribute name suffix, so the pointer stays valid for as long as ocfs2 uses it. It is fragile though, as an LSM that allocates the name together with the value would turn it into a use-after-free, so the rework below copies the names as well. Fix this by storing copies of all the security xattrs: allocate an array in ocfs2_initxattrs(), copy the names and values, account for all of them in the credit calculations, and write each one in ocfs2_init_security_set(). While at it, switch the allocations from GFP_KERNEL to GFP_NOFS. The callback runs in the inode creation path with the parent directory locked, and the security layer allocates the very same xattr array with GFP_NOFS, so direct reclaim must not be allowed to recurse into the filesystem from here. Link: https://lore.kernel.org/20260917014825.678947-1-joseph.qi@linux.alibaba.com Fixes: de3004c874e7 ("ocfs2: Switch to security_inode_init_security()") Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Cc: Daniel Borkmann <daniel@iogearbox.net> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Junxiao Bi <junxiao.bi@oracle.com> Cc: Changwei Ge <gechangwei@live.cn> Cc: Jun Piao <piaojun@huawei.com> Cc: Heming Zhao <heming.zhao@suse.com>
14 daysocfs2: update xattr count before moving bucket entriesSu Yue
Since commit 2f26f58df041 ("ocfs2: annotate flexible array members with __counted_by_le()"), the xh_entries array is annotated with __counted_by_le(xh_count), so FORTIFY uses xh_count to determine its bounds. When inserting an entry into a bucket, ocfs2_xa_bucket_add_entry() shifts existing entries before incrementing xh_count. The destination therefore extends one entry past the bounds described by the old count. With CONFIG_CC_HAS_COUNTED_BY and CONFIG_FORTIFY_SOURCE enabled, ocfs2-test: single_run-WIP.sh -t reflink triggers the following failure while adding an extended attribute: [ 150.156484] memmove: detected buffer overflow: 352 byte write of buffer size 336 [ 150.156487] WARNING: lib/string_helpers.c:1036 at __fortify_report+0x3d/0x50, CPU#15: reflink_test/2336 [ 150.160496] RIP: 0010:__fortify_report+0x40/0x50 [ 150.164031] Call Trace: [ 150.164141] <TASK> [ 150.164232] __fortify_panic+0x9/0xb [ 150.164383] ocfs2_xa_bucket_add_entry.cold+0x17/0x28 [ocfs2] [ 150.164661] ocfs2_xa_set+0x8fb/0xf40 [ocfs2] Increment xh_count before memmove() so the destination bounds include the new entry. Keep the local count unchanged to calculate the insertion position and move length from the original number of entries. The caller has already checked that there is enough space for the new entry. Link: https://lore.kernel.org/20260916024750.9450-1-glass.su@suse.com Fixes: 2f26f58df041 ("ocfs2: annotate flexible array members with __counted_by_le()") Signed-off-by: Su Yue <glass.su@suse.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Heming Zhao <heming.zhao@suse.com> Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Junxiao Bi <junxiao.bi@oracle.com> Cc: Changwei Ge <gechangwei@live.cn> Cc: Jun Piao <piaojun@huawei.com> Cc: <stable@vger.kernel.org>
14 daysocfs2: fix chunk number of the first chunk in a local quota fileJiaming Zhang
Mounting a crafted OCFS2 image can corrupt kernel memory. When the local quota file header of such an image claims zero chunks, the first quota entry allocated during the mount gets a chunk number taken from a kernel pointer, and releasing that entry clears a bit far outside the chunk bitmap. The write will corrupt the data stored at that address, and the corruption may lead to a system crash or damage unrelated data. Mounting requires CAP_SYS_ADMIN, so it takes an untrusted image, such as removable media or a loop mount of a file from elsewhere. Since the chunk number comes from a kernel pointer, the address of the bit that gets cleared differs from boot to boot, and the issue has been reported under several titles: - BUG: unable to handle kernel paging request in ocfs2_local_release_dquot - KASAN: use-after-free Write in ocfs2_local_release_dquot - KASAN: slab-out-of-bounds Write in ocfs2_local_release_dquot - KASAN: slab-use-after-free Write in ocfs2_local_release_dquot - KFENCE: use-after-free write in ocfs2_local_release_dquot Note that none of these is a use-after-free in the quota code: the chunk and its buffer head are alive. The bit that gets cleared lies far outside the bitmap. KASAN and KFENCE name each report based on the object occupying that address, which explains why the same issue is reported under so many different titles. In the report I sent, the address fell in a free page, which KASAN labels use-after-free. There are known reproducers. Both a syzkaller and a C reproducer are available from the Google Drive link [1] in my report thread. The issue was found by our modified syzkaller, not a theoretical thing found by an LLM. I have not seen it outside fuzzing, so it is not affecting me in the real world. The local quota file in OCFS2 is divided into chunks, and each chunk begins with a header block holding a bitmap of the quota entries that chunk has handed out. Chunks are numbered from zero, and that number is used to convert the file offset of an entry back into a bit position in the bitmap. ocfs2_local_quota_add_chunk() appends a new chunk to the in-memory list and numbers it one past the chunk that was last: list_add_tail(&chunk->qc_chunk, &oinfo->dqi_chunk); chunk->qc_num = list_entry(chunk->qc_chunk.prev, struct ocfs2_quota_chunk, qc_chunk)->qc_num + 1; The predecessor is looked up after the new chunk is added to the list, so if the list was empty, the prev pointer is the list head itself. The head is the dqi_chunk member of struct ocfs2_mem_dqinfo and is not a chunk, so reading qc_num through it lands 16 bytes past the start of the head, on the dqi_gqinode pointer that follows it. The first chunk of the file is then numbered with the lower half of a kernel pointer instead of 0. The list is empty when the local quota file header claims the file has no chunks. ocfs2_local_read_info() takes dqi_chunks from that header without validating it, so an image with dqi_chunks == 0 takes this path when the first quota entry is allocated. ocfs2_create_local_dquot() turns the bad number into a file offset with ol_dqblk_off(), which shifts a 32-bit block number left by the block size bits, so the top bits of such a large block number are lost. ocfs2_local_release_dquot() turns the offset back into a bit index with ol_dqblk_chunk_off(), using the full chunk number, so the lost bits push that index far outside the bitmap, and clearing it corrupts unrelated memory. Compute the chunk number before putting the chunk on the list, and use 0 when the list is empty. Link: https://lore.kernel.org/20260914034940.4070970-1-r772577952@gmail.com Link: https://drive.google.com/drive/folders/1-LzPTgOALEc3eOjmgM6oOfRKkYXJ-bnO?usp=drive_link [1] Fixes: 9e33d69f553a ("ocfs2: Implementation of local and global quota file handling") Signed-off-by: Jiaming Zhang <r772577952@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Closes: https://lore.kernel.org/lkml/CANypQFZ05tpth0Xc33gmP6jgPnkY4VuezyHcsajV-SqCsmN_gg@mail.gmail.com/ Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com> Assisted-by: Claude Code:claude-opus-5 Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Junxiao Bi <junxiao.bi@oracle.com> Cc: Changwei Ge <gechangwei@live.cn> Cc: Jun Piao <piaojun@huawei.com> Cc: Heming Zhao <heming.zhao@suse.com> Cc: <stable@vger.kernel.org>
2026-10-10ocfs2: validate dl_blkno and dl_fs_generation of dir index leaf blocksJoseph Qi
ocfs2_validate_dx_leaf() checks the checksum, the signature and the entry list counts, but it never checks dl_blkno or dl_fs_generation. The inode, extent block, xattr block, refcount block and dir index root validators all check the on-disk block number against bh->b_blocknr and the generation against the superblock, and both dir index leaf fields are documented as "Must match super block". Without the checks, a stale dir index leaf block left on the device from a previously formatted filesystem at the same physical block number can pass validation as long as its signature, entry counts and checksum match. Its index entries would then be used in the new filesystem context. Both fields are written unconditionally when a leaf block is formatted in ocfs2_dx_dir_format_cluster(), from the live superblock generation and the real block number, so a correctly formatted filesystem cannot trip the new checks. The leaf block number read back here comes from on-disk dir index root extent records. Reject dir index leaf blocks whose dl_blkno or dl_fs_generation does not match, like the dir index root validator does. Link: https://lore.kernel.org/20260905142144.2869105-2-joseph.qi@linux.alibaba.com Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Heming Zhao <heming.zhao@suse.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Junxiao Bi <junxiao.bi@oracle.com> Cc: Changwei Ge <gechangwei@live.cn> Cc: Jun Piao <piaojun@huawei.com>
2026-10-10ocfs2: validate dr_fs_generation of dir index root blocksJoseph Qi
ocfs2_validate_dx_root() does not verify dr_fs_generation against the superblock generation, unlike the extent and xattr block validators which check h_fs_generation and xb_fs_generation respectively. The field is documented as "Must match super block". Without the check, a stale dir index root block left on the device from a previously formatted filesystem at the same physical block number can pass validation as long as its signature, dr_blkno and checksum match. Its index entries and suballocator information would then be used in the new filesystem context. Reject dir index root blocks whose dr_fs_generation does not match the mounted filesystem, like the extent and xattr block validators do. Link: https://lore.kernel.org/20260905142144.2869105-1-joseph.qi@linux.alibaba.com Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Heming Zhao <heming.zhao@suse.com> Cc: Changwei Ge <gechangwei@live.cn> Cc: Joel Becker <jlbec@evilplan.org> Cc: Jun Piao <piaojun@huawei.com> Cc: Junxiao Bi <junxiao.bi@oracle.com> Cc: Mark Fasheh <mark@fasheh.com>
2026-10-06ocfs2: skip uninitialized lockres in ocfs2_mark_lockres_freeing()Joseph Qi
A hard readonly mount skips ocfs2_dlm_init(), so the per-osb lock resources are never initialized and osb->cconn stays NULL. Before commit 550842cc60987 ("ocfs2: fix freeing uninitialized resource on ocfs2_dlm_shutdown") ocfs2_dismount_volume() only called ocfs2_dlm_shutdown() when osb->cconn was set. It now calls it unconditionally, so unmounting a hard readonly mount drops the osb locks and takes the never initialized l_lock in ocfs2_mark_lockres_freeing(). With lockdep enabled this triggers: INFO: trying to register non-static key. The code is fine but needs lockdep annotation, or maybe you didn't initialize this object before use? turning off the locking correctness validator. ocfs2_drop_lock() and ocfs2_lock_res_free() already skip lock resources without OCFS2_LOCK_INITIALIZED. Add the same check to ocfs2_mark_lockres_freeing(), which is reachable before them through ocfs2_simple_drop_lockres(), so an uninitialized lockres is never touched. Link: https://lore.kernel.org/20260903104744.2164235-1-joseph.qi@linux.alibaba.com Fixes: 550842cc6098 ("ocfs2: fix freeing uninitialized resource on ocfs2_dlm_shutdown") Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reported-by: ZW Tang <tangzw515@gmail.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Junxiao Bi <junxiao.bi@oracle.com> Cc: Changwei Ge <gechangwei@live.cn> Cc: Jun Piao <piaojun@huawei.com> Cc: Heming Zhao <heming.zhao@suse.com> Cc: <stable@vger.kernel.org>
2026-10-06ocfs2: reject inconsistent xattr bucket during defragJoseph Qi
ocfs2_defrag_xattr_bucket() has two mlog_bug_on_msg() checks that assume the name/value pairs in a bucket are disjoint and that xh_free_start is not below the compacted region. ocfs2_validate_xattr_bucket() only checks each entry in isolation, so a corrupt bucket holding overlapping entries, or one with an inflated xh_free_start, passes validation and then hits BUG() in defrag when a setxattr triggers it. Defrag works on a linear copy of the bucket and does not touch the real blocks before the copy back, so the checks can return an error instead of calling BUG(). Link: https://lore.kernel.org/20260903131313.2396208-3-joseph.qi@linux.alibaba.com Fixes: 012255961c9e ("ocfs2: Enable xattr set in index btree") Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Cc: Changwei Ge <gechangwei@live.cn> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Jun Piao <piaojun@huawei.com> Cc: Junxiao Bi <junxiao.bi@oracle.com> Cc: Mark Fasheh <mark@fasheh.com>