
# CVE-2026-31429 PoC (Linux 커널 >= 6.3 < 6.12.82 Slab 크로스-캐시 혼동) - Antonius - w1sdom - bluedragonsec.com이 발견한 취약점
bpf_prog_test_run_skb를 통한 KFENCE 할당 SKB 헤드의 교차 캐시 해제심각도: 중간 (CWE-763: 잘못된 포인터 또는 참조 해제)
게시일: 2026-04-20
영향을 받는 서브시스템: net/core/skbuff.c — skb_kfree_head()
연구자: Antonius / w1sdom — Blue Dragon Security
연락처: [email protected]
Lore 스레드: https://lore.kernel.org/netdev/CAK8a0jxC5L5N7hq-DT2_NhUyjBxrPocoiDazzsBk4TGgT1r4-A@mail.gmail.com/
이 저장소는 CVE-2026-31429에 대한 개념 증명(작동하는 익스플로잇이 아닌 POC만 해당)을 포함하며, Linux 커널 네트워킹 스택의 슬랩 교차 캐시 혼동 버그입니다. 이 버그는 KFENCE가 활성화되고 호출자(특히 의 )가 와 우연히 동일한 크기로 을 통해 SKB 헤드 버퍼를 할당할 때 트리거됩니다. KFENCE의 정확한 크기 보고 의미론으로 인해 커널의 함수는 객체를 원래 캐시 대신 로 잘못 해제하여 슬랩 메타데이터를 손상시킵니다.
net/bpf/test_run.cbpf_test_initSKB_SMALL_HEAD_CACHE_SIZEkzalloc()skb_kfree_head()kmalloc-1kskb_small_head_cache| 상태 | 범위 |
|---|---|
| 영향 있음 | Linux >= 6.3 (bf9f1baa279f에 의해 도입) |
| 영향 없음 | < 6.3 |
| 수정됨 | >= 6.12.82 |
| 수정됨 | >= 6.18.23 |
| 수정됨 | >= 6.19.13 |
| 수정됨 | >= 7.0 (메인라인, 커밋 0f42e3f4fe2a) |
이 취약점은 skb_small_head_cache와 skb_kfree_head()의 조건부 해제 로직을 추가한 커밋 bf9f1baa279f ("net: add dedicated kmem_cache for typical/small skb->head")에 의해 도입되었습니다.
skb_small_head_cache 설계 의도SKB_SMALL_HEAD_CACHE_SIZE는 일반적인 kmalloc 버킷 크기(항상 2의 거듭제곱: 512, 1024, ...)와의 충돌을 피하기 위해 의도적으로 2의 거듭제곱이 아닌 값(예: x86_64에서 704바이트)으로 설정됩니다. skb_kfree_head()의 휴리스틱은 이 고유성을 활용하여 skb_end_offset만으로 해제를 라우팅합니다:
// net/core/skbuff.c (취약 — 수정 전)
static void skb_kfree_head(void *head, unsigned int end_offset)
{
if (end_offset == SKB_SMALL_HEAD_HEADROOM)
kmem_cache_free(net_hotdata.skb_small_head_cache, head);
else
kfree(head);
}
end_offset == SKB_SMALL_HEAD_HEADROOM → skb_small_head_cache에서 온 것으로 가정 → kmem_cache_free()kfree()이 휴리스틱은 일반적인 슬랩 의미론에서만 타당하며, 여기서 ksize()는 버킷 크기(704바이트 요청의 경우 1024)를 반환하며, 이는 SKB_SMALL_HEAD_CACHE_SIZE와 결코 같지 않습니다.
KFENCE(커널 전기 울타리)는 커널 할당의 일부를 가로채서 가드 페이지 메모리에서 제공합니다. 중요한 동작 차이: kfence_ksize()는 슬랩 버킷 크기가 아닌 정확한 요청 크기를 반환합니다.
BPF_PROG_TEST_RUN (시스콜 321, cmd BPF_PROG_TEST_RUN=10)
└─> __sys_bpf()
└─> bpf_prog_test_run_skb()
└─> bpf_test_init()
└─> kzalloc(size, GFP_USER)
│ size == SKB_SMALL_HEAD_CACHE_SIZE (x86_64에서 704)
│ KFENCE 가로챔 → 객체가 kmalloc-1k 영역에서 제공됨
│
└─> slab_build_skb(data, NULL, size)
└─> ksize(data)
└─> kfence_ksize() ← 704 반환 (정확!)
└─> skb_end_offset
= ksize(data) - sizeof(skb_shared_info)
= 704 - 320
= 384
= SKB_SMALL_HEAD_HEADROOM ← 잘못된 일치!
[SKB 해제 경로에서:]
└─> sk_skb_reason_drop()
└─> skb_release_data()
└─> skb_free_head()
└─> skb_kfree_head(head, skb->end)
└─> (end_offset == SKB_SMALL_HEAD_HEADROOM) == TRUE
└─> kmem_cache_free(skb_small_head_cache, head)
↑ 버그: head는 skb_small_head_cache가 아닌 kmalloc-1k에서 온 것!
→ warn_free_bad_obj() → SLUB 손상
skb_end_offset = 384인가?x86_64에서:
SKB_SMALL_HEAD_CACHE_SIZE = 704바이트
sizeof(skb_shared_info) = 320바이트
SKB_SMALL_HEAD_HEADROOM = 704 - 320 = 384
KFENCE가 704바이트 kzalloc()을 가로챌 때 kfence_ksize()는 정확히 704를 반환합니다. 산술 연산은 skb_end_offset = 384 = SKB_SMALL_HEAD_HEADROOM을 생성하여 skb_kfree_head()의 조건을 충족시키고 잘못된 해제 경로를 트리거합니다.
Jiayuan Chen이 작성하고(Eric Dumazet이 검토, Jakub Kicinski가 병합) 업스트림 수정은 휴리스틱을 완전히 제거합니다:
// net/core/skbuff.c (수정됨)
static void skb_kfree_head(void *head, unsigned int end_offset)
{
kfree(head); // 항상 일반; 두 경우 모두 작동
}
kfree()는 kmalloc 할당 메모리와 skb_small_head_cache 할당 메모리 모두에 안전합니다. skb_small_head_cache에 대한 kmem_cache_free()가 더 이상 필요하지 않기 때문입니다 — 일반 kfree()는 슬랩 페이지의 kmem_cache 포인터를 통해 내부적으로 올바른 캐시를 해결합니다.
재현 프로그램(repro_bpf.c)은 QEMU 환경(i440FX, BIOS 1.17.0-debian)에서 Linux 7.0.0-rc5에서 실행되었습니다. 다음 커널 WARNING 연쇄가 관찰되었습니다:
[ 3065.322973] ------------[ cut here ]------------
[ 3065.322990] kmem_cache_free(skbuff_small_head, ffff888186d6e000): object belongs to different cache kmalloc-1k
[ 3065.323005] WARNING: mm/slub.c:6258 at warn_free_bad_obj+0x91/0xc0, CPU#0: repro_bpf/2167
[ 3065.323061] CPU: 0 UID: 0 PID: 2167 Comm: repro_bpf Not tainted 7.0.0-rc5 #1 PREEMPT(lazy)
[ 3065.323098] RIP: 0010:warn_free_bad_obj+0x98/0xc0
...
[ 3065.323231] Call Trace:
[ 3065.323247] skb_free_head+0x1ec/0x290
[ 3065.323267] skb_release_data+0x7a6/0x9d0
[ 3065.323308] bpf_prog_test_run_skb+0x14f8/0x3410
[ 3065.323510] __sys_bpf+0x769/0x4b60
[ 3065.323763] __x64_sys_bpf+0x78/0xc0
[ 3065.323794] do_syscall_64+0x111/0x690
[ 3065.323813] entry_SYSCALL_64_after_hwframe+0x77/0x7f
WARNING 연쇄는 트리거당 4개의 개별 스플랫을 생성합니다:
warn_free_bad_obj — 기본 교차 캐시 해제 감지 (mm/slub.c:6258)depot_fetch_stack — Allocated 추적에서 스택 디포 풀 인덱스 범위 초과 (lib/stackdepot.c:506)stack_depot_print — 손상된 핸들 감지 (lib/stackdepot.c:780)depot_fetch_stack + stack_depot_print — Freed 추적에 대해 동일한 쌍 반복이 연쇄는 객체의 SLUB 추적 메타데이터(alloc_track / free_track)가 잘못된 캐시 해제 후 손상되는 스택 디포 핸들을 참조함을 나타냅니다.
커널: Linux >= 6.3, 다음으로 컴파일:
CONFIG_KFENCE=y
CONFIG_BPF_SYSCALL=y
CONFIG_NET_SCH_INGRESS=y (또는 SCHED_CLS 지원 드라이버)
CONFIG_SLUB_DEBUG=y (warn_free_bad_obj 가시성용)
CONFIG_STACKDEPOT=y (전체 연쇄용)
권한: root (uid=0) — BPF_PROG_LOAD에 필요
gcc -O2 -o cve-2026-31429-poc-only cve-2026-31429-poc-only.c
sudo ./cve-2026-31429-poc-only
dmesg | grep -E "warn_free_bad_obj|Wrong slab cache|cross-cache"
PoC는 최소 3-명령 BPF 프로그램(유형 BPF_PROG_TYPE_SCHED_CLS)을 로드합니다:
ld_imm64 r0, 0 ; 2개 명령 (와이드)
exit ; 1개 명령
그런 다음 BPF_PROG_TEST_RUN (cmd=10)을 다음으로 호출합니다:
data_size_in = 284바이트의 Syzkaller 파생 패킷 데이터flags = BPF_F_TEST_RUN_ON_CPU (0x4) — CPU 0에 실행 고정repeat = 4284바이트 입력 데이터는 bpf_test_init의 할당 경로를 실행하여 요청된 버퍼 크기가 SKB_SMALL_HEAD_CACHE_SIZE와 같아지도록 하여 확률적으로 KFENCE 가로챔 창에 도달합니다.
| 커밋 | 트리 | 병합자 | 날짜 |
|---|---|---|---|
0f42e3f4fe2a | 메인라인 | Jakub Kicinski | 2026-04-06 |
60313768a8ed | linux-stable | Greg Kroah-Hartman | 2026-04-18 |
2d64618ea846 | linux-stable | Greg Kroah-Hartman | 2026-04-18 |
474e00b935db | linux-stable | Greg Kroah-Hartman | 2026-04-18 |
서명 체인: Jiayuan Chen → Reviewed-by Eric Dumazet (Google) → Jakub Kicinski → Greg Kroah-Hartman
Reported-by 크레딧: 4개 커밋 모두에서 Antonius <[email protected]>
도입 커밋: bf9f1baa279f ("net: add dedicated kmem_cache for typical/small skb->head")
.
├── README.md — 이 파일
├── cve-2026-31429-poc-only.c — 개념 증명 전용 (익스플로잇 아님)
└── dmesg.txt — 성공적인 재현의 원시 커널 스플랫
| 날짜 | 이벤트 |
|---|---|
| ~2026년 초 | Linux 7.0-rc5에서 Syzkaller 퍼징을 통해 버그 발견 |
| 2026-04-03 | Jiayuan Chen이 패치 작성, Reported-by 크레딧 Antonius |
| 2026-04-06 | 메인라인 커밋 0f42e3f4fe2a Jakub Kicinski가 병합 |
| 2026-04-18 | Greg Kroah-Hartman이 Stable 백포트 병합 (6.12.x, 6.18.x, 6.19.x) |
| 2026-04-20 | CVE-2026-31429 게시 |
Antonius (별명: w1sdom)
설립자 및 선임 연구원 — Blue Dragon Security
인도네시아
[email protected]
이 PoC는 업스트림 패치가 제공된 후 교육 및 연구 목적으로 공개되었습니다. 소유하지 않거나 명시적 테스트 허가를 받지 않은 시스템에서는 사용하지 마십시오. 작성자는 오용에 대한 책임을 지지 않습니다.