🔍 CVE-2026-74700

7.8
CVSS Score
HIGH
2026-08-22
2026-10-30 13:20
✅ 완료

🇰🇷 한글 설명

Linux 커널의 net/sched cls_api에서 발생하는 레이스 컨디션 취약점으로, 잠금되지 않은 필터 처리 과정에서 잘못된 참조가 발생할 수 있습니다. 이로 인해 특정 상황에서 객체가 조기에 해제되어 Use-After-Free(UAF) 메모리 오류를 유발할 수 있습니다.

💡 쉬운 설명

두 개의 서로 다른 네트워크 규칙을 동시에 만들 때, 시스템이 혼란을 일으켜 이미 삭제된 메모리 영역을 다시 사용하려고 시도하는 문제입니다. 이 과정에서 시스템이 충돌하거나 보안 공격에 노출될 수 있습니다.

🇺🇸 원문 (English)

In the Linux kernel, the following vulnerability has been resolved: net/sched: cls_api: Always acquire rtnl_lock when destroying locked classifiers Another challenge with unlocked filters. There is a short window in tc_new_tfilter where a tcf_proto can be found and briefly referenced by a totally unrelated, unlocked classifier's request and cause a race. Feng created a poc which created this race with two threads, one creating a u32 filter and other a flower filter in the same chain/prio: 1. Both threads enter tc_new_tfilter, both find the chain empty, both drop filter_chain_lock 2. u32 finishes tcf_proto_create("u32") first, calls tcf_chain_tp_insert_unique() -> inserts u32_tp into the chain 3. flower finishes tcf_proto_create("flower") later, calls tcf_chain_tp_insert_unique() -> tcf_chain_tp_find() now sees u32_tp already there, takes a reference on it, destroys flower's own tp_new and returns u32_tp to the caller. Flower then hits the kind mismatch check (because it requested for kind "flower" but tp->ops->kind is "u32") and goes through the errout path which calls tcf_proto_put() on u32_tp. If the u32 thread has already gone through its own errout (its change() call failed on the PoC's empty options) and dropped its create and insert refs, flower's put is the last one and drops u32_tp's refcnt to zero. At this point tp->ops->destroy() runs in a context that never took rtnl_lock. When that happens, it might cause a UAF like the following (illustrated by the PoC): [ +0.000710] BUG: KASAN: slab-use-after-free in u32_init (net/sched/cls_u32.c:393) [ +0.000281] Read of size 8 at addr ffff888120022f00 by task poc_feng_xue/524 Call Trace: u32_init (net/sched/cls_u32.c:393) tc_new_tfilter (net/sched/cls_api.c:2378) Allocated by task 526: u32_init (net/sched/cls_u32.c:378) tc_new_tfilter (net/sched/cls_api.c:2378) Freed by task 522: kfree u32_destroy (net/sched/cls_u32.c:662) tcf_proto_destroy (net/sched/cls_api.c:446) tcf_proto_put (net/sched/cls_api.c:459) tc_new_tfilter (net/sched/cls_api.c:2459) Fix this by having tcf_proto_destroy() take rtnl_lock around tp->ops->destroy() for locked classifiers whenever rtnl is not held. To explain why I used a temp variable "not_lockless" I'd like to point to a semi-related note on rtnl_held vs TCF_PROTO_OPS_DOIT_UNLOCKED (adding here for future cleanup if deemed necessary): The rtnl_held parameter and the TCF_PROTO_OPS_DOIT_UNLOCKED flag are redundant sources of truth for whether rtnl_lock is held. Among the nine classifier destroy(..rtnl_held..) callbacks, only flower consults the rtnl_held parameter which it propagates to tc_setup_cb_destroy() and tc_setup_cb_call(). The other eight (u32, flow, bpf, cgroup, route, basic, fw, mall) ignore it entirely;-> those that call tc_setup_cb_destroy() (u32, bpf, mall) hardcode true always instead of forwarding the parameter. A future cleanup should remove the rtnl_held parameter from the destroy callback signature entirely and have callers rely solely on their knowledge whether they are running in an unlocked context.

📍 영향받는 시스템

Linux Kernel (net/sched 모듈 사용 환경)

⚔️ 공격 벡터

두 개 이상의 스레드를 이용해 서로 다른 종류의 tc 필터(예: u32와 flower)를 동시에 생성하여 레이스 컨디션 유도

🛡️ 대응 방법

커널 패치를 통해 tcf_proto_destroy() 호출 시 rtnl_lock을 반드시 획득하도록 수정된 최신 커널로 업데이트

🔧 검증 코드

출력된 커널 버전이 해당 취약점이 수정된 패치 버전 이후인지 확인하십시오.

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