PT-2026-116726 · Linux · Linux
CVE-2026-98507
·
Published
2026-10-18
·
Updated
2026-10-18
None
No severity ratings or metrics are available. When they are, we'll update the corresponding info on the page.
In the Linux kernel, the following vulnerability has been resolved:
mm/rmap: fix missing barrier between anon vma init and vma->anon vma publish
On arm64 server, we find that a task trying to grab the anon vma lock
triggers hungtask.
INFO: task main:2354726 blocked for more than 120 seconds.
Tainted: G E 5.10.0-0021.aarch64 #1
"echo 0 > /proc/sys/kernel/hung task timeout secs" disables this message.
task:main state:D stack: 0 pid:2354726 ppid:2350673 flags:0x00000a01
Call trace:
switch to+0x7c/0xbc
schedule+0x3b4/0x8a0
schedule+0x50/0xe0
rwsem down write slowpath+0x3cc/0x6cc
down write+0x60/0x260
anon vma prepare+0x6c/0x210
do anonymous page+0x258/0x660
handle pte fault+0x188/0x214
handle mm fault+0x1b0/0x380
handle mm fault+0xf4/0x284
do page fault+0x19c/0x494
do translation fault+0xcc/0xf8
do mem abort+0x48/0xac
el0 da+0x44/0x80
el0 sync handler+0x88/0xb4
el0 sync+0x160/0x180
After analyzing the vmcore, we found the anon vma->root->rwsem.count is
-1. There is another anon vma whose anon vma->root->rwsem.count is 1, the
anon vma->root->rwsem.owner shows the lock is held, but the stack of the
task shows the task doesn't hold the anon vma lock.
After adding more debugging info, we found anon vma prepare() reuses
anon vma and triggers the UAF of anon vma->root due to missing memory
barrier, leading to locking and unlocking two different anon vma->root,
thus leading to an anon vma will never be unlocked, and another anon vma
couldn't be locked anymore.
This race requires two adjacent VMAs that are not merged but are
anon vma-compatible (e.g., they differ in VMA ACCESS FLAGS that can be
changed by mprotect()). Two threads fault on each VMA concurrently, both
calling anon vma prepare() with only mmap lock held for reading.
THREAD A THREAD B
anon vma prepare anon vma prepare
find mergeable anon vma() -> NULL
anon vma = anon vma alloc();
anon vma->root = anon vma;
// the two stores may be reordered
vma->anon vma = anon vma;
// finds A's anon vma
anon vma = find mergeable anon vma(vma);
anon vma lock write(anon vma);
// may still see the old root
down write(&anon vma->root->rwsem);
anon vma unlock write(anon vma);
// see the new root, never unlock old
up write(&anon vma->root->rwsem);
thread A triggers page fault and calls anon vma prepare() to prepare
anon vma for the faulting vma. anon vma prepare() allocates and
initializes a new anon vma, and then publishes it to the vma with a plain
store. anon vma prepare() only requires the mmap lock to be held for
reading, so two threads can fault on adjacent VMAs at the same time.
While thread A publishes a new anon vma, thread B could find the anon vma
via find mergeable anon vma() and then locks anon vma->root->rwsem.
The store to anon vma->root in anon vma alloc() and the store to
vma->anon vma can be reordered. The anon vma lock write() and spin lock()
only provide acquire semantics, which do not prevent prior stores from
being reordered after them. The release semantics of the corresponding
spin unlock() and anon vma unlock write() come too late, the store to
vma->anon vma is already published before they take effect. As a result,
thread B can observe the following order:
vma->anon vma = anon vma;
anon vma->root = anon vma;
The anon vma slab is SLAB TYPESAFE BY RCU, so a newly allocated anon vma
may reuse memory from a previously freed one. The constructor
(anon vma ctor) does not reset anon vma->root, and put anon vma()
doesn't clear it either, so the old root value persists until
anon vma alloc() overwrites it. If that store isn't v
---truncated---
Found an issue in the description? Have something to add? Feel free to write us 👾
Related Identifiers
Affected Products
Linux