🇰🇷 한글 설명
Linux 커널의 mm/rmap에서 anon_vma 초기화와 vma->anon_vma 게시 사이에 메모리 배리어가 누락되어, 두 스레드가 인접 VMA에 동시에 페이지 폴트를 일으킬 때 잘못된 anon_vma->root에 대해 락을 잡고 해제하지 못하는 경쟁 조건이 발생합니다. 그 결과 특정 프로세스가 rwsem 락 대기 중 영구적으로 블록되는 행(hang) 현상이 나타날 수 있습니다.
💡 쉬운 설명
여러 작업이 동시에 메모리 페이지를 할당할 때 커널이 데이터를 준비하는 순서가 뒤바뀌어 잘못된 잠금을 걸게 됩니다. 이로 인해 일부 프로그램이 멈춰서 응답하지 않는 문제가 생길 수 있습니다.
🇺🇸 원문 (English)
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---
📍 영향받는 시스템
패치가 적용되지 않은 Linux 커널 (arm64 서버 등, 5.10 계열 포함 다수 버전)
⚔️ 공격 벡터
로컬 권한을 가진 사용자가 mprotect 등으로 인접 VMA를 만들고 다중 스레드로 동시에 페이지 폴트를 유발하여 시스템 행 또는 서비스 거부를 일으킴
🛡️ 대응 방법
배포판 제공 최신 커널 업데이트를 적용하고 재부팅 (상류 수정 커밋 반영 커널 사용)
🔧 검증 코드
출력된 커널 버전을 배포판 보안 공지에서 해당 CVE 수정 포함 버전과 비교하여, 수정 버전 이상이면 패치 적용됨, 미만이면 취약한 것으로 판단