* kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression
@ 2026-01-13 11:22 kernel test robot
2026-01-13 11:59 ` Sebastian Andrzej Siewior
0 siblings, 1 reply; 11+ messages in thread
From: kernel test robot @ 2026-01-13 11:22 UTC (permalink / raw)
To: Peter Zijlstra; +Cc: oe-kbuild-all, linux-kernel, Sebastian Andrzej Siewior
tree: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
head: b71e635feefc852405b14620a7fc58c4c80c0f73
commit: cec199c5e39bde7191a08087cc3d002ccfab31ff futex: Implement FUTEX2_NUMA
date: 9 months ago
config: arm64-randconfig-r123-20260113 (https://download.01.org/0day-ci/archive/20260113/202601131901.j7WJ9OeZ-lkp@intel.com/config)
compiler: clang version 16.0.6 (https://github.com/llvm/llvm-project 7cbf1a2591520c2491aa35339f227775f4d3adf6)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260113/202601131901.j7WJ9OeZ-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601131901.j7WJ9OeZ-lkp@intel.com/
sparse warnings: (new ones prefixed by >>)
>> kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression
>> kernel/futex/core.c:505:51: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected unsigned int [noderef] [usertype] __user *naddr @@ got void * @@
kernel/futex/core.c:505:51: sparse: expected unsigned int [noderef] [usertype] __user *naddr
kernel/futex/core.c:505:51: sparse: got void *
kernel/futex/core.c:894:9: sparse: sparse: context imbalance in 'futex_q_lockptr_lock' - wrong count at exit
vim +/__user +505 kernel/futex/core.c
446
447 /**
448 * get_futex_key() - Get parameters which are the keys for a futex
449 * @uaddr: virtual address of the futex
450 * @flags: FLAGS_*
451 * @key: address where result is stored.
452 * @rw: mapping needs to be read/write (values: FUTEX_READ,
453 * FUTEX_WRITE)
454 *
455 * Return: a negative error code or 0
456 *
457 * The key words are stored in @key on success.
458 *
459 * For shared mappings (when @fshared), the key is:
460 *
461 * ( inode->i_sequence, page->index, offset_within_page )
462 *
463 * [ also see get_inode_sequence_number() ]
464 *
465 * For private mappings (or when !@fshared), the key is:
466 *
467 * ( current->mm, address, 0 )
468 *
469 * This allows (cross process, where applicable) identification of the futex
470 * without keeping the page pinned for the duration of the FUTEX_WAIT.
471 *
472 * lock_page() might sleep, the caller should not hold a spinlock.
473 */
474 int get_futex_key(u32 __user *uaddr, unsigned int flags, union futex_key *key,
475 enum futex_access rw)
476 {
477 unsigned long address = (unsigned long)uaddr;
478 struct mm_struct *mm = current->mm;
479 struct page *page;
480 struct folio *folio;
481 struct address_space *mapping;
482 int node, err, size, ro = 0;
483 bool fshared;
484
485 fshared = flags & FLAGS_SHARED;
486 size = futex_size(flags);
487 if (flags & FLAGS_NUMA)
488 size *= 2;
489
490 /*
491 * The futex address must be "naturally" aligned.
492 */
493 key->both.offset = address % PAGE_SIZE;
494 if (unlikely((address % size) != 0))
495 return -EINVAL;
496 address -= key->both.offset;
497
498 if (unlikely(!access_ok(uaddr, size)))
499 return -EFAULT;
500
501 if (unlikely(should_fail_futex(fshared)))
502 return -EFAULT;
503
504 if (flags & FLAGS_NUMA) {
> 505 u32 __user *naddr = (void *)uaddr + size / 2;
506
507 if (futex_get_value(&node, naddr))
508 return -EFAULT;
509
510 if (node == FUTEX_NO_NODE) {
511 node = numa_node_id();
512 if (futex_put_value(node, naddr))
513 return -EFAULT;
514
515 } else if (node >= MAX_NUMNODES || !node_possible(node)) {
516 return -EINVAL;
517 }
518
519 key->both.node = node;
520
521 } else {
522 key->both.node = FUTEX_NO_NODE;
523 }
524
525 /*
526 * PROCESS_PRIVATE futexes are fast.
527 * As the mm cannot disappear under us and the 'key' only needs
528 * virtual address, we dont even have to find the underlying vma.
529 * Note : We do have to check 'uaddr' is a valid user address,
530 * but access_ok() should be faster than find_vma()
531 */
532 if (!fshared) {
533 /*
534 * On no-MMU, shared futexes are treated as private, therefore
535 * we must not include the current process in the key. Since
536 * there is only one address space, the address is a unique key
537 * on its own.
538 */
539 if (IS_ENABLED(CONFIG_MMU))
540 key->private.mm = mm;
541 else
542 key->private.mm = NULL;
543
544 key->private.address = address;
545 return 0;
546 }
547
548 again:
549 /* Ignore any VERIFY_READ mapping (futex common case) */
550 if (unlikely(should_fail_futex(true)))
551 return -EFAULT;
552
553 err = get_user_pages_fast(address, 1, FOLL_WRITE, &page);
554 /*
555 * If write access is not required (eg. FUTEX_WAIT), try
556 * and get read-only access.
557 */
558 if (err == -EFAULT && rw == FUTEX_READ) {
559 err = get_user_pages_fast(address, 1, 0, &page);
560 ro = 1;
561 }
562 if (err < 0)
563 return err;
564 else
565 err = 0;
566
567 /*
568 * The treatment of mapping from this point on is critical. The folio
569 * lock protects many things but in this context the folio lock
570 * stabilizes mapping, prevents inode freeing in the shared
571 * file-backed region case and guards against movement to swap cache.
572 *
573 * Strictly speaking the folio lock is not needed in all cases being
574 * considered here and folio lock forces unnecessarily serialization.
575 * From this point on, mapping will be re-verified if necessary and
576 * folio lock will be acquired only if it is unavoidable
577 *
578 * Mapping checks require the folio so it is looked up now. For
579 * anonymous pages, it does not matter if the folio is split
580 * in the future as the key is based on the address. For
581 * filesystem-backed pages, the precise page is required as the
582 * index of the page determines the key.
583 */
584 folio = page_folio(page);
585 mapping = READ_ONCE(folio->mapping);
586
587 /*
588 * If folio->mapping is NULL, then it cannot be an anonymous
589 * page; but it might be the ZERO_PAGE or in the gate area or
590 * in a special mapping (all cases which we are happy to fail);
591 * or it may have been a good file page when get_user_pages_fast
592 * found it, but truncated or holepunched or subjected to
593 * invalidate_complete_page2 before we got the folio lock (also
594 * cases which we are happy to fail). And we hold a reference,
595 * so refcount care in invalidate_inode_page's remove_mapping
596 * prevents drop_caches from setting mapping to NULL beneath us.
597 *
598 * The case we do have to guard against is when memory pressure made
599 * shmem_writepage move it from filecache to swapcache beneath us:
600 * an unlikely race, but we do need to retry for folio->mapping.
601 */
602 if (unlikely(!mapping)) {
603 int shmem_swizzled;
604
605 /*
606 * Folio lock is required to identify which special case above
607 * applies. If this is really a shmem page then the folio lock
608 * will prevent unexpected transitions.
609 */
610 folio_lock(folio);
611 shmem_swizzled = folio_test_swapcache(folio) || folio->mapping;
612 folio_unlock(folio);
613 folio_put(folio);
614
615 if (shmem_swizzled)
616 goto again;
617
618 return -EFAULT;
619 }
620
621 /*
622 * Private mappings are handled in a simple way.
623 *
624 * If the futex key is stored in anonymous memory, then the associated
625 * object is the mm which is implicitly pinned by the calling process.
626 *
627 * NOTE: When userspace waits on a MAP_SHARED mapping, even if
628 * it's a read-only handle, it's expected that futexes attach to
629 * the object not the particular process.
630 */
631 if (folio_test_anon(folio)) {
632 /*
633 * A RO anonymous page will never change and thus doesn't make
634 * sense for futex operations.
635 */
636 if (unlikely(should_fail_futex(true)) || ro) {
637 err = -EFAULT;
638 goto out;
639 }
640
641 key->both.offset |= FUT_OFF_MMSHARED; /* ref taken on mm */
642 key->private.mm = mm;
643 key->private.address = address;
644
645 } else {
646 struct inode *inode;
647
648 /*
649 * The associated futex object in this case is the inode and
650 * the folio->mapping must be traversed. Ordinarily this should
651 * be stabilised under folio lock but it's not strictly
652 * necessary in this case as we just want to pin the inode, not
653 * update i_pages or anything like that.
654 *
655 * The RCU read lock is taken as the inode is finally freed
656 * under RCU. If the mapping still matches expectations then the
657 * mapping->host can be safely accessed as being a valid inode.
658 */
659 rcu_read_lock();
660
661 if (READ_ONCE(folio->mapping) != mapping) {
662 rcu_read_unlock();
663 folio_put(folio);
664
665 goto again;
666 }
667
668 inode = READ_ONCE(mapping->host);
669 if (!inode) {
670 rcu_read_unlock();
671 folio_put(folio);
672
673 goto again;
674 }
675
676 key->both.offset |= FUT_OFF_INODE; /* inode-based key */
677 key->shared.i_seq = get_inode_sequence_number(inode);
678 key->shared.pgoff = page_pgoff(folio, page);
679 rcu_read_unlock();
680 }
681
682 out:
683 folio_put(folio);
684 return err;
685 }
686
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression
2026-01-13 11:22 kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression kernel test robot
@ 2026-01-13 11:59 ` Sebastian Andrzej Siewior
2026-01-13 12:10 ` Peter Zijlstra
0 siblings, 1 reply; 11+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-01-13 11:59 UTC (permalink / raw)
To: Peter Zijlstra
Cc: kernel test robot, Peter Zijlstra, oe-kbuild-all, linux-kernel
On 2026-01-13 19:22:40 [+0800], kernel test robot wrote:
> tree: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
> head: b71e635feefc852405b14620a7fc58c4c80c0f73
> commit: cec199c5e39bde7191a08087cc3d002ccfab31ff futex: Implement FUTEX2_NUMA
> date: 9 months ago
> config: arm64-randconfig-r123-20260113 (https://download.01.org/0day-ci/archive/20260113/202601131901.j7WJ9OeZ-lkp@intel.com/config)
> compiler: clang version 16.0.6 (https://github.com/llvm/llvm-project 7cbf1a2591520c2491aa35339f227775f4d3adf6)
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260113/202601131901.j7WJ9OeZ-lkp@intel.com/reproduce)
…
>sparse warnings: (new ones prefixed by >>)
>>> kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression
>>> kernel/futex/core.c:505:51: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected unsigned int [noderef] [usertype] __user *naddr @@ got void * @@
> kernel/futex/core.c:505:51: sparse: expected unsigned int [noderef] [usertype] __user *naddr
> kernel/futex/core.c:505:51: sparse: got void *
> kernel/futex/core.c:894:9: sparse: sparse: context imbalance in 'futex_q_lockptr_lock' - wrong count at exit
…
> > 505 u32 __user *naddr = (void *)uaddr + size / 2;
[ ] I (PeterZ) have it fixed, will post soon
[ ] Fix it for me, I'm saving the world atm
[ ] Ignore it because of $reasonable_reason
Sebastian
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression
2026-01-13 11:59 ` Sebastian Andrzej Siewior
@ 2026-01-13 12:10 ` Peter Zijlstra
2026-01-13 17:37 ` Sebastian Andrzej Siewior
0 siblings, 1 reply; 11+ messages in thread
From: Peter Zijlstra @ 2026-01-13 12:10 UTC (permalink / raw)
To: Sebastian Andrzej Siewior; +Cc: kernel test robot, oe-kbuild-all, linux-kernel
On Tue, Jan 13, 2026 at 12:59:46PM +0100, Sebastian Andrzej Siewior wrote:
> On 2026-01-13 19:22:40 [+0800], kernel test robot wrote:
> > tree: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
> > head: b71e635feefc852405b14620a7fc58c4c80c0f73
> > commit: cec199c5e39bde7191a08087cc3d002ccfab31ff futex: Implement FUTEX2_NUMA
> > date: 9 months ago
> > config: arm64-randconfig-r123-20260113 (https://download.01.org/0day-ci/archive/20260113/202601131901.j7WJ9OeZ-lkp@intel.com/config)
> > compiler: clang version 16.0.6 (https://github.com/llvm/llvm-project 7cbf1a2591520c2491aa35339f227775f4d3adf6)
> > reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260113/202601131901.j7WJ9OeZ-lkp@intel.com/reproduce)
> …
> >sparse warnings: (new ones prefixed by >>)
> >>> kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression
> >>> kernel/futex/core.c:505:51: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected unsigned int [noderef] [usertype] __user *naddr @@ got void * @@
> > kernel/futex/core.c:505:51: sparse: expected unsigned int [noderef] [usertype] __user *naddr
> > kernel/futex/core.c:505:51: sparse: got void *
> > kernel/futex/core.c:894:9: sparse: sparse: context imbalance in 'futex_q_lockptr_lock' - wrong count at exit
> …
> > > 505 u32 __user *naddr = (void *)uaddr + size / 2;
>
> [ ] I (PeterZ) have it fixed, will post soon
> [ ] Fix it for me, I'm saving the world atm
> [ ] Ignore it because of $reasonable_reason
Lol :-)
Its here and a few lines down with the same thing I think. The cast is
to get byte pointer math, instead of u32 sized pointer math. Both the
original uaddr and naddr have the __user thing on, but that intermediate
cast trips it up.
Does this work?
---
kernel/futex/core.c | 6 +++---
kernel/futex/futex.h | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/kernel/futex/core.c b/kernel/futex/core.c
index cf7e610eac42..3961d256c79c 100644
--- a/kernel/futex/core.c
+++ b/kernel/futex/core.c
@@ -545,7 +545,7 @@ static u64 get_inode_sequence_number(struct inode *inode)
*
* lock_page() might sleep, the caller should not hold a spinlock.
*/
-int get_futex_key(u32 __user *uaddr, unsigned int flags, union futex_key *key,
+int get_futex_key(void __user *uaddr, unsigned int flags, union futex_key *key,
enum futex_access rw)
{
unsigned long address = (unsigned long)uaddr;
@@ -579,7 +579,7 @@ int get_futex_key(u32 __user *uaddr, unsigned int flags, union futex_key *key,
node = FUTEX_NO_NODE;
if (flags & FLAGS_NUMA) {
- u32 __user *naddr = (void *)uaddr + size / 2;
+ u32 __user *naddr = uaddr + size / 2;
if (get_user_inline(node, naddr))
return -EFAULT;
@@ -595,7 +595,7 @@ int get_futex_key(u32 __user *uaddr, unsigned int flags, union futex_key *key,
}
if (flags & FLAGS_NUMA) {
- u32 __user *naddr = (void *)uaddr + size / 2;
+ u32 __user *naddr = uaddr + size / 2;
if (node == FUTEX_NO_NODE) {
node = numa_node_id();
diff --git a/kernel/futex/futex.h b/kernel/futex/futex.h
index 30c2afa03889..99595742c994 100644
--- a/kernel/futex/futex.h
+++ b/kernel/futex/futex.h
@@ -215,7 +215,7 @@ enum futex_access {
FUTEX_WRITE
};
-extern int get_futex_key(u32 __user *uaddr, unsigned int flags, union futex_key *key,
+extern int get_futex_key(void __user *uaddr, unsigned int flags, union futex_key *key,
enum futex_access rw);
extern void futex_q_lockptr_lock(struct futex_q *q);
extern struct hrtimer_sleeper *
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression
2026-01-13 12:10 ` Peter Zijlstra
@ 2026-01-13 17:37 ` Sebastian Andrzej Siewior
2026-01-13 19:39 ` Peter Zijlstra
0 siblings, 1 reply; 11+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-01-13 17:37 UTC (permalink / raw)
To: Peter Zijlstra; +Cc: kernel test robot, oe-kbuild-all, linux-kernel
On 2026-01-13 13:10:40 [+0100], Peter Zijlstra wrote:
> Its here and a few lines down with the same thing I think. The cast is
> to get byte pointer math, instead of u32 sized pointer math. Both the
> original uaddr and naddr have the __user thing on, but that intermediate
> cast trips it up.
>
> Does this work?
Yes. It removes the __user thing warning which leaves us with
| kernel/futex/requeue.c:692:9: warning: context imbalance in 'futex_requeue' - different lock contexts for basic block
| kernel/futex/requeue.c:841:25: warning: context imbalance in 'futex_wait_requeue_pi' - unexpected unlock
| kernel/futex/pi.c:663:9: warning: context imbalance in 'wake_futex_pi' - unexpected unlock
| kernel/futex/pi.c:791:9: warning: context imbalance in '__fixup_pi_state_owner' - unexpected unlock
| kernel/futex/pi.c:1093:17: warning: context imbalance in 'futex_lock_pi' - unexpected unlock
| kernel/futex/pi.c:1132:5: warning: context imbalance in 'futex_unlock_pi' - different lock contexts for basic block
| kernel/futex/waitwake.c:275:41: warning: context imbalance in 'futex_wake_op' - different lock contexts for basic block
| kernel/futex/waitwake.c:460:44: warning: context imbalance in 'futex_wait_multiple_setup' - unexpected unlock
| kernel/futex/waitwake.c:660:28: warning: context imbalance in 'futex_wait_setup' - unexpected unlock
| kernel/futex/core.c:979:9: warning: context imbalance in 'futex_q_lockptr_lock' - wrong count at exit
With the following I get it down to:
| kernel/futex/waitwake.c:275:41: warning: context imbalance in 'futex_wake_op' - different lock contexts for basic block
| kernel/futex/requeue.c:692:9: warning: context imbalance in 'futex_requeue' - different lock contexts for basic block
double_lock_hb() + double_unlock_hb()
| kernel/futex/pi.c:792:9: warning: context imbalance in '__fixup_pi_state_owner' - unexpected unlock
raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock) + lock in a label.
which I give up on. I remember you said once that llvm will get support
for it so we can bury sparse but I don't remember if it was __user or
the lock lock annotation. Any all of these are "old".
diff --git a/kernel/futex/core.c b/kernel/futex/core.c
index 3961d256c79c4..6911d60fc5815 100644
--- a/kernel/futex/core.c
+++ b/kernel/futex/core.c
@@ -965,6 +965,7 @@ int futex_unqueue(struct futex_q *q)
}
void futex_q_lockptr_lock(struct futex_q *q)
+ __acquires(q->lock_ptr)
{
spinlock_t *lock_ptr;
diff --git a/kernel/futex/futex.h b/kernel/futex/futex.h
index 99595742c9941..1eba1a7a80efd 100644
--- a/kernel/futex/futex.h
+++ b/kernel/futex/futex.h
@@ -217,7 +217,7 @@ enum futex_access {
extern int get_futex_key(void __user *uaddr, unsigned int flags, union futex_key *key,
enum futex_access rw);
-extern void futex_q_lockptr_lock(struct futex_q *q);
+extern void futex_q_lockptr_lock(struct futex_q *q) __acquires(q->lock_ptr);
extern struct hrtimer_sleeper *
futex_setup_timer(ktime_t *time, struct hrtimer_sleeper *timeout,
int flags, u64 range_ns);
@@ -358,8 +358,8 @@ static inline int futex_hb_waiters_pending(struct futex_hash_bucket *hb)
#endif
}
-extern void futex_q_lock(struct futex_q *q, struct futex_hash_bucket *hb);
-extern void futex_q_unlock(struct futex_hash_bucket *hb);
+extern void futex_q_lock(struct futex_q *q, struct futex_hash_bucket *hb) __acquires(&hb->lock);
+extern void futex_q_unlock(struct futex_hash_bucket *hb) __releases(&hb->lock);
extern int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb,
diff --git a/kernel/futex/pi.c b/kernel/futex/pi.c
index dacb2330f1fbc..6a93a5eb5a0ab 100644
--- a/kernel/futex/pi.c
+++ b/kernel/futex/pi.c
@@ -614,6 +614,7 @@ int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb,
static int wake_futex_pi(u32 __user *uaddr, u32 uval,
struct futex_pi_state *pi_state,
struct rt_mutex_waiter *top_waiter)
+ __releases(q->lock_ptr)
{
struct task_struct *new_owner;
bool postunlock = false;
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression
2026-01-13 17:37 ` Sebastian Andrzej Siewior
@ 2026-01-13 19:39 ` Peter Zijlstra
2026-01-14 9:35 ` Sebastian Andrzej Siewior
0 siblings, 1 reply; 11+ messages in thread
From: Peter Zijlstra @ 2026-01-13 19:39 UTC (permalink / raw)
To: Sebastian Andrzej Siewior; +Cc: kernel test robot, oe-kbuild-all, linux-kernel
On Tue, Jan 13, 2026 at 06:37:08PM +0100, Sebastian Andrzej Siewior wrote:
> On 2026-01-13 13:10:40 [+0100], Peter Zijlstra wrote:
> > Its here and a few lines down with the same thing I think. The cast is
> > to get byte pointer math, instead of u32 sized pointer math. Both the
> > original uaddr and naddr have the __user thing on, but that intermediate
> > cast trips it up.
> >
> > Does this work?
>
> Yes. It removes the __user thing warning which leaves us with
> | kernel/futex/requeue.c:692:9: warning: context imbalance in 'futex_requeue' - different lock contexts for basic block
> | kernel/futex/requeue.c:841:25: warning: context imbalance in 'futex_wait_requeue_pi' - unexpected unlock
> | kernel/futex/pi.c:663:9: warning: context imbalance in 'wake_futex_pi' - unexpected unlock
> | kernel/futex/pi.c:791:9: warning: context imbalance in '__fixup_pi_state_owner' - unexpected unlock
> | kernel/futex/pi.c:1093:17: warning: context imbalance in 'futex_lock_pi' - unexpected unlock
> | kernel/futex/pi.c:1132:5: warning: context imbalance in 'futex_unlock_pi' - different lock contexts for basic block
> | kernel/futex/waitwake.c:275:41: warning: context imbalance in 'futex_wake_op' - different lock contexts for basic block
> | kernel/futex/waitwake.c:460:44: warning: context imbalance in 'futex_wait_multiple_setup' - unexpected unlock
> | kernel/futex/waitwake.c:660:28: warning: context imbalance in 'futex_wait_setup' - unexpected unlock
> | kernel/futex/core.c:979:9: warning: context imbalance in 'futex_q_lockptr_lock' - wrong count at exit
>
tip/locking/core removes all the sparse lock annotations in favour of
clang-22 tcsan.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression
2026-01-13 19:39 ` Peter Zijlstra
@ 2026-01-14 9:35 ` Sebastian Andrzej Siewior
2026-01-14 11:08 ` Peter Zijlstra
0 siblings, 1 reply; 11+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-01-14 9:35 UTC (permalink / raw)
To: Peter Zijlstra
Cc: kernel test robot, oe-kbuild-all, linux-kernel, Marco Elver
On 2026-01-13 20:39:19 [+0100], Peter Zijlstra wrote:
> tip/locking/core removes all the sparse lock annotations in favour of
> clang-22 tcsan.
So this is what salvation looks like? With
diff --git a/kernel/futex/core.c b/kernel/futex/core.c
index cf7e610eac429..b9e6be1c30179 100644
--- a/kernel/futex/core.c
+++ b/kernel/futex/core.c
@@ -965,6 +965,7 @@ int futex_unqueue(struct futex_q *q)
}
void futex_q_lockptr_lock(struct futex_q *q)
+ __acquires(q->lock_ptr)
{
spinlock_t *lock_ptr;
@@ -1443,12 +1444,15 @@ static void futex_cleanup(struct task_struct *tsk)
void futex_exit_recursive(struct task_struct *tsk)
{
/* If the state is FUTEX_STATE_EXITING then futex_exit_mutex is held */
- if (tsk->futex_state == FUTEX_STATE_EXITING)
+ if (tsk->futex_state == FUTEX_STATE_EXITING) {
+ lockdep_assert_held(&tsk->futex_exit_mutex);
mutex_unlock(&tsk->futex_exit_mutex);
+ }
tsk->futex_state = FUTEX_STATE_DEAD;
}
static void futex_cleanup_begin(struct task_struct *tsk)
+ __acquires(&tsk->futex_exit_mutex)
{
/*
* Prevent various race issues against a concurrent incoming waiter
@@ -1475,6 +1479,7 @@ static void futex_cleanup_begin(struct task_struct *tsk)
}
static void futex_cleanup_end(struct task_struct *tsk, int state)
+ __releases(&tsk->futex_exit_mutex)
{
/*
* Lockless store. The only side effect is that an observer might
diff --git a/kernel/futex/futex.h b/kernel/futex/futex.h
index 30c2afa038890..423989ffa5e91 100644
--- a/kernel/futex/futex.h
+++ b/kernel/futex/futex.h
@@ -379,6 +379,7 @@ extern int fixup_pi_owner(u32 __user *uaddr, struct futex_q *q, int locked);
*/
static inline void
double_lock_hb(struct futex_hash_bucket *hb1, struct futex_hash_bucket *hb2)
+ __cond_acquires(true, &hb2->lock)
{
if (hb1 > hb2)
swap(hb1, hb2);
@@ -391,9 +392,12 @@ double_lock_hb(struct futex_hash_bucket *hb1, struct futex_hash_bucket *hb2)
static inline void
double_unlock_hb(struct futex_hash_bucket *hb1, struct futex_hash_bucket *hb2)
{
+ lockdep_assert_held(&hb1->lock);
spin_unlock(&hb1->lock);
- if (hb1 != hb2)
+ if (hb1 != hb2) {
+ lockdep_assert_held(&hb2->lock);
spin_unlock(&hb2->lock);
+ }
}
/* syscalls */
diff --git a/kernel/futex/pi.c b/kernel/futex/pi.c
index dacb2330f1fbc..e45ad40b59550 100644
--- a/kernel/futex/pi.c
+++ b/kernel/futex/pi.c
@@ -621,6 +621,7 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval,
u32 curval, newval;
int ret = 0;
+ lockdep_assert_held(&pi_state->pi_mutex.wait_lock);
new_owner = top_waiter->task;
/*
I managed to pass core.c But then started looking at pi.c I run into
this:
| kernel/futex/pi.c:706:7: error: expecting raw_spinlock 'q->pi_state->pi_mutex.wait_lock' to be held at start of each loop
| [-Werror,-Wthread-safety-analysis]
| 706 | if (!argowner) {
| | ^
| kernel/futex/pi.c:811:2: note: raw_spinlock acquired here
| 811 | raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock);
| | ^
| include/linux/spinlock.h:275:34: note: expanded from macro 'raw_spin_lock_irq'
| 275 | #define raw_spin_lock_irq(lock) _raw_spin_lock_irq(lock)
| | ^
| kernel/futex/pi.c:792:2: error: releasing raw_spinlock 'q->pi_state->pi_mutex.wait_lock' that was not held
| [-Werror,-Wthread-safety-analysis]
| 792 | raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
it can be told from the context that waitlock is held at start of each
loop. It is just that unlock+lock combo after handle_err: that breaks
llvm.
cond_acquires() works and lockdep_assert_held() is taken into account
which is an improvement over sparse. For futex_cleanup_begin()/_end() it
seems to lose the context for futex_exit_mutex but this is all local…
Sebastian
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression
2026-01-14 9:35 ` Sebastian Andrzej Siewior
@ 2026-01-14 11:08 ` Peter Zijlstra
2026-01-14 12:09 ` Sebastian Andrzej Siewior
0 siblings, 1 reply; 11+ messages in thread
From: Peter Zijlstra @ 2026-01-14 11:08 UTC (permalink / raw)
To: Sebastian Andrzej Siewior
Cc: kernel test robot, oe-kbuild-all, linux-kernel, Marco Elver
On Wed, Jan 14, 2026 at 10:35:17AM +0100, Sebastian Andrzej Siewior wrote:
> On 2026-01-13 20:39:19 [+0100], Peter Zijlstra wrote:
> > tip/locking/core removes all the sparse lock annotations in favour of
> > clang-22 tcsan.
>
> So this is what salvation looks like?
:-)
This seems to build for me. It is the bare basic conversion, without
making use of the fancy __guarded_by() stuff for variables.
---
diff --git a/kernel/futex/Makefile b/kernel/futex/Makefile
index b77188d1fa07..9242110a32e2 100644
--- a/kernel/futex/Makefile
+++ b/kernel/futex/Makefile
@@ -1,3 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
+CONTEXT_ANALYSIS_core.o := y
+CONTEXT_ANALYSIS_syscalls.o := y
+CONTEXT_ANALYSIS_pi.o := y
+CONTEXT_ANALYSIS_requeue.o := y
+CONTEXT_ANALYSIS_waitwake.o := y
+
obj-y += core.o syscalls.o pi.o requeue.o waitwake.o
diff --git a/kernel/futex/core.c b/kernel/futex/core.c
index cf7e610eac42..36fe2add982b 100644
--- a/kernel/futex/core.c
+++ b/kernel/futex/core.c
@@ -864,7 +864,6 @@ void __futex_unqueue(struct futex_q *q)
/* The key must be already stored in q->key. */
void futex_q_lock(struct futex_q *q, struct futex_hash_bucket *hb)
- __acquires(&hb->lock)
{
/*
* Increment the counter before taking the lock so that
@@ -879,10 +878,10 @@ void futex_q_lock(struct futex_q *q, struct futex_hash_bucket *hb)
q->lock_ptr = &hb->lock;
spin_lock(&hb->lock);
+ __acquire_ctx_lock(q->lock_ptr);
}
void futex_q_unlock(struct futex_hash_bucket *hb)
- __releases(&hb->lock)
{
futex_hb_waiters_dec(hb);
spin_unlock(&hb->lock);
@@ -1443,12 +1442,15 @@ static void futex_cleanup(struct task_struct *tsk)
void futex_exit_recursive(struct task_struct *tsk)
{
/* If the state is FUTEX_STATE_EXITING then futex_exit_mutex is held */
- if (tsk->futex_state == FUTEX_STATE_EXITING)
+ if (tsk->futex_state == FUTEX_STATE_EXITING) {
+ lockdep_assert_held(&tsk->futex_exit_mutex);
mutex_unlock(&tsk->futex_exit_mutex);
+ }
tsk->futex_state = FUTEX_STATE_DEAD;
}
static void futex_cleanup_begin(struct task_struct *tsk)
+ __acquires(&tsk->futex_exit_mutex)
{
/*
* Prevent various race issues against a concurrent incoming waiter
@@ -1475,6 +1477,7 @@ static void futex_cleanup_begin(struct task_struct *tsk)
}
static void futex_cleanup_end(struct task_struct *tsk, int state)
+ __releases(&tsk->futex_exit_mutex)
{
/*
* Lockless store. The only side effect is that an observer might
diff --git a/kernel/futex/futex.h b/kernel/futex/futex.h
index 30c2afa03889..7f0944683e43 100644
--- a/kernel/futex/futex.h
+++ b/kernel/futex/futex.h
@@ -217,7 +217,7 @@ enum futex_access {
extern int get_futex_key(u32 __user *uaddr, unsigned int flags, union futex_key *key,
enum futex_access rw);
-extern void futex_q_lockptr_lock(struct futex_q *q);
+extern void futex_q_lockptr_lock(struct futex_q *q) __acquires(q->lock_ptr);
extern struct hrtimer_sleeper *
futex_setup_timer(ktime_t *time, struct hrtimer_sleeper *timeout,
int flags, u64 range_ns);
@@ -311,9 +311,11 @@ extern int futex_unqueue(struct futex_q *q);
static inline void futex_queue(struct futex_q *q, struct futex_hash_bucket *hb,
struct task_struct *task)
__releases(&hb->lock)
+ __releases(q->lock_ptr)
{
__futex_queue(q, hb, task);
spin_unlock(&hb->lock);
+ __release_ctx_lock(q->lock_ptr);
}
extern void futex_unqueue_pi(struct futex_q *q);
@@ -358,9 +360,12 @@ static inline int futex_hb_waiters_pending(struct futex_hash_bucket *hb)
#endif
}
-extern void futex_q_lock(struct futex_q *q, struct futex_hash_bucket *hb);
-extern void futex_q_unlock(struct futex_hash_bucket *hb);
+extern void futex_q_lock(struct futex_q *q, struct futex_hash_bucket *hb)
+ __acquires(&hb->lock)
+ __acquires(q->lock_ptr);
+extern void futex_q_unlock(struct futex_hash_bucket *hb)
+ __releases(&hb->lock);
extern int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb,
union futex_key *key,
@@ -379,6 +384,9 @@ extern int fixup_pi_owner(u32 __user *uaddr, struct futex_q *q, int locked);
*/
static inline void
double_lock_hb(struct futex_hash_bucket *hb1, struct futex_hash_bucket *hb2)
+ __acquires(&hb1->lock)
+ __acquires(&hb2->lock)
+ __no_context_analysis
{
if (hb1 > hb2)
swap(hb1, hb2);
@@ -390,10 +398,16 @@ double_lock_hb(struct futex_hash_bucket *hb1, struct futex_hash_bucket *hb2)
static inline void
double_unlock_hb(struct futex_hash_bucket *hb1, struct futex_hash_bucket *hb2)
+ __releases(&hb1->lock)
+ __releases(&hb2->lock)
+ __no_context_analysis
{
+ lockdep_assert_held(&hb1->lock);
spin_unlock(&hb1->lock);
- if (hb1 != hb2)
+ if (hb1 != hb2) {
+ lockdep_assert_held(&hb2->lock);
spin_unlock(&hb2->lock);
+ }
}
/* syscalls */
diff --git a/kernel/futex/pi.c b/kernel/futex/pi.c
index dacb2330f1fb..80b3b92dadf4 100644
--- a/kernel/futex/pi.c
+++ b/kernel/futex/pi.c
@@ -614,6 +614,8 @@ int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb,
static int wake_futex_pi(u32 __user *uaddr, u32 uval,
struct futex_pi_state *pi_state,
struct rt_mutex_waiter *top_waiter)
+ __must_hold(&pi_state->pi_mutex.wait_lock)
+ __releases(&pi_state->pi_mutex.wait_lock)
{
struct task_struct *new_owner;
bool postunlock = false;
@@ -670,6 +672,8 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval,
static int __fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
struct task_struct *argowner)
+ __must_hold(&q->pi_state->pi_mutex.wait_lock)
+ __must_hold(q->lock_ptr)
{
struct futex_pi_state *pi_state = q->pi_state;
struct task_struct *oldowner, *newowner;
@@ -966,6 +970,7 @@ int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int tryl
* - EAGAIN: The user space value changed.
*/
futex_q_unlock(hb);
+ __release_ctx_lock(q.lock_ptr);
/*
* Handle the case where the owner is in the middle of
* exiting. Wait for the exit to complete otherwise
@@ -1090,6 +1095,7 @@ int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int tryl
if (res)
ret = (res < 0) ? res : 0;
+ __release_ctx_lock(&hb->lock);
futex_unqueue_pi(&q);
spin_unlock(q.lock_ptr);
if (q.drop_hb_ref) {
@@ -1101,10 +1107,12 @@ int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int tryl
out_unlock_put_key:
futex_q_unlock(hb);
+ __release_ctx_lock(q.lock_ptr);
goto out;
uaddr_faulted:
futex_q_unlock(hb);
+ __release_ctx_lock(q.lock_ptr);
ret = fault_in_user_writeable(uaddr);
if (ret)
diff --git a/kernel/futex/waitwake.c b/kernel/futex/waitwake.c
index 1c2dd03f11ec..09b636cbc3d0 100644
--- a/kernel/futex/waitwake.c
+++ b/kernel/futex/waitwake.c
@@ -462,6 +462,7 @@ int futex_wait_multiple_setup(struct futex_vector *vs, int count, int *woken)
}
futex_q_unlock(hb);
+ __release_ctx_lock(q->lock_ptr);
}
__set_current_state(TASK_RUNNING);
@@ -628,6 +629,7 @@ int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
if (ret) {
futex_q_unlock(hb);
+ __release_ctx_lock(q->lock_ptr);
ret = get_user(uval, uaddr);
if (ret)
@@ -641,11 +643,13 @@ int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
if (uval != val) {
futex_q_unlock(hb);
+ __release_ctx_lock(q->lock_ptr);
return -EWOULDBLOCK;
}
if (key2 && futex_match(&q->key, key2)) {
futex_q_unlock(hb);
+ __release_ctx_lock(q->lock_ptr);
return -EINVAL;
}
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression
2026-01-14 11:08 ` Peter Zijlstra
@ 2026-01-14 12:09 ` Sebastian Andrzej Siewior
2026-01-14 12:16 ` Peter Zijlstra
0 siblings, 1 reply; 11+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-01-14 12:09 UTC (permalink / raw)
To: Peter Zijlstra
Cc: kernel test robot, oe-kbuild-all, linux-kernel, Marco Elver
On 2026-01-14 12:08:28 [+0100], Peter Zijlstra wrote:
> On Wed, Jan 14, 2026 at 10:35:17AM +0100, Sebastian Andrzej Siewior wrote:
> > On 2026-01-13 20:39:19 [+0100], Peter Zijlstra wrote:
> > > tip/locking/core removes all the sparse lock annotations in favour of
> > > clang-22 tcsan.
> >
> > So this is what salvation looks like?
>
> :-)
>
> This seems to build for me. It is the bare basic conversion, without
> making use of the fancy __guarded_by() stuff for variables.
>
> ---
> diff --git a/kernel/futex/Makefile b/kernel/futex/Makefile
> index b77188d1fa07..9242110a32e2 100644
> --- a/kernel/futex/Makefile
> +++ b/kernel/futex/Makefile
> @@ -1,3 +1,9 @@
> # SPDX-License-Identifier: GPL-2.0
>
> +CONTEXT_ANALYSIS_core.o := y
> +CONTEXT_ANALYSIS_syscalls.o := y
> +CONTEXT_ANALYSIS_pi.o := y
> +CONTEXT_ANALYSIS_requeue.o := y
> +CONTEXT_ANALYSIS_waitwake.o := y
You could just do 'CONTEXT_ANALYSIS := y' since everything else works,
too. Can confirm.
…
> --- a/kernel/futex/core.c
> +++ b/kernel/futex/core.c
> @@ -879,10 +878,10 @@ void futex_q_lock(struct futex_q *q, struct futex_hash_bucket *hb)
> q->lock_ptr = &hb->lock;
>
> spin_lock(&hb->lock);
> + __acquire_ctx_lock(q->lock_ptr);
so it sees hb->lock and we fake q->lock_ptr. Okay.
> static void futex_cleanup_begin(struct task_struct *tsk)
> + __acquires(&tsk->futex_exit_mutex)
so this is needed even if it sees the whole context.
> --- a/kernel/futex/futex.h
> +++ b/kernel/futex/futex.h
> @@ -217,7 +217,7 @@ enum futex_access {
>
> extern int get_futex_key(u32 __user *uaddr, unsigned int flags, union futex_key *key,
> enum futex_access rw);
> -extern void futex_q_lockptr_lock(struct futex_q *q);
> +extern void futex_q_lockptr_lock(struct futex_q *q) __acquires(q->lock_ptr);
> extern struct hrtimer_sleeper *
> futex_setup_timer(ktime_t *time, struct hrtimer_sleeper *timeout,
> int flags, u64 range_ns);
> @@ -311,9 +311,11 @@ extern int futex_unqueue(struct futex_q *q);
> static inline void futex_queue(struct futex_q *q, struct futex_hash_bucket *hb,
> struct task_struct *task)
> __releases(&hb->lock)
> + __releases(q->lock_ptr)
> {
> __futex_queue(q, hb, task);
> spin_unlock(&hb->lock);
> + __release_ctx_lock(q->lock_ptr);
so we need both. I then misunderstood the concept.
The function attribute is for other functions to learn and
__release_ctx_lock() for the current context?
> }
>
> @@ -379,6 +384,9 @@ extern int fixup_pi_owner(u32 __user *uaddr, struct futex_q *q, int locked);
> */
> static inline void
> double_lock_hb(struct futex_hash_bucket *hb1, struct futex_hash_bucket *hb2)
> + __acquires(&hb1->lock)
> + __acquires(&hb2->lock)
> + __no_context_analysis
so I was proud of my conditional thingy and the lockdep part but you use
whatever is there ;)
It certainly makes sense since logically both locks are acquired but is
kind of hard to explain.
> --- a/kernel/futex/pi.c
> +++ b/kernel/futex/pi.c
> @@ -614,6 +614,8 @@ int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb,
> static int wake_futex_pi(u32 __user *uaddr, u32 uval,
> struct futex_pi_state *pi_state,
> struct rt_mutex_waiter *top_waiter)
> + __must_hold(&pi_state->pi_mutex.wait_lock)
> + __releases(&pi_state->pi_mutex.wait_lock)
So need to tell that wait_lock must be held by the caller and it will be
released because that latter is not expected even if the flow would
match it.
> {
> struct task_struct *new_owner;
> bool postunlock = false;
> @@ -670,6 +672,8 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval,
>
> static int __fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
> struct task_struct *argowner)
> + __must_hold(&q->pi_state->pi_mutex.wait_lock)
> + __must_hold(q->lock_ptr)
So that was the trick and llvm does not need to deal with unlock+lock
even if it is obvious from the flow.
Okay thank you. I go grab some food and then cry then a bit in the corner…
Sebastian
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression
2026-01-14 12:09 ` Sebastian Andrzej Siewior
@ 2026-01-14 12:16 ` Peter Zijlstra
0 siblings, 0 replies; 11+ messages in thread
From: Peter Zijlstra @ 2026-01-14 12:16 UTC (permalink / raw)
To: Sebastian Andrzej Siewior
Cc: kernel test robot, oe-kbuild-all, linux-kernel, Marco Elver
On Wed, Jan 14, 2026 at 01:09:23PM +0100, Sebastian Andrzej Siewior wrote:
> On 2026-01-14 12:08:28 [+0100], Peter Zijlstra wrote:
> > On Wed, Jan 14, 2026 at 10:35:17AM +0100, Sebastian Andrzej Siewior wrote:
> > > On 2026-01-13 20:39:19 [+0100], Peter Zijlstra wrote:
> > > > tip/locking/core removes all the sparse lock annotations in favour of
> > > > clang-22 tcsan.
> > >
> > > So this is what salvation looks like?
> >
> > :-)
> >
> > This seems to build for me. It is the bare basic conversion, without
> > making use of the fancy __guarded_by() stuff for variables.
> >
> > ---
> > diff --git a/kernel/futex/Makefile b/kernel/futex/Makefile
> > index b77188d1fa07..9242110a32e2 100644
> > --- a/kernel/futex/Makefile
> > +++ b/kernel/futex/Makefile
> > @@ -1,3 +1,9 @@
> > # SPDX-License-Identifier: GPL-2.0
> >
> > +CONTEXT_ANALYSIS_core.o := y
> > +CONTEXT_ANALYSIS_syscalls.o := y
> > +CONTEXT_ANALYSIS_pi.o := y
> > +CONTEXT_ANALYSIS_requeue.o := y
> > +CONTEXT_ANALYSIS_waitwake.o := y
>
> You could just do 'CONTEXT_ANALYSIS := y' since everything else works,
> too. Can confirm.
Ah indeed. Clearly I copy/pasted the wrong example.
> …
> > --- a/kernel/futex/core.c
> > +++ b/kernel/futex/core.c
> > @@ -879,10 +878,10 @@ void futex_q_lock(struct futex_q *q, struct futex_hash_bucket *hb)
> > q->lock_ptr = &hb->lock;
> >
> > spin_lock(&hb->lock);
> > + __acquire_ctx_lock(q->lock_ptr);
>
> so it sees hb->lock and we fake q->lock_ptr. Okay.
Yeah, the thing doesn't understand q->lock_ptr is an alias of hb->lock
and gets totally confused.
> > static void futex_cleanup_begin(struct task_struct *tsk)
> > + __acquires(&tsk->futex_exit_mutex)
>
> so this is needed even if it sees the whole context.
Yeah, no inter-procedural analysis.
> > --- a/kernel/futex/futex.h
> > +++ b/kernel/futex/futex.h
> > @@ -217,7 +217,7 @@ enum futex_access {
> >
> > extern int get_futex_key(u32 __user *uaddr, unsigned int flags, union futex_key *key,
> > enum futex_access rw);
> > -extern void futex_q_lockptr_lock(struct futex_q *q);
> > +extern void futex_q_lockptr_lock(struct futex_q *q) __acquires(q->lock_ptr);
> > extern struct hrtimer_sleeper *
> > futex_setup_timer(ktime_t *time, struct hrtimer_sleeper *timeout,
> > int flags, u64 range_ns);
> > @@ -311,9 +311,11 @@ extern int futex_unqueue(struct futex_q *q);
> > static inline void futex_queue(struct futex_q *q, struct futex_hash_bucket *hb,
> > struct task_struct *task)
> > __releases(&hb->lock)
> > + __releases(q->lock_ptr)
> > {
> > __futex_queue(q, hb, task);
> > spin_unlock(&hb->lock);
> > + __release_ctx_lock(q->lock_ptr);
>
> so we need both. I then misunderstood the concept.
> The function attribute is for other functions to learn and
> __release_ctx_lock() for the current context?
The __releases() attribute is for others but also verified against self
as a post-condition. And then yes, __release_ctx_lock() makes it true
for self.
> > }
> >
> > @@ -379,6 +384,9 @@ extern int fixup_pi_owner(u32 __user *uaddr, struct futex_q *q, int locked);
> > */
> > static inline void
> > double_lock_hb(struct futex_hash_bucket *hb1, struct futex_hash_bucket *hb2)
> > + __acquires(&hb1->lock)
> > + __acquires(&hb2->lock)
> > + __no_context_analysis
>
> so I was proud of my conditional thingy and the lockdep part but you use
> whatever is there ;)
> It certainly makes sense since logically both locks are acquired but is
> kind of hard to explain.
Right. I went back and forth a bit, but I figured this one was
conceptually cleaner.
> > --- a/kernel/futex/pi.c
> > +++ b/kernel/futex/pi.c
> > @@ -614,6 +614,8 @@ int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb,
> > static int wake_futex_pi(u32 __user *uaddr, u32 uval,
> > struct futex_pi_state *pi_state,
> > struct rt_mutex_waiter *top_waiter)
> > + __must_hold(&pi_state->pi_mutex.wait_lock)
> > + __releases(&pi_state->pi_mutex.wait_lock)
>
> So need to tell that wait_lock must be held by the caller and it will be
> released because that latter is not expected even if the flow would
> match it.
>
> > {
> > struct task_struct *new_owner;
> > bool postunlock = false;
> > @@ -670,6 +672,8 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval,
> >
> > static int __fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
> > struct task_struct *argowner)
> > + __must_hold(&q->pi_state->pi_mutex.wait_lock)
> > + __must_hold(q->lock_ptr)
>
> So that was the trick and llvm does not need to deal with unlock+lock
> even if it is obvious from the flow.
Right, so by stating the lock is held on entry and exit, the unlock+lock
becomes a valid pattern and is no longer complained about.
> Okay thank you. I go grab some food and then cry then a bit in the corner…
:-)
^ permalink raw reply [flat|nested] 11+ messages in thread
* kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression
@ 2026-02-15 22:01 kernel test robot
0 siblings, 0 replies; 11+ messages in thread
From: kernel test robot @ 2026-02-15 22:01 UTC (permalink / raw)
To: Peter Zijlstra; +Cc: oe-kbuild-all, linux-kernel, Sebastian Andrzej Siewior
tree: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
head: 26a4cfaff82a2dcb810f6bfd5f4842f9b6046c8a
commit: cec199c5e39bde7191a08087cc3d002ccfab31ff futex: Implement FUTEX2_NUMA
date: 10 months ago
config: i386-randconfig-063-20260215 (https://download.01.org/0day-ci/archive/20260216/202602160508.5lWZQNiX-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260216/202602160508.5lWZQNiX-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602160508.5lWZQNiX-lkp@intel.com/
sparse warnings: (new ones prefixed by >>)
>> kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression
kernel/futex/core.c:505:51: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected unsigned int [noderef] [usertype] __user *naddr @@ got void * @@
kernel/futex/core.c:505:51: sparse: expected unsigned int [noderef] [usertype] __user *naddr
kernel/futex/core.c:505:51: sparse: got void *
kernel/futex/core.c:894:9: sparse: sparse: context imbalance in 'futex_q_lockptr_lock' - wrong count at exit
vim +/__user +505 kernel/futex/core.c
446
447 /**
448 * get_futex_key() - Get parameters which are the keys for a futex
449 * @uaddr: virtual address of the futex
450 * @flags: FLAGS_*
451 * @key: address where result is stored.
452 * @rw: mapping needs to be read/write (values: FUTEX_READ,
453 * FUTEX_WRITE)
454 *
455 * Return: a negative error code or 0
456 *
457 * The key words are stored in @key on success.
458 *
459 * For shared mappings (when @fshared), the key is:
460 *
461 * ( inode->i_sequence, page->index, offset_within_page )
462 *
463 * [ also see get_inode_sequence_number() ]
464 *
465 * For private mappings (or when !@fshared), the key is:
466 *
467 * ( current->mm, address, 0 )
468 *
469 * This allows (cross process, where applicable) identification of the futex
470 * without keeping the page pinned for the duration of the FUTEX_WAIT.
471 *
472 * lock_page() might sleep, the caller should not hold a spinlock.
473 */
474 int get_futex_key(u32 __user *uaddr, unsigned int flags, union futex_key *key,
475 enum futex_access rw)
476 {
477 unsigned long address = (unsigned long)uaddr;
478 struct mm_struct *mm = current->mm;
479 struct page *page;
480 struct folio *folio;
481 struct address_space *mapping;
482 int node, err, size, ro = 0;
483 bool fshared;
484
485 fshared = flags & FLAGS_SHARED;
486 size = futex_size(flags);
487 if (flags & FLAGS_NUMA)
488 size *= 2;
489
490 /*
491 * The futex address must be "naturally" aligned.
492 */
493 key->both.offset = address % PAGE_SIZE;
494 if (unlikely((address % size) != 0))
495 return -EINVAL;
496 address -= key->both.offset;
497
498 if (unlikely(!access_ok(uaddr, size)))
499 return -EFAULT;
500
501 if (unlikely(should_fail_futex(fshared)))
502 return -EFAULT;
503
504 if (flags & FLAGS_NUMA) {
> 505 u32 __user *naddr = (void *)uaddr + size / 2;
506
507 if (futex_get_value(&node, naddr))
508 return -EFAULT;
509
510 if (node == FUTEX_NO_NODE) {
511 node = numa_node_id();
512 if (futex_put_value(node, naddr))
513 return -EFAULT;
514
515 } else if (node >= MAX_NUMNODES || !node_possible(node)) {
516 return -EINVAL;
517 }
518
519 key->both.node = node;
520
521 } else {
522 key->both.node = FUTEX_NO_NODE;
523 }
524
525 /*
526 * PROCESS_PRIVATE futexes are fast.
527 * As the mm cannot disappear under us and the 'key' only needs
528 * virtual address, we dont even have to find the underlying vma.
529 * Note : We do have to check 'uaddr' is a valid user address,
530 * but access_ok() should be faster than find_vma()
531 */
532 if (!fshared) {
533 /*
534 * On no-MMU, shared futexes are treated as private, therefore
535 * we must not include the current process in the key. Since
536 * there is only one address space, the address is a unique key
537 * on its own.
538 */
539 if (IS_ENABLED(CONFIG_MMU))
540 key->private.mm = mm;
541 else
542 key->private.mm = NULL;
543
544 key->private.address = address;
545 return 0;
546 }
547
548 again:
549 /* Ignore any VERIFY_READ mapping (futex common case) */
550 if (unlikely(should_fail_futex(true)))
551 return -EFAULT;
552
553 err = get_user_pages_fast(address, 1, FOLL_WRITE, &page);
554 /*
555 * If write access is not required (eg. FUTEX_WAIT), try
556 * and get read-only access.
557 */
558 if (err == -EFAULT && rw == FUTEX_READ) {
559 err = get_user_pages_fast(address, 1, 0, &page);
560 ro = 1;
561 }
562 if (err < 0)
563 return err;
564 else
565 err = 0;
566
567 /*
568 * The treatment of mapping from this point on is critical. The folio
569 * lock protects many things but in this context the folio lock
570 * stabilizes mapping, prevents inode freeing in the shared
571 * file-backed region case and guards against movement to swap cache.
572 *
573 * Strictly speaking the folio lock is not needed in all cases being
574 * considered here and folio lock forces unnecessarily serialization.
575 * From this point on, mapping will be re-verified if necessary and
576 * folio lock will be acquired only if it is unavoidable
577 *
578 * Mapping checks require the folio so it is looked up now. For
579 * anonymous pages, it does not matter if the folio is split
580 * in the future as the key is based on the address. For
581 * filesystem-backed pages, the precise page is required as the
582 * index of the page determines the key.
583 */
584 folio = page_folio(page);
585 mapping = READ_ONCE(folio->mapping);
586
587 /*
588 * If folio->mapping is NULL, then it cannot be an anonymous
589 * page; but it might be the ZERO_PAGE or in the gate area or
590 * in a special mapping (all cases which we are happy to fail);
591 * or it may have been a good file page when get_user_pages_fast
592 * found it, but truncated or holepunched or subjected to
593 * invalidate_complete_page2 before we got the folio lock (also
594 * cases which we are happy to fail). And we hold a reference,
595 * so refcount care in invalidate_inode_page's remove_mapping
596 * prevents drop_caches from setting mapping to NULL beneath us.
597 *
598 * The case we do have to guard against is when memory pressure made
599 * shmem_writepage move it from filecache to swapcache beneath us:
600 * an unlikely race, but we do need to retry for folio->mapping.
601 */
602 if (unlikely(!mapping)) {
603 int shmem_swizzled;
604
605 /*
606 * Folio lock is required to identify which special case above
607 * applies. If this is really a shmem page then the folio lock
608 * will prevent unexpected transitions.
609 */
610 folio_lock(folio);
611 shmem_swizzled = folio_test_swapcache(folio) || folio->mapping;
612 folio_unlock(folio);
613 folio_put(folio);
614
615 if (shmem_swizzled)
616 goto again;
617
618 return -EFAULT;
619 }
620
621 /*
622 * Private mappings are handled in a simple way.
623 *
624 * If the futex key is stored in anonymous memory, then the associated
625 * object is the mm which is implicitly pinned by the calling process.
626 *
627 * NOTE: When userspace waits on a MAP_SHARED mapping, even if
628 * it's a read-only handle, it's expected that futexes attach to
629 * the object not the particular process.
630 */
631 if (folio_test_anon(folio)) {
632 /*
633 * A RO anonymous page will never change and thus doesn't make
634 * sense for futex operations.
635 */
636 if (unlikely(should_fail_futex(true)) || ro) {
637 err = -EFAULT;
638 goto out;
639 }
640
641 key->both.offset |= FUT_OFF_MMSHARED; /* ref taken on mm */
642 key->private.mm = mm;
643 key->private.address = address;
644
645 } else {
646 struct inode *inode;
647
648 /*
649 * The associated futex object in this case is the inode and
650 * the folio->mapping must be traversed. Ordinarily this should
651 * be stabilised under folio lock but it's not strictly
652 * necessary in this case as we just want to pin the inode, not
653 * update i_pages or anything like that.
654 *
655 * The RCU read lock is taken as the inode is finally freed
656 * under RCU. If the mapping still matches expectations then the
657 * mapping->host can be safely accessed as being a valid inode.
658 */
659 rcu_read_lock();
660
661 if (READ_ONCE(folio->mapping) != mapping) {
662 rcu_read_unlock();
663 folio_put(folio);
664
665 goto again;
666 }
667
668 inode = READ_ONCE(mapping->host);
669 if (!inode) {
670 rcu_read_unlock();
671 folio_put(folio);
672
673 goto again;
674 }
675
676 key->both.offset |= FUT_OFF_INODE; /* inode-based key */
677 key->shared.i_seq = get_inode_sequence_number(inode);
678 key->shared.pgoff = page_pgoff(folio, page);
679 rcu_read_unlock();
680 }
681
682 out:
683 folio_put(folio);
684 return err;
685 }
686
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 11+ messages in thread* kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression
@ 2025-12-11 8:07 kernel test robot
0 siblings, 0 replies; 11+ messages in thread
From: kernel test robot @ 2025-12-11 8:07 UTC (permalink / raw)
To: Peter Zijlstra; +Cc: oe-kbuild-all, linux-kernel, Sebastian Andrzej Siewior
tree: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
head: d358e5254674b70f34c847715ca509e46eb81e6f
commit: cec199c5e39bde7191a08087cc3d002ccfab31ff futex: Implement FUTEX2_NUMA
date: 7 months ago
config: x86_64-randconfig-121-20251211 (https://download.01.org/0day-ci/archive/20251211/202512111555.3X7kd9T0-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251211/202512111555.3X7kd9T0-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202512111555.3X7kd9T0-lkp@intel.com/
sparse warnings: (new ones prefixed by >>)
>> kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression
kernel/futex/core.c:505:51: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected unsigned int [noderef] [usertype] __user *naddr @@ got void * @@
kernel/futex/core.c:505:51: sparse: expected unsigned int [noderef] [usertype] __user *naddr
kernel/futex/core.c:505:51: sparse: got void *
kernel/futex/core.c:894:9: sparse: sparse: context imbalance in 'futex_q_lockptr_lock' - wrong count at exit
vim +/__user +505 kernel/futex/core.c
446
447 /**
448 * get_futex_key() - Get parameters which are the keys for a futex
449 * @uaddr: virtual address of the futex
450 * @flags: FLAGS_*
451 * @key: address where result is stored.
452 * @rw: mapping needs to be read/write (values: FUTEX_READ,
453 * FUTEX_WRITE)
454 *
455 * Return: a negative error code or 0
456 *
457 * The key words are stored in @key on success.
458 *
459 * For shared mappings (when @fshared), the key is:
460 *
461 * ( inode->i_sequence, page->index, offset_within_page )
462 *
463 * [ also see get_inode_sequence_number() ]
464 *
465 * For private mappings (or when !@fshared), the key is:
466 *
467 * ( current->mm, address, 0 )
468 *
469 * This allows (cross process, where applicable) identification of the futex
470 * without keeping the page pinned for the duration of the FUTEX_WAIT.
471 *
472 * lock_page() might sleep, the caller should not hold a spinlock.
473 */
474 int get_futex_key(u32 __user *uaddr, unsigned int flags, union futex_key *key,
475 enum futex_access rw)
476 {
477 unsigned long address = (unsigned long)uaddr;
478 struct mm_struct *mm = current->mm;
479 struct page *page;
480 struct folio *folio;
481 struct address_space *mapping;
482 int node, err, size, ro = 0;
483 bool fshared;
484
485 fshared = flags & FLAGS_SHARED;
486 size = futex_size(flags);
487 if (flags & FLAGS_NUMA)
488 size *= 2;
489
490 /*
491 * The futex address must be "naturally" aligned.
492 */
493 key->both.offset = address % PAGE_SIZE;
494 if (unlikely((address % size) != 0))
495 return -EINVAL;
496 address -= key->both.offset;
497
498 if (unlikely(!access_ok(uaddr, size)))
499 return -EFAULT;
500
501 if (unlikely(should_fail_futex(fshared)))
502 return -EFAULT;
503
504 if (flags & FLAGS_NUMA) {
> 505 u32 __user *naddr = (void *)uaddr + size / 2;
506
507 if (futex_get_value(&node, naddr))
508 return -EFAULT;
509
510 if (node == FUTEX_NO_NODE) {
511 node = numa_node_id();
512 if (futex_put_value(node, naddr))
513 return -EFAULT;
514
515 } else if (node >= MAX_NUMNODES || !node_possible(node)) {
516 return -EINVAL;
517 }
518
519 key->both.node = node;
520
521 } else {
522 key->both.node = FUTEX_NO_NODE;
523 }
524
525 /*
526 * PROCESS_PRIVATE futexes are fast.
527 * As the mm cannot disappear under us and the 'key' only needs
528 * virtual address, we dont even have to find the underlying vma.
529 * Note : We do have to check 'uaddr' is a valid user address,
530 * but access_ok() should be faster than find_vma()
531 */
532 if (!fshared) {
533 /*
534 * On no-MMU, shared futexes are treated as private, therefore
535 * we must not include the current process in the key. Since
536 * there is only one address space, the address is a unique key
537 * on its own.
538 */
539 if (IS_ENABLED(CONFIG_MMU))
540 key->private.mm = mm;
541 else
542 key->private.mm = NULL;
543
544 key->private.address = address;
545 return 0;
546 }
547
548 again:
549 /* Ignore any VERIFY_READ mapping (futex common case) */
550 if (unlikely(should_fail_futex(true)))
551 return -EFAULT;
552
553 err = get_user_pages_fast(address, 1, FOLL_WRITE, &page);
554 /*
555 * If write access is not required (eg. FUTEX_WAIT), try
556 * and get read-only access.
557 */
558 if (err == -EFAULT && rw == FUTEX_READ) {
559 err = get_user_pages_fast(address, 1, 0, &page);
560 ro = 1;
561 }
562 if (err < 0)
563 return err;
564 else
565 err = 0;
566
567 /*
568 * The treatment of mapping from this point on is critical. The folio
569 * lock protects many things but in this context the folio lock
570 * stabilizes mapping, prevents inode freeing in the shared
571 * file-backed region case and guards against movement to swap cache.
572 *
573 * Strictly speaking the folio lock is not needed in all cases being
574 * considered here and folio lock forces unnecessarily serialization.
575 * From this point on, mapping will be re-verified if necessary and
576 * folio lock will be acquired only if it is unavoidable
577 *
578 * Mapping checks require the folio so it is looked up now. For
579 * anonymous pages, it does not matter if the folio is split
580 * in the future as the key is based on the address. For
581 * filesystem-backed pages, the precise page is required as the
582 * index of the page determines the key.
583 */
584 folio = page_folio(page);
585 mapping = READ_ONCE(folio->mapping);
586
587 /*
588 * If folio->mapping is NULL, then it cannot be an anonymous
589 * page; but it might be the ZERO_PAGE or in the gate area or
590 * in a special mapping (all cases which we are happy to fail);
591 * or it may have been a good file page when get_user_pages_fast
592 * found it, but truncated or holepunched or subjected to
593 * invalidate_complete_page2 before we got the folio lock (also
594 * cases which we are happy to fail). And we hold a reference,
595 * so refcount care in invalidate_inode_page's remove_mapping
596 * prevents drop_caches from setting mapping to NULL beneath us.
597 *
598 * The case we do have to guard against is when memory pressure made
599 * shmem_writepage move it from filecache to swapcache beneath us:
600 * an unlikely race, but we do need to retry for folio->mapping.
601 */
602 if (unlikely(!mapping)) {
603 int shmem_swizzled;
604
605 /*
606 * Folio lock is required to identify which special case above
607 * applies. If this is really a shmem page then the folio lock
608 * will prevent unexpected transitions.
609 */
610 folio_lock(folio);
611 shmem_swizzled = folio_test_swapcache(folio) || folio->mapping;
612 folio_unlock(folio);
613 folio_put(folio);
614
615 if (shmem_swizzled)
616 goto again;
617
618 return -EFAULT;
619 }
620
621 /*
622 * Private mappings are handled in a simple way.
623 *
624 * If the futex key is stored in anonymous memory, then the associated
625 * object is the mm which is implicitly pinned by the calling process.
626 *
627 * NOTE: When userspace waits on a MAP_SHARED mapping, even if
628 * it's a read-only handle, it's expected that futexes attach to
629 * the object not the particular process.
630 */
631 if (folio_test_anon(folio)) {
632 /*
633 * A RO anonymous page will never change and thus doesn't make
634 * sense for futex operations.
635 */
636 if (unlikely(should_fail_futex(true)) || ro) {
637 err = -EFAULT;
638 goto out;
639 }
640
641 key->both.offset |= FUT_OFF_MMSHARED; /* ref taken on mm */
642 key->private.mm = mm;
643 key->private.address = address;
644
645 } else {
646 struct inode *inode;
647
648 /*
649 * The associated futex object in this case is the inode and
650 * the folio->mapping must be traversed. Ordinarily this should
651 * be stabilised under folio lock but it's not strictly
652 * necessary in this case as we just want to pin the inode, not
653 * update i_pages or anything like that.
654 *
655 * The RCU read lock is taken as the inode is finally freed
656 * under RCU. If the mapping still matches expectations then the
657 * mapping->host can be safely accessed as being a valid inode.
658 */
659 rcu_read_lock();
660
661 if (READ_ONCE(folio->mapping) != mapping) {
662 rcu_read_unlock();
663 folio_put(folio);
664
665 goto again;
666 }
667
668 inode = READ_ONCE(mapping->host);
669 if (!inode) {
670 rcu_read_unlock();
671 folio_put(folio);
672
673 goto again;
674 }
675
676 key->both.offset |= FUT_OFF_INODE; /* inode-based key */
677 key->shared.i_seq = get_inode_sequence_number(inode);
678 key->shared.pgoff = page_pgoff(folio, page);
679 rcu_read_unlock();
680 }
681
682 out:
683 folio_put(folio);
684 return err;
685 }
686
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-02-15 22:02 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-01-13 11:22 kernel/futex/core.c:505:38: sparse: sparse: cast removes address space '__user' of expression kernel test robot
2026-01-13 11:59 ` Sebastian Andrzej Siewior
2026-01-13 12:10 ` Peter Zijlstra
2026-01-13 17:37 ` Sebastian Andrzej Siewior
2026-01-13 19:39 ` Peter Zijlstra
2026-01-14 9:35 ` Sebastian Andrzej Siewior
2026-01-14 11:08 ` Peter Zijlstra
2026-01-14 12:09 ` Sebastian Andrzej Siewior
2026-01-14 12:16 ` Peter Zijlstra
-- strict thread matches above, loose matches on Subject: below --
2026-02-15 22:01 kernel test robot
2025-12-11 8:07 kernel test robot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®