{"id":41269,"date":"2026-10-19T00:32:55","date_gmt":"2026-10-18T16:32:55","guid":{"rendered":"https:\/\/www.54iter.cn\/?vuln=cve-2026-98797"},"modified":"2026-10-19T00:32:55","modified_gmt":"2026-10-18T16:32:55","slug":"cve-2026-98797","status":"publish","type":"vuln","link":"https:\/\/www.54iter.cn\/?vuln=cve-2026-98797","title":{"rendered":"CVE-2026-98797 &#8211; In the Linux kernel, the following vulnerability has been resolved:\n\nnet: cap skb-&gt;queue_m"},"content":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>net: cap skb-&gt;queue_mapping when the tx queue is picked<\/p>\n<p>skbedit can set skb-&gt;queue_mapping and raise the per-CPU skip_txqueue<br \/>\nflag so __dev_queue_xmit() honours the mapping. __dev_queue_xmit()<br \/>\ncleared the flag before sch_handle_egress() and only read it afterwards,<br \/>\nso the flag was not confined to the xmit that set it: a nested xmit<br \/>\n(mirred redirect or mirror, or a drop after skbedit) could set the flag<br \/>\nand the outer xmit would consume it for an skb that never went through<br \/>\nskbedit.<\/p>\n<p>A forwarded packet still carries the ingress NIC&#8217;s rx_queue + 1 in<br \/>\nskb-&gt;queue_mapping, so the outer device then indexes its tx queue state<br \/>\nwith that stale value. Taprio&#8217;s child array q-&gt;qdiscs[] is sized to the<br \/>\ndevice&#8217;s queue count, so taprio_enqueue() indexes past its allocation<br \/>\nand dereferences the result as a struct Qdisc *.<\/p>\n<p>We (ab)use the skb-&gt;nf_skip_egress which means &#8220;skip netfilter egress<br \/>\nfor this packet&#8221; to tag to &#8220;am I in tc egress?&#8221;. Despite the overload<br \/>\nI dont see it as a conflict since the marker is set only around the<br \/>\nsingle sch_handle_egress() call and ingress path is guarded by<br \/>\ntc_at_ingress.<br \/>\nI will send a followup(net-next) patch once this hits net-next to<br \/>\nrename the skb-&gt;nf_skip_egress bit\/flag to skb-&gt;skip_egress<\/p>\n<p>Arm the flag only from the egress classifier that can use it: raise<br \/>\nskip_txqueue from tcf_skbedit_act() only when it runs inside<br \/>\nsch_handle_egress(), thanks to skb-&gt;nf_skip_egress. An egress qdisc<br \/>\nclassifier runs in q-&gt;enqueue(), after the tx queue has been picked,<br \/>\nso a mapping it sets cannot affect the current packet; arming the flag<br \/>\nthere only pollutes it for a later xmit. Then own the flag for the xmit<br \/>\nframe the egress hook runs in: save the incoming value and clear it just<br \/>\nbefore sch_handle_egress(), and restore it after the hook &#8211; on the<br \/>\nconsumed (drop) path, or, in the same call that reads it, on the<br \/>\nsurviving path. The save and the restores stay inside the<br \/>\negress_needed_key static branch, so a packet pays for them only when<br \/>\negress hooks are active (2f1e85b1aee4).<\/p>\n<p>Store the value netdev_cap_txqueue() selected back into skb-&gt;queue_mapping<br \/>\nin netdev_tx_queue_mapping(), as netdev_core_pick_tx() already does, so<br \/>\nthe skip_txqueue path never hands a later reader on the xmit path a<br \/>\nmapping the device cannot serve. A store made still later in the same<br \/>\nframe, by a tc BPF program attached to a transmit qdisc, is outside this<br \/>\npath and is not re-capped; a separate followup will resolve that path.<\/p>\n<p>netdev_xmit_skip_txqueue() returns the previous flag value so the<br \/>\nsave-and-clear is one call, and a no-op stub is provided when<br \/>\nCONFIG_NET_EGRESS is disabled. skb-&gt;nf_skip_egress is compiled under<br \/>\nCONFIG_NET_EGRESS rather than CONFIG_NETFILTER_SKIP_EGRESS, so<br \/>\nskb_at_tc_egress() is valid whenever the egress path is built.<\/p>\n<p>A local user in a network namespace can redirect a packet from a device<br \/>\nwith more TX queues to one with fewer after setting a mapping valid only<br \/>\non the larger device. That reaches these reads and, under KASAN, faults<br \/>\nwith &#8220;slab-out-of-bounds in taprio_enqueue&#8221;.<\/p>\n<p>Conditions to recreate the bug: the report&#8217;s own trigger is a local user<br \/>\nwith CAP_NET_ADMIN in a network namespace, so no eBPF program is needed.<br \/>\nWith CONFIG_NET_SCH_TAPRIO=y, CONFIG_NET_ACT_SKBEDIT=y,<br \/>\nCONFIG_NET_ACT_MIRRED=y, CONFIG_NET_CLS_MATCHALL=y,<br \/>\nCONFIG_NET_SCH_PRIO=y and KASAN enabled, create qa (3 queues), qb<br \/>\n(2 queues) and qc (1 queue) as dummy devices; put a taprio root on qb<br \/>\n(num_tc 1, queues 2@0) and clsact on all three; then add an egress<br \/>\nmatchall filter on every device. On qa: &#8220;action skbedit queue_mapping 2<br \/>\npipe action mirred egress redirect dev qb&#8221;. On qb: &#8220;action mirred egress<br \/>\nmirror dev qc&#8221;. On qc: &#8220;action skbedit queue_mapping 0 pipe&#8221;. Send one<br \/>\npacket out qa. qc&#8217;s skbedit sets the flag while qb&#8217;s outer xmit is in<br \/>\nflight; without the fix qb consumes it and reads its two-entry taprio<br \/>\nchild array with the forwarded packet&#8217;s stale mapping. A qc whose<br \/>\nskb<br \/>\n&#8212;truncated&#8212;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>net: cap skb-&gt;queue_mapping when the tx queue is picked<\/p>\n<p>skbedit can set skb-&gt;queue_mapping and raise the per-CPU skip_txqueue<br \/>\nflag<\/p>\n","protected":false},"template":"","meta":[],"categories":[],"tags":[],"class_list":["post-41269","vuln","type-vuln","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln\/41269","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln"}],"about":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/types\/vuln"}],"wp:attachment":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=41269"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=41269"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=41269"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}