{"api_version":"1","generated_at":"2026-08-22T21:53:15+00:00","cve":"CVE-2026-74700","urls":{"html":"https://cve.report/CVE-2026-74700","api":"https://cve.report/api/cve/CVE-2026-74700.json","docs":"https://cve.report/api","cve_org":"https://www.cve.org/CVERecord?id=CVE-2026-74700","nvd":"https://nvd.nist.gov/vuln/detail/CVE-2026-74700"},"summary":{"title":"net/sched: cls_api: Always acquire rtnl_lock when destroying locked classifiers","description":"In the Linux kernel, the following vulnerability has been resolved:\n\nnet/sched: cls_api: Always acquire rtnl_lock when destroying locked classifiers\n\nAnother challenge with unlocked filters.\nThere is a short window in tc_new_tfilter where a tcf_proto can be found\nand briefly referenced by a totally unrelated, unlocked classifier's request\nand cause a race.\n\nFeng created a poc which created this race with two threads, one creating a\nu32 filter and other a flower filter in the same chain/prio:\n\n1. Both threads enter tc_new_tfilter, both find the chain empty, both\n   drop filter_chain_lock\n2. u32 finishes tcf_proto_create(\"u32\") first, calls\n   tcf_chain_tp_insert_unique() -> inserts u32_tp into the chain\n3. flower finishes tcf_proto_create(\"flower\") later, calls\n   tcf_chain_tp_insert_unique() -> tcf_chain_tp_find() now sees u32_tp\n   already there, takes a reference on it, destroys flower's own tp_new\n   and returns u32_tp to the caller.\n\nFlower then hits the kind mismatch check (because it requested for kind\n\"flower\" but tp->ops->kind is \"u32\") and goes through the errout path\nwhich calls tcf_proto_put() on u32_tp. If the u32 thread has already\ngone through its own errout (its change() call failed on the PoC's empty\noptions) and dropped its create and insert refs, flower's put is the\nlast one and drops u32_tp's refcnt to zero.\n\nAt this point tp->ops->destroy() runs in a context that never took\nrtnl_lock. When that happens, it might cause a UAF like the following\n(illustrated by the PoC):\n\n[  +0.000710] BUG: KASAN: slab-use-after-free in u32_init (net/sched/cls_u32.c:393)\n[  +0.000281] Read of size 8 at addr ffff888120022f00 by task poc_feng_xue/524\n\n  Call Trace:\n   u32_init (net/sched/cls_u32.c:393)\n   tc_new_tfilter (net/sched/cls_api.c:2378)\n\n  Allocated by task 526:\n   u32_init (net/sched/cls_u32.c:378)\n   tc_new_tfilter (net/sched/cls_api.c:2378)\n\n  Freed by task 522:\n   kfree\n   u32_destroy (net/sched/cls_u32.c:662)\n   tcf_proto_destroy (net/sched/cls_api.c:446)\n   tcf_proto_put (net/sched/cls_api.c:459)\n   tc_new_tfilter (net/sched/cls_api.c:2459)\n\nFix this by having tcf_proto_destroy() take rtnl_lock around\ntp->ops->destroy() for locked classifiers whenever rtnl is not held.\n\nTo explain why I used a temp variable \"not_lockless\" I'd like to point to a\nsemi-related note on rtnl_held vs TCF_PROTO_OPS_DOIT_UNLOCKED (adding here\nfor future cleanup if deemed necessary):\nThe rtnl_held parameter and the TCF_PROTO_OPS_DOIT_UNLOCKED flag are\nredundant sources of truth for whether rtnl_lock is held. Among the nine\nclassifier destroy(..rtnl_held..) callbacks, only flower consults the\nrtnl_held parameter which it propagates to tc_setup_cb_destroy()\nand tc_setup_cb_call(). The other eight (u32, flow, bpf, cgroup, route, basic,\nfw, mall) ignore it entirely;-> those that call tc_setup_cb_destroy()\n(u32, bpf, mall) hardcode true always instead of forwarding the parameter.\n\nA future cleanup should remove the rtnl_held parameter from the destroy callback\nsignature entirely and have callers rely solely on their knowledge whether\nthey are running in an unlocked context.","state":"PUBLISHED","assigner":"Linux","published_at":"2026-08-22 16:16:44","updated_at":"2026-08-22 16:16:44"},"problem_types":[],"metrics":[],"references":[{"url":"https://git.kernel.org/stable/c/b648c8a56531aeabd1c14f6b5cf1891e269b3756","name":"https://git.kernel.org/stable/c/b648c8a56531aeabd1c14f6b5cf1891e269b3756","refsource":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","tags":[],"title":"","mime":"","httpstatus":"","archivestatus":"0"},{"url":"https://git.kernel.org/stable/c/d6222af7274f08e7a1848131dc30319993d8f377","name":"https://git.kernel.org/stable/c/d6222af7274f08e7a1848131dc30319993d8f377","refsource":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","tags":[],"title":"","mime":"","httpstatus":"","archivestatus":"0"},{"url":"https://git.kernel.org/stable/c/a347304b2ca1a5377d5bd2d8a72e4b4f12afe648","name":"https://git.kernel.org/stable/c/a347304b2ca1a5377d5bd2d8a72e4b4f12afe648","refsource":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","tags":[],"title":"","mime":"","httpstatus":"","archivestatus":"0"},{"url":"https://git.kernel.org/stable/c/34e77d8e3570f9df3952496ddb402833695662fd","name":"https://git.kernel.org/stable/c/34e77d8e3570f9df3952496ddb402833695662fd","refsource":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","tags":[],"title":"","mime":"","httpstatus":"","archivestatus":"0"},{"url":"https://git.kernel.org/stable/c/a81f9c44d87fb59d99fce72e29e02cab3a49a1c3","name":"https://git.kernel.org/stable/c/a81f9c44d87fb59d99fce72e29e02cab3a49a1c3","refsource":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","tags":[],"title":"","mime":"","httpstatus":"","archivestatus":"0"},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-74700","name":"CVE Program record","refsource":"CVE.ORG","tags":["canonical"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-74700","name":"NVD vulnerability detail","refsource":"NVD","tags":["canonical","analysis"]}],"affected":[{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 12db03b65c2b90752e4c37666977fd4a1b5f5824 34e77d8e3570f9df3952496ddb402833695662fd git","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 12db03b65c2b90752e4c37666977fd4a1b5f5824 b648c8a56531aeabd1c14f6b5cf1891e269b3756 git","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 12db03b65c2b90752e4c37666977fd4a1b5f5824 d6222af7274f08e7a1848131dc30319993d8f377 git","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 12db03b65c2b90752e4c37666977fd4a1b5f5824 a81f9c44d87fb59d99fce72e29e02cab3a49a1c3 git","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 12db03b65c2b90752e4c37666977fd4a1b5f5824 a347304b2ca1a5377d5bd2d8a72e4b4f12afe648 git","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 5.1","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 5.1 semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 6.6.152 6.6.* semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 6.12.104 6.12.* semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 6.18.45 6.18.* semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 7.1.9 7.1.* semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 7.2 * original_commit_for_fix","platforms":[]}],"timeline":[],"solutions":[],"workarounds":[],"exploits":[],"credits":[],"nvd_cpes":[],"vendor_comments":[],"enrichments":{"kev":null,"epss":null,"legacy_qids":[]},"source_records":{"cve_program":{"containers":{"cna":{"affected":[{"defaultStatus":"unaffected","product":"Linux","programFiles":["net/sched/cls_api.c"],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","vendor":"Linux","versions":[{"lessThan":"34e77d8e3570f9df3952496ddb402833695662fd","status":"affected","version":"12db03b65c2b90752e4c37666977fd4a1b5f5824","versionType":"git"},{"lessThan":"b648c8a56531aeabd1c14f6b5cf1891e269b3756","status":"affected","version":"12db03b65c2b90752e4c37666977fd4a1b5f5824","versionType":"git"},{"lessThan":"d6222af7274f08e7a1848131dc30319993d8f377","status":"affected","version":"12db03b65c2b90752e4c37666977fd4a1b5f5824","versionType":"git"},{"lessThan":"a81f9c44d87fb59d99fce72e29e02cab3a49a1c3","status":"affected","version":"12db03b65c2b90752e4c37666977fd4a1b5f5824","versionType":"git"},{"lessThan":"a347304b2ca1a5377d5bd2d8a72e4b4f12afe648","status":"affected","version":"12db03b65c2b90752e4c37666977fd4a1b5f5824","versionType":"git"}]},{"defaultStatus":"affected","product":"Linux","programFiles":["net/sched/cls_api.c"],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","vendor":"Linux","versions":[{"status":"affected","version":"5.1"},{"lessThan":"5.1","status":"unaffected","version":"0","versionType":"semver"},{"lessThanOrEqual":"6.6.*","status":"unaffected","version":"6.6.152","versionType":"semver"},{"lessThanOrEqual":"6.12.*","status":"unaffected","version":"6.12.104","versionType":"semver"},{"lessThanOrEqual":"6.18.*","status":"unaffected","version":"6.18.45","versionType":"semver"},{"lessThanOrEqual":"7.1.*","status":"unaffected","version":"7.1.9","versionType":"semver"},{"lessThanOrEqual":"*","status":"unaffected","version":"7.2","versionType":"original_commit_for_fix"}]}],"cpeApplicability":[{"nodes":[{"cpeMatch":[{"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionEndExcluding":"6.6.152","versionStartIncluding":"5.1","vulnerable":true},{"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionEndExcluding":"6.12.104","versionStartIncluding":"5.1","vulnerable":true},{"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionEndExcluding":"6.18.45","versionStartIncluding":"5.1","vulnerable":true},{"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionEndExcluding":"7.1.9","versionStartIncluding":"5.1","vulnerable":true},{"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionEndExcluding":"7.2","versionStartIncluding":"5.1","vulnerable":true}],"negate":false,"operator":"OR"}]}],"descriptions":[{"lang":"en","value":"In the Linux kernel, the following vulnerability has been resolved:\n\nnet/sched: cls_api: Always acquire rtnl_lock when destroying locked classifiers\n\nAnother challenge with unlocked filters.\nThere is a short window in tc_new_tfilter where a tcf_proto can be found\nand briefly referenced by a totally unrelated, unlocked classifier's request\nand cause a race.\n\nFeng created a poc which created this race with two threads, one creating a\nu32 filter and other a flower filter in the same chain/prio:\n\n1. Both threads enter tc_new_tfilter, both find the chain empty, both\n   drop filter_chain_lock\n2. u32 finishes tcf_proto_create(\"u32\") first, calls\n   tcf_chain_tp_insert_unique() -> inserts u32_tp into the chain\n3. flower finishes tcf_proto_create(\"flower\") later, calls\n   tcf_chain_tp_insert_unique() -> tcf_chain_tp_find() now sees u32_tp\n   already there, takes a reference on it, destroys flower's own tp_new\n   and returns u32_tp to the caller.\n\nFlower then hits the kind mismatch check (because it requested for kind\n\"flower\" but tp->ops->kind is \"u32\") and goes through the errout path\nwhich calls tcf_proto_put() on u32_tp. If the u32 thread has already\ngone through its own errout (its change() call failed on the PoC's empty\noptions) and dropped its create and insert refs, flower's put is the\nlast one and drops u32_tp's refcnt to zero.\n\nAt this point tp->ops->destroy() runs in a context that never took\nrtnl_lock. When that happens, it might cause a UAF like the following\n(illustrated by the PoC):\n\n[  +0.000710] BUG: KASAN: slab-use-after-free in u32_init (net/sched/cls_u32.c:393)\n[  +0.000281] Read of size 8 at addr ffff888120022f00 by task poc_feng_xue/524\n\n  Call Trace:\n   u32_init (net/sched/cls_u32.c:393)\n   tc_new_tfilter (net/sched/cls_api.c:2378)\n\n  Allocated by task 526:\n   u32_init (net/sched/cls_u32.c:378)\n   tc_new_tfilter (net/sched/cls_api.c:2378)\n\n  Freed by task 522:\n   kfree\n   u32_destroy (net/sched/cls_u32.c:662)\n   tcf_proto_destroy (net/sched/cls_api.c:446)\n   tcf_proto_put (net/sched/cls_api.c:459)\n   tc_new_tfilter (net/sched/cls_api.c:2459)\n\nFix this by having tcf_proto_destroy() take rtnl_lock around\ntp->ops->destroy() for locked classifiers whenever rtnl is not held.\n\nTo explain why I used a temp variable \"not_lockless\" I'd like to point to a\nsemi-related note on rtnl_held vs TCF_PROTO_OPS_DOIT_UNLOCKED (adding here\nfor future cleanup if deemed necessary):\nThe rtnl_held parameter and the TCF_PROTO_OPS_DOIT_UNLOCKED flag are\nredundant sources of truth for whether rtnl_lock is held. Among the nine\nclassifier destroy(..rtnl_held..) callbacks, only flower consults the\nrtnl_held parameter which it propagates to tc_setup_cb_destroy()\nand tc_setup_cb_call(). The other eight (u32, flow, bpf, cgroup, route, basic,\nfw, mall) ignore it entirely;-> those that call tc_setup_cb_destroy()\n(u32, bpf, mall) hardcode true always instead of forwarding the parameter.\n\nA future cleanup should remove the rtnl_held parameter from the destroy callback\nsignature entirely and have callers rely solely on their knowledge whether\nthey are running in an unlocked context."}],"providerMetadata":{"dateUpdated":"2026-08-22T15:33:00.933Z","orgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","shortName":"Linux"},"references":[{"url":"https://git.kernel.org/stable/c/34e77d8e3570f9df3952496ddb402833695662fd"},{"url":"https://git.kernel.org/stable/c/b648c8a56531aeabd1c14f6b5cf1891e269b3756"},{"url":"https://git.kernel.org/stable/c/d6222af7274f08e7a1848131dc30319993d8f377"},{"url":"https://git.kernel.org/stable/c/a81f9c44d87fb59d99fce72e29e02cab3a49a1c3"},{"url":"https://git.kernel.org/stable/c/a347304b2ca1a5377d5bd2d8a72e4b4f12afe648"}],"title":"net/sched: cls_api: Always acquire rtnl_lock when destroying locked classifiers","x_generator":{"engine":"bippy-1.2.0"}}},"cveMetadata":{"assignerOrgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","assignerShortName":"Linux","cveId":"CVE-2026-74700","datePublished":"2026-08-22T15:33:00.933Z","dateReserved":"2026-08-15T05:44:03.927Z","dateUpdated":"2026-08-22T15:33:00.933Z","state":"PUBLISHED"},"dataType":"CVE_RECORD","dataVersion":"5.2"},"nvd":{"publishedDate":"2026-08-22 16:16:44","lastModifiedDate":"2026-08-22 16:16:44","problem_types":[],"metrics":[],"configurations":[]},"legacy_mitre":{"record":{"CveYear":"2026","CveId":"74700","Ordinal":"1","Title":"net/sched: cls_api: Always acquire rtnl_lock when destroying loc","CVE":"CVE-2026-74700","Year":"2026"},"notes":[{"CveYear":"2026","CveId":"74700","Ordinal":"1","NoteData":"In the Linux kernel, the following vulnerability has been resolved:\n\nnet/sched: cls_api: Always acquire rtnl_lock when destroying locked classifiers\n\nAnother challenge with unlocked filters.\nThere is a short window in tc_new_tfilter where a tcf_proto can be found\nand briefly referenced by a totally unrelated, unlocked classifier's request\nand cause a race.\n\nFeng created a poc which created this race with two threads, one creating a\nu32 filter and other a flower filter in the same chain/prio:\n\n1. Both threads enter tc_new_tfilter, both find the chain empty, both\n   drop filter_chain_lock\n2. u32 finishes tcf_proto_create(\"u32\") first, calls\n   tcf_chain_tp_insert_unique() -> inserts u32_tp into the chain\n3. flower finishes tcf_proto_create(\"flower\") later, calls\n   tcf_chain_tp_insert_unique() -> tcf_chain_tp_find() now sees u32_tp\n   already there, takes a reference on it, destroys flower's own tp_new\n   and returns u32_tp to the caller.\n\nFlower then hits the kind mismatch check (because it requested for kind\n\"flower\" but tp->ops->kind is \"u32\") and goes through the errout path\nwhich calls tcf_proto_put() on u32_tp. If the u32 thread has already\ngone through its own errout (its change() call failed on the PoC's empty\noptions) and dropped its create and insert refs, flower's put is the\nlast one and drops u32_tp's refcnt to zero.\n\nAt this point tp->ops->destroy() runs in a context that never took\nrtnl_lock. When that happens, it might cause a UAF like the following\n(illustrated by the PoC):\n\n[  +0.000710] BUG: KASAN: slab-use-after-free in u32_init (net/sched/cls_u32.c:393)\n[  +0.000281] Read of size 8 at addr ffff888120022f00 by task poc_feng_xue/524\n\n  Call Trace:\n   u32_init (net/sched/cls_u32.c:393)\n   tc_new_tfilter (net/sched/cls_api.c:2378)\n\n  Allocated by task 526:\n   u32_init (net/sched/cls_u32.c:378)\n   tc_new_tfilter (net/sched/cls_api.c:2378)\n\n  Freed by task 522:\n   kfree\n   u32_destroy (net/sched/cls_u32.c:662)\n   tcf_proto_destroy (net/sched/cls_api.c:446)\n   tcf_proto_put (net/sched/cls_api.c:459)\n   tc_new_tfilter (net/sched/cls_api.c:2459)\n\nFix this by having tcf_proto_destroy() take rtnl_lock around\ntp->ops->destroy() for locked classifiers whenever rtnl is not held.\n\nTo explain why I used a temp variable \"not_lockless\" I'd like to point to a\nsemi-related note on rtnl_held vs TCF_PROTO_OPS_DOIT_UNLOCKED (adding here\nfor future cleanup if deemed necessary):\nThe rtnl_held parameter and the TCF_PROTO_OPS_DOIT_UNLOCKED flag are\nredundant sources of truth for whether rtnl_lock is held. Among the nine\nclassifier destroy(..rtnl_held..) callbacks, only flower consults the\nrtnl_held parameter which it propagates to tc_setup_cb_destroy()\nand tc_setup_cb_call(). The other eight (u32, flow, bpf, cgroup, route, basic,\nfw, mall) ignore it entirely;-> those that call tc_setup_cb_destroy()\n(u32, bpf, mall) hardcode true always instead of forwarding the parameter.\n\nA future cleanup should remove the rtnl_held parameter from the destroy callback\nsignature entirely and have callers rely solely on their knowledge whether\nthey are running in an unlocked context.","Type":"Description","Title":"net/sched: cls_api: Always acquire rtnl_lock when destroying loc"}]}}}