---
id: CVE-2026-98507
title: 'mm/rmap: fix missing barrier between anon_vma init and vma->anon_vma publish'
summary: |-
  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.

  …
severity: none
vendor: Linux
product: Linux
affected:
  - >-
    Linux >= 5c341ee1dfc8fe69d66b1c8b19e463c6d7201ae1 <
    a500df43cde075f6ea4b8bae495d89f2c35b3e16
  - >-
    Linux >= 5c341ee1dfc8fe69d66b1c8b19e463c6d7201ae1 <
    4cef512a955e4008ae4436a4fc97726976df1fdf
  - >-
    Linux >= 5c341ee1dfc8fe69d66b1c8b19e463c6d7201ae1 <
    d348a715380bfcc6a611bdf722169d53fd01e735
  - >-
    Linux >= 5c341ee1dfc8fe69d66b1c8b19e463c6d7201ae1 <
    b6ac0b3f6013c168f22cad97e79967accacb08e1
  - Linux 2.6.36
published: '2026-10-18'
updated: '2026-10-18'
sourceUpdated: '2026-10-18T15:17:13.767Z'
source: CVEORG
sourceUrl: 'https://www.cve.org/CVERecord?id=CVE-2026-98507'
references:
  - url: 'https://git.kernel.org/stable/c/a500df43cde075f6ea4b8bae495d89f2c35b3e16'
  - url: 'https://git.kernel.org/stable/c/4cef512a955e4008ae4436a4fc97726976df1fdf'
  - url: 'https://git.kernel.org/stable/c/d348a715380bfcc6a611bdf722169d53fd01e735'
  - url: 'https://git.kernel.org/stable/c/b6ac0b3f6013c168f22cad97e79967accacb08e1'
tags:
  - cve.org
ingestedAt: '2026-10-18T15:45:12.286Z'
---

## Overview

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---

## Affected

- `Linux >= 5c341ee1dfc8fe69d66b1c8b19e463c6d7201ae1 < a500df43cde075f6ea4b8bae495d89f2c35b3e16`
- `Linux >= 5c341ee1dfc8fe69d66b1c8b19e463c6d7201ae1 < 4cef512a955e4008ae4436a4fc97726976df1fdf`
- `Linux >= 5c341ee1dfc8fe69d66b1c8b19e463c6d7201ae1 < d348a715380bfcc6a611bdf722169d53fd01e735`
- `Linux >= 5c341ee1dfc8fe69d66b1c8b19e463c6d7201ae1 < b6ac0b3f6013c168f22cad97e79967accacb08e1`
- `Linux 2.6.36`

## Remediation

Refer to the linked advisories for vendor-supplied fixes and affected version ranges.
