{"id":"CVE-2026-98797","title":"net: cap skb->queue_mapping when the tx queue is picked","summary":"In the Linux kernel, the following vulnerability has been resolved:\n\nnet: cap skb->queue_mapping when the tx queue is picked\n\nskbedit can set skb->queue_mapping and raise the per-CPU skip_txqueue\nflag so __dev_queue_xmit() honours the ma…","severity":"none","vendor":"Linux","product":"Linux","affected":["Linux >= 2f1e85b1aee459b7d0fd981839042c6a38ffaf0c < c6ec35c8267c84c2094cb485db20c06384fc2bcc","Linux >= 2f1e85b1aee459b7d0fd981839042c6a38ffaf0c < 2f581814e4e3ea6a5e98f49813dc4c68444a0c4a","Linux >= 2f1e85b1aee459b7d0fd981839042c6a38ffaf0c < f99de42f5c6f8d088063817dad82c3334e87c570","Linux >= 2f1e85b1aee459b7d0fd981839042c6a38ffaf0c < ea4d4b5dddb5121e64f56fd8e0720bd8b447b63d","Linux 5.19"],"published":"2026-10-18","updated":"2026-10-18","sourceUpdated":"2026-10-18T15:48:41.613Z","source":"CVEORG","sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-98797","references":[{"url":"https://git.kernel.org/stable/c/c6ec35c8267c84c2094cb485db20c06384fc2bcc"},{"url":"https://git.kernel.org/stable/c/2f581814e4e3ea6a5e98f49813dc4c68444a0c4a"},{"url":"https://git.kernel.org/stable/c/f99de42f5c6f8d088063817dad82c3334e87c570"},{"url":"https://git.kernel.org/stable/c/ea4d4b5dddb5121e64f56fd8e0720bd8b447b63d"}],"tags":["cve.org"],"ingestedAt":"2026-10-18T16:47:32.483Z","slug":"CVE-2026-98797","body":"## Overview\n\nIn the Linux kernel, the following vulnerability has been resolved:\n\nnet: cap skb->queue_mapping when the tx queue is picked\n\nskbedit can set skb->queue_mapping and raise the per-CPU skip_txqueue\nflag so __dev_queue_xmit() honours the mapping. __dev_queue_xmit()\ncleared the flag before sch_handle_egress() and only read it afterwards,\nso the flag was not confined to the xmit that set it: a nested xmit\n(mirred redirect or mirror, or a drop after skbedit) could set the flag\nand the outer xmit would consume it for an skb that never went through\nskbedit.\n\nA forwarded packet still carries the ingress NIC's rx_queue + 1 in\nskb->queue_mapping, so the outer device then indexes its tx queue state\nwith that stale value. Taprio's child array q->qdiscs[] is sized to the\ndevice's queue count, so taprio_enqueue() indexes past its allocation\nand dereferences the result as a struct Qdisc *.\n\nWe (ab)use the skb->nf_skip_egress which means \"skip netfilter egress\nfor this packet\" to tag to \"am I in tc egress?\". Despite the overload\nI dont see it as a conflict since the marker is set only around the\nsingle sch_handle_egress() call and ingress path is guarded by\ntc_at_ingress.\nI will send a followup(net-next) patch once this hits net-next to\nrename the skb->nf_skip_egress bit/flag to skb->skip_egress\n\nArm the flag only from the egress classifier that can use it: raise\nskip_txqueue from tcf_skbedit_act() only when it runs inside\nsch_handle_egress(), thanks to skb->nf_skip_egress. An egress qdisc\nclassifier runs in q->enqueue(), after the tx queue has been picked,\nso a mapping it sets cannot affect the current packet; arming the flag\nthere only pollutes it for a later xmit. Then own the flag for the xmit\nframe the egress hook runs in: save the incoming value and clear it just\nbefore sch_handle_egress(), and restore it after the hook - on the\nconsumed (drop) path, or, in the same call that reads it, on the\nsurviving path. The save and the restores stay inside the\negress_needed_key static branch, so a packet pays for them only when\negress hooks are active (2f1e85b1aee4).\n\nStore the value netdev_cap_txqueue() selected back into skb->queue_mapping\nin netdev_tx_queue_mapping(), as netdev_core_pick_tx() already does, so\nthe skip_txqueue path never hands a later reader on the xmit path a\nmapping the device cannot serve. A store made still later in the same\nframe, by a tc BPF program attached to a transmit qdisc, is outside this\npath and is not re-capped; a separate followup will resolve that path.\n\nnetdev_xmit_skip_txqueue() returns the previous flag value so the\nsave-and-clear is one call, and a no-op stub is provided when\nCONFIG_NET_EGRESS is disabled. skb->nf_skip_egress is compiled under\nCONFIG_NET_EGRESS rather than CONFIG_NETFILTER_SKIP_EGRESS, so\nskb_at_tc_egress() is valid whenever the egress path is built.\n\nA local user in a network namespace can redirect a packet from a device\nwith more TX queues to one with fewer after setting a mapping valid only\non the larger device. That reaches these reads and, under KASAN, faults\nwith \"slab-out-of-bounds in taprio_enqueue\".\n\nConditions to recreate the bug: the report's own trigger is a local user\nwith CAP_NET_ADMIN in a network namespace, so no eBPF program is needed.\nWith CONFIG_NET_SCH_TAPRIO=y, CONFIG_NET_ACT_SKBEDIT=y,\nCONFIG_NET_ACT_MIRRED=y, CONFIG_NET_CLS_MATCHALL=y,\nCONFIG_NET_SCH_PRIO=y and KASAN enabled, create qa (3 queues), qb\n(2 queues) and qc (1 queue) as dummy devices; put a taprio root on qb\n(num_tc 1, queues 2@0) and clsact on all three; then add an egress\nmatchall filter on every device. On qa: \"action skbedit queue_mapping 2\npipe action mirred egress redirect dev qb\". On qb: \"action mirred egress\nmirror dev qc\". On qc: \"action skbedit queue_mapping 0 pipe\". Send one\npacket out qa. qc's skbedit sets the flag while qb's outer xmit is in\nflight; without the fix qb consumes it and reads its two-entry taprio\nchild array with the forwarded packet's stale mapping. A qc whose\nskb\n---truncated---\n\n## Affected\n\n- `Linux >= 2f1e85b1aee459b7d0fd981839042c6a38ffaf0c < c6ec35c8267c84c2094cb485db20c06384fc2bcc`\n- `Linux >= 2f1e85b1aee459b7d0fd981839042c6a38ffaf0c < 2f581814e4e3ea6a5e98f49813dc4c68444a0c4a`\n- `Linux >= 2f1e85b1aee459b7d0fd981839042c6a38ffaf0c < f99de42f5c6f8d088063817dad82c3334e87c570`\n- `Linux >= 2f1e85b1aee459b7d0fd981839042c6a38ffaf0c < ea4d4b5dddb5121e64f56fd8e0720bd8b447b63d`\n- `Linux 5.19`\n\n## Remediation\n\nRefer to the linked advisories for vendor-supplied fixes and affected version ranges.","depth":"sunlit","depthScore":3,"depthScoreParts":{"impact":2.8,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}