🔍 CVE-2024-46787

7.8
CVSS Score
HIGH
2024-09-18
2026-10-30 13:17
✅ 완료

🇰🇷 한글 설명

Linux 커널의 userfaultfd 메커니즘에서 huge PMD(Page Middle Directory)를 처리할 때 발생하는 레이스 컨디션 취약점입니다. 이 결함은 특정 커널 버전에서 페이지 테이블이 아닌 데이터를 페이지 테이블로 오인하여 처리하게 만들어 커널 패닉(BUG_ON)이나 메모리 손상을 유발할 수 있습니다.

💡 쉬운 설명

컴퓨터의 메모리를 관리하는 과정에서 두 가지 작업이 동시에 일어날 때 발생하는 충돌 문제입니다. 이 충돌로 인해 시스템이 갑자기 멈추거나(크래시), 보안상 위험한 메모리 오류가 발생할 수 있습니다.

🇺🇸 원문 (English)

In the Linux kernel, the following vulnerability has been resolved: userfaultfd: fix checks for huge PMDs Patch series "userfaultfd: fix races around pmd_trans_huge() check", v2. The pmd_trans_huge() code in mfill_atomic() is wrong in three different ways depending on kernel version: 1. The pmd_trans_huge() check is racy and can lead to a BUG_ON() (if you hit the right two race windows) - I've tested this in a kernel build with some extra mdelay() calls. See the commit message for a description of the race scenario. On older kernels (before 6.5), I think the same bug can even theoretically lead to accessing transhuge page contents as a page table if you hit the right 5 narrow race windows (I haven't tested this case). 2. As pointed out by Qi Zheng, pmd_trans_huge() is not sufficient for detecting PMDs that don't point to page tables. On older kernels (before 6.5), you'd just have to win a single fairly wide race to hit this. I've tested this on 6.1 stable by racing migration (with a mdelay() patched into try_to_migrate()) against UFFDIO_ZEROPAGE - on my x86 VM, that causes a kernel oops in ptlock_ptr(). 3. On newer kernels (>=6.5), for shmem mappings, khugepaged is allowed to yank page tables out from under us (though I haven't tested that), so I think the BUG_ON() checks in mfill_atomic() are just wrong. I decided to write two separate fixes for these (one fix for bugs 1+2, one fix for bug 3), so that the first fix can be backported to kernels affected by bugs 1+2. This patch (of 2): This fixes two issues. I discovered that the following race can occur: mfill_atomic other thread ============ ============ <zap PMD> pmdp_get_lockless() [reads none pmd] <bail if trans_huge> <if none:> <pagefault creates transhuge zeropage> __pte_alloc [no-op] <zap PMD> <bail if pmd_trans_huge(*dst_pmd)> BUG_ON(pmd_none(*dst_pmd)) I have experimentally verified this in a kernel with extra mdelay() calls; the BUG_ON(pmd_none(*dst_pmd)) triggers. On kernels newer than commit 0d940a9b270b ("mm/pgtable: allow pte_offset_map[_lock]() to fail"), this can't lead to anything worse than a BUG_ON(), since the page table access helpers are actually designed to deal with page tables concurrently disappearing; but on older kernels (<=6.4), I think we could probably theoretically race past the two BUG_ON() checks and end up treating a hugepage as a page table. The second issue is that, as Qi Zheng pointed out, there are other types of huge PMDs that pmd_trans_huge() can't catch: devmap PMDs and swap PMDs (in particular, migration PMDs). On <=6.4, this is worse than the first issue: If mfill_atomic() runs on a PMD that contains a migration entry (which just requires winning a single, fairly wide race), it will pass the PMD to pte_offset_map_lock(), which assumes that the PMD points to a page table. Breakage follows: First, the kernel tries to take the PTE lock (which will crash or maybe worse if there is no "struct page" for the address bits in the migration entry PMD - I think at least on X86 there usually is no corresponding "struct page" thanks to the PTE inversion mitigation, amd64 looks different). If that didn't crash, the kernel would next try to write a PTE into what it wrongly thinks is a page table. As part of fixing these issues, get rid of the check for pmd_trans_huge() before __pte_alloc() - that's redundant, we're going to have to check for that after the __pte_alloc() anyway. Backport note: pmdp_get_lockless() is pmd_read_atomic() in older kernels.

📍 영향받는 시스템

Linux Kernel (특히 6.5 미만 버전에서 더 위험하며, 6.5 이상에서도 특정 조건에서 발생 가능)

⚔️ 공격 벡터

사용자 공간에서 userfaultfd를 이용해 메모리 페이지 폴트 시점을 정밀하게 제어하여 커널 내 레이스 컨디션을 유도

🛡️ 대응 방법

Linux 커널을 최신 보안 패치가 적용된 버전으로 업데이트하거나, 해당 커널 패치 시리즈를 백포트하여 적용

🔧 검증 코드

출력된 커널 버전을 확인하여 취약점이 해결된 패치 버전 이상인지 확인합니다.

Shell / Script
uname -r

📊 CVSS Vector String

CVSS Vector
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

💻 영향받는 소프트웨어

Linux Kernel