{"id":"CVE-2026-98797","kind":"vulnerability","aliases":["CVE-2026-98797"],"related":[],"title":"Linux kernel: stale queue mapping lets a local user crash taprio","description":"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 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---","summary":"A local user with CAP_NET_ADMIN in a network namespace can redirect packets between devices with different transmit queue counts, leaving a stale queue index that makes the taprio scheduler read past its allocated array. Under KASAN this is reported as an out-of-bounds access in taprio_enqueue, so the impact is a kernel memory fault and possible privilege escalation.","severity":"high","cvss":null,"epss":null,"exploited":false,"exploited_at":null,"products":[],"ecosystems":[],"tags":["containers","linux-kernel","multi-tenant","runtime","taprio"],"not_affected_if":["no-taprio","no-untrusted-local-users"],"how_to_check":"- `uname -r` and compare against the kernel build that contains the fix, which the sources do not name\n- `lsmod | grep sch_taprio` or `zgrep CONFIG_NET_SCH_TAPRIO /proc/config.gz` to see whether taprio is built in or loaded\n- `zgrep CONFIG_NET_ACT_SKBEDIT /proc/config.gz` and `zgrep CONFIG_NET_ACT_MIRRED /proc/config.gz` to see whether the required actions are present","fix":"- Update the Linux kernel to a stable release that includes the upstream commit resolving this issue; the exact fixed version is unknown from the sources\n- Until patched, unload or avoid the taprio qdisc where it is not needed, and restrict CAP_NET_ADMIN in untrusted network namespaces","status":"open","published_at":"2026-10-18T16:17:33+00:00","first_seen_at":"2026-10-23T21:57:16+00:00","triaged_at":"2026-10-25T05:15:10+00:00","updated_at":"2026-10-25T05:15:10+00:00","category":"runtime","confidence":0.7,"triage":{"at":"2026-10-25T05:15:10Z","model":"claude-haiku-5-5","output":{"fix":"- Update the Linux kernel to a stable release that includes the upstream commit resolving this issue; the exact fixed version is unknown from the sources\n- Until patched, unload or avoid the taprio qdisc where it is not needed, and restrict CAP_NET_ADMIN in untrusted network namespaces","reach":"local","title":"Linux kernel: stale queue mapping lets a local user crash taprio","impact":"high","summary":"A local user with CAP_NET_ADMIN in a network namespace can redirect packets between devices with different transmit queue counts, leaving a stale queue index that makes the taprio scheduler read past its allocated array. Under KASAN this is reported as an out-of-bounds access in taprio_enqueue, so the impact is a kernel memory fault and possible privilege escalation.","category":"runtime","nicknames":[],"who_cares":["linux-kernel","taprio","multi-tenant","containers"],"confidence":0.7,"needs_auth":"yes","worst_case":"local user crashes kernel or may escalate privileges","analyst_note":"Confirm the fixed kernel versions and whether the bug is exploitable for privilege escalation beyond the KASAN out-of-bounds report; check the NVD entry at https://nvd.nist.gov/vuln/detail/CVE-2026-98797 and the upstream kernel commit.","how_to_check":"- `uname -r` and compare against the kernel build that contains the fix, which the sources do not name\n- `lsmod | grep sch_taprio` or `zgrep CONFIG_NET_SCH_TAPRIO /proc/config.gz` to see whether taprio is built in or loaded\n- `zgrep CONFIG_NET_ACT_SKBEDIT /proc/config.gz` and `zgrep CONFIG_NET_ACT_MIRRED /proc/config.gz` to see whether the required actions are present","needs_analyst":true,"not_affected_if":["no-taprio","no-untrusted-local-users"],"who_is_affected":"Systems running a Linux kernel with taprio, skbedit and mirred available, where local users can create network namespaces and configure traffic control. Hosts that do not build or load taprio, or that do not let untrusted users gain CAP_NET_ADMIN in a namespace, are much less exposed. The sources do not state which kernel versions are vulnerable.","user_interaction":"no","needs_special_setup":"yes","severity_if_unknown":"high"},"cost_usd":0.000905,"input_tokens":4830,"output_tokens":844,"severity_before":"unknown"},"analyst_status":null,"analyst_note":null,"search":"'-2026':13A,16A '-98797':14A,17A '0':748C,791C '1':202C,732C,745C '2':728C,747C,769C '2f1e85b1aee4':475C '3':725C 'ab':251C 'access':74B 'act':351C,702C,707C 'action':765C,771C,779C,787C 'activ':474C 'add':755C 'admin':33B,683C 'affect':387C 'afterward':147C 'alloc':61B,241C 'alreadi':498C 'arm':333C,391C 'around':290C 'array':62B,224C,826C 'attach':534C 'back':483C 'bit/flag':328C 'bound':73B,662C 'bpf':532C 'branch':462C 'bug':670C 'built':616C 'call':296C,442C,573C 'cannot':386C,518C 'cap':31B,101C,480C,548C,681C 'carri':195C 'child':223C,825C 'classifi':340C,369C 'clear':136C,420C,570C 'cls':712C 'clsact':750C 'compil':593C 'condit':666C 'config':583C,595C,600C,695C,700C,705C,710C,715C 'confin':153C 'conflict':283C 'consum':181C,435C,816C 'contain':18B 'core':495C 'could':172C 'count':47B,234C 'cpu':122C 'crash':10A 'creat':723C 'current':389C 'cve':12A,15A 'derefer':243C 'despit':274C 'dev':127C,133C,775C,783C 'devic':42B,210C,231C,517C,630C,648C,736C,762C 'differ':44B 'disabl':587C 'dont':278C 'drop':169C,436C 'dummi':735C 'ebpf':690C 'egress':142C,257C,262C,273C,295C,327C,332C,339C,359C,365C,367C,411C,426C,458C,471C,585C,591C,597C,603C,608C,613C,757C,773C,781C 'enabl':722C 'enqueu':77B,237C,373C,665C 'entri':823C 'escal':89B 'everi':761C 'fault':85B,656C 'fewer':638C 'filter':759C 'fix':814C 'flag':125C,138C,150C,175C,335C,393C,405C,563C,803C 'flight':811C 'follow':95C 'followup':310C,551C 'forward':192C,829C 'frame':409C,528C 'guard':301C 'hand':506C 'handl':141C,294C,358C,425C 'hit':317C 'honour':130C 'hook':412C,432C,472C 'impact':80B 'incom':417C 'index':52B,212C,238C 'ingress':197C,298C,305C 'insid':356C,456C 'kasan':64B,655C,721C 'kernel':2A,21B,83B,93C 'key':460C 'larger':647C 'later':400C,508C,524C 'leav':48B 'let':6A 'linux':1A,20B,92C 'linux-kernel':19B 'local':8A,28B,618C,678C 'made':522C 'make':54B 'map':5A,104C,116C,132C,206C,383C,487C,492C,515C,642C,768C,790C,833C 'marker':286C 'matchal':713C,758C 'mean':259C 'memori':84B 'mir':163C,708C,772C,780C 'mirror':166C,782C 'multi':23B 'multi-ten':22B 'namespac':37B,623C,687C 'need':459C,693C 'nest':161C 'net':32B,100C,312C,319C,584C,596C,682C,696C,701C,706C,711C,716C 'net-next':311C,318C 'netdev':479C,489C,494C,556C 'netfilt':261C,601C 'network':36B,622C,686C 'never':187C,505C 'next':313C,320C 'nf':255C,325C,363C,589C 'nic':198C 'no-op':576C 'num':743C 'one':572C,636C,794C 'op':578C 'out-of-bound':70B 'outer':178C,209C,807C 'outsid':540C 'overload':276C 'packet':40B,193C,265C,390C,465C,627C,795C,830C 'past':59B,239C 'patch':314C 'path':299C,437C,449C,504C,513C,542C,555C,614C 'pay':466C 'per':121C 'per-cpu':120C 'pick':110C,380C,496C 'pipe':770C,792C 'pollut':396C 'possibl':87B 'previous':562C 'prio':718C 'privileg':88B 'program':533C,691C 'provid':581C 'put':737C 'q':225C,372C 'qa':724C,764C,797C 'qb':727C,742C,776C,778C,805C,815C 'qc':731C,784C,786C,798C,835C 'qdisc':226C,249C,368C,538C 'queue':4A,46B,51B,103C,108C,115C,128C,134C,201C,205C,215C,233C,377C,486C,491C,634C,726C,729C,733C,746C,767C,789C 'rais':118C,345C 'rather':598C 're':547C 're-cap':546C 'reach':650C 'read':58B,145C,444C,652C,819C 'reader':509C 'recreat':668C 'redirect':39B,164C,625C,774C 'renam':322C 'report':67B,672C 'resolv':99C,553C 'restor':428C,454C 'result':245C 'return':560C 'root':740C 'run':355C,370C,413C 'runtim':25B 'rx':200C 'save':415C,451C,568C 'save-and-clear':567C 'sch':140C,293C,357C,424C,697C,717C 'schedul':57B 'see':279C 'select':482C 'send':308C,793C 'separ':550C 'serv':519C 'set':113C,158C,173C,288C,385C,640C,801C 'sinc':284C 'singl':292C 'size':228C 'skb':102C,114C,185C,204C,254C,324C,330C,362C,485C,588C,605C,837C 'skbedit':111C,171C,190C,350C,703C,766C,788C,800C 'skip':123C,256C,260C,326C,331C,346C,364C,502C,558C,590C,602C 'slab':659C 'slab-out-of-bound':658C 'stale':3A,50B,219C,832C 'state':216C 'static':461C 'stay':455C 'still':194C,523C 'store':476C,521C 'struct':248C 'stub':579C 'surviv':448C 'tag':267C 'taprio':11A,26B,56B,76B,221C,236C,664C,698C,739C,824C 'tc':272C,303C,531C,607C,744C 'tcf':349C 'tenant':24B 'thank':360C 'three':753C 'transmit':45B,537C 'trigger':675C 'truncat':838C 'two':822C 'two-entri':821C 'tx':107C,214C,376C,490C,497C,633C 'txqueue':124C,347C,481C,503C,559C 'use':252C,343C 'user':9A,29B,619C,679C 'valid':610C,643C 'valu':220C,418C,478C,564C 'vulner':96C 'went':188C 'whenev':611C 'whose':836C 'without':812C 'would':180C 'xmit':129C,135C,156C,162C,179C,401C,408C,512C,557C,808C 'y':699C,704C,709C,714C,719C","nicknames":[],"mitigations":[],"who_affected":"Systems running a Linux kernel with taprio, skbedit and mirred available, where local users can create network namespaces and configure traffic control. Hosts that do not build or load taprio, or that do not let untrusted users gain CAP_NET_ADMIN in a namespace, are much less exposed. The sources do not state which kernel versions are vulnerable.","tier":"B","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-18T16:17:33+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"nvd","external_id":"CVE-2026-98797","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-98797","severity":null,"published_at":"2026-10-18T16:17:33+00:00","modified_at":"2026-10-19T10:19:43+00:00","first_seen_at":"2026-10-23T21:57:16+00:00","fetched_at":"2026-10-23T22:28:56+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}