During futex_key_to_node_opt() execution, vma->vm_policy is read under speculative mmap lock and RCU. Concurrently, mbind() may call vma_replace_policy() which frees the old mempolicy immediately via kmem_cache_free(). This creates a race where __futex_key_to_node() dereferences a freed mempolicy pointer, causing a use-after-free read of mpol->mode. [ 151.412631] BUG: KASAN: slab-use-after-free in __futex_key_to_node (kernel/futex/core.c:349) [ 151.414046] Read of size 2 at addr ffff888001c49634 by task e/87 [ 151.415969] Call Trace: [ 151.416732] __asan_load2 (mm/kasan/generic.c:271) [ 151.416777] __futex_key_to_node (kernel/futex/core.c:349) [ 151.416822] get_futex_key (kernel/futex/core.c:374 kernel/futex/core.c:386 kernel/futex/core.c:593) Fix by adding rcu to __mpol_put().
Unified · Split
| @@ -55,6 +55,7 @@ struct mempolicy { · 54 unmodified lines | ||
| 55 | 55 | nodemask_t cpuset_mems_allowed; /* relative to these nodes */ |
| 56 | 56 | nodemask_t user_nodemask; /* nodemask passed by user */ |
| 57 | 57 | } w; |
| 58 | + struct rcu_head rcu; | |
| 58 | 59 | }; |
| 59 | 60 | |
| 60 | 61 | /* |
| @@ -342,7 +342,7 @@ static int __futex_key_to_node(struct mm_struct *mm, unsigned long addr) · 341 unmodified lines | ||
| 342 | 342 | if (!vma) |
| 343 | 343 | return FUTEX_NO_NODE; |
| 344 | 344 | |
| 345 | − mpol = vma_policy(vma); | |
| 345 | + mpol = READ_ONCE(vma->vm_policy); | |
| 346 | 346 | if (!mpol) |
| 347 | 347 | return FUTEX_NO_NODE; |
| 348 | 348 | |
| @@ -487,7 +487,13 @@ void __mpol_put(struct mempolicy *pol) · 486 unmodified lines | ||
| 487 | 487 | { |
| 488 | 488 | if (!atomic_dec_and_test(&pol->refcnt)) |
| 489 | 489 | return; |
| 490 | − kmem_cache_free(policy_cache, pol); | |
| 490 | + /* | |
| 491 | + * Required to allow mmap_lock_speculative*() access, see for example | |
| 492 | + * futex_key_to_node_opt(). All accesses are serialized by mmap_lock, | |
| 493 | + * however the speculative lock section unbound by the normal lock | |
| 494 | + * boundaries, requiring RCU freeing. | |
| 495 | + */ | |
| 496 | + kfree_rcu(pol, rcu); | |
| 491 | 497 | } |
| 492 | 498 | EXPORT_SYMBOL_FOR_MODULES(__mpol_put, "kvm"); |
| 493 | 499 | |
| @@ -1020,7 +1026,7 @@ static int vma_replace_policy(struct vm_area_struct *vma, · 526 unmodified lines | ||
| 1020 | 1026 | } |
| 1021 | 1027 | |
| 1022 | 1028 | old = vma->vm_policy; |
| 1023 | − vma->vm_policy = new; /* protected by mmap_lock */ | |
| 1029 | + WRITE_ONCE(vma->vm_policy, new); /* protected by mmap_lock */ | |
| 1024 | 1030 | mpol_put(old); |
| 1025 | 1031 | |
| 1026 | 1032 | return 0; |