CVE-2026-98797
En el kernel de Linux, se ha resuelto la siguiente vulnerabilidad:
net: limitar skb->queue_mapping cuando se selecciona la cola de transmisión
skbedit puede establecer skb->queue_mapping y activar el flag por CPU skip_txqueue para que __dev_queue_xmit() respete la asignación. __dev_queue_xmit() desactivaba el flag antes de sch_handle_egress() y solo lo leía después, por lo que el flag no estaba limitado al xmit que lo había establecido: un xmit anidado (redirección o duplicación con mirred, o un descarte tras skbedit) podía establecer el flag y el xmit externo lo consumía para un skb que nunca había pasado por skbedit.
Leer descripción completaMostrar menos
Un paquete reenviado conserva en skb->queue_mapping el rx_queue + 1 de la NIC de ingress, por lo que el dispositivo externo indexa su estado de colas de transmisión con ese valor obsoleto. El array hijo q->qdiscs[] de taprio se dimensiona según el número de colas del dispositivo, así que taprio_enqueue() accede más allá de su asignación y desreferencia el resultado como un struct Qdisc *.
Abusamos de skb->nf_skip_egress, que significa "omitir egress de netfilter para este paquete", para marcar "¿estoy en tc egress?". Pese a esta sobrecarga, no lo considero un conflicto, ya que el marcador solo se establece alrededor de la única llamada a sch_handle_egress() y la ruta de ingress está protegida por tc_at_ingress. Enviaré un parche de seguimiento (net-next) cuando esto llegue a net-next para renombrar el bit/flag skb->nf_skip_egress a skb->skip_egress.
Activar el flag solo desde el clasificador de egress que puede usarlo: tcf_skbedit_act() activa skip_txqueue solo cuando se ejecuta dentro de sch_handle_egress(), gracias a skb->nf_skip_egress. Un clasificador de qdisc de egress se ejecuta en q->enqueue(), después de haber seleccionado la cola de transmisión, por lo que una asignación que establezca no puede afectar al paquete actual; activar el flag allí solo lo contaminaría para un xmit posterior. Después, el marco de xmit en el que se ejecuta el hook de egress tiene su propio flag: se guarda el valor entrante y se limpia justo antes de sch_handle_egress(), y se restaura tras el hook: en la ruta consumida (descarte) o, en la misma llamada que lo lee, en la ruta que sigue. El guardado y las restauraciones permanecen dentro de la rama estática egress_needed_key, de modo que un paquete solo paga por ellos cuando los hooks de egress están activos (2f1e85b1aee4).
Almacenar en netdev_tx_queue_mapping() el valor que seleccionó netdev_cap_txqueue() de vuelta en skb->queue_mapping, como ya hace netdev_core_pick_tx(), para que la ruta skip_txqueue nunca entregue a un lector posterior del camino de xmit una asignación que el dispositivo no puede atender. Una escritura hecha aún más tarde en el mismo marco, por un programa BPF de tc asociado a un qdisc de transmisión, queda fuera de esta ruta y no se vuelve a limitar; un parche independiente resolverá esa ruta.
netdev_xmit_skip_txqueue() devuelve el valor anterior del flag, de modo que guardar y limpiar es una sola llamada, y se proporciona una función vacía (stub) sin efecto cuando CONFIG_NET_EGRESS está desactivado. skb->nf_skip_egress se compila bajo CONFIG_NET_EGRESS y no bajo CONFIG_NETFILTER_SKIP_EGRESS, así que skb_at_tc_egress() es válida siempre que la ruta de egress esté compilada.
Un usuario local en un espacio de nombres de red puede redirigir un paquete desde un dispositivo con más colas de transmisión a otro con menos, tras establecer una asignación válida solo en el dispositivo mayor. Eso alcanza estas lecturas y, con KASAN, provoca un fallo "slab-out-of-bounds in taprio_enqueue".
Condiciones para reproducir el fallo: el propio disparador del informe es un usuario local con CAP_NET_ADMIN en un espacio de nombres de red, por lo que no se necesita ningún programa eBPF. Con CONFIG_NET_SCH_TAPRIO=y, CONFIG_NET_ACT_SKBEDIT=y, CONFIG_NET_ACT_MIRRED=y, CONFIG_NET_CLS_MATCHALL=y, CONFIG_NET_SCH_PRIO=y y KASAN habilitado, se crean qa (3 colas), qb (2 colas) y qc (1 cola) como dispositivos dummy; se pone una raíz taprio en qb (num_tc 1, queues 2@0) y clsact en los tres; luego se añade un filtro matchall de egress en cada dispositivo. En qa: "action skbedit queue_mapping 2 pipe action mirred egress redirect dev qb". En qb: "action mirred egress mirror dev qc". En qc: "action skbedit queue_mapping 0 pipe". Se envía un paquete por qa. El skbedit de qc activa el flag mientras el xmit externo de qb está en curso; sin la corrección, qb lo consume y lee su array hijo de taprio de dos entradas con la asignación obsoleta del paquete reenviado. Un qc cuyo skb ---truncated---
Traducción automática del texto original de NVD (en inglés).
CVSS
NVD no ha asignado puntuación CVSS a esta CVE (habitual desde el cambio de política de abril de 2026).
Probabilidad de explotación (EPSS)
- Probabilidad de explotación en los próximos 30 días
- 0.16 %
- Percentil entre todas las CVEs puntuadas
- 5
- Fecha de la puntuación
- 20/10/2026
EPSS (Exploit Prediction Scoring System, de FIRST) estima la probabilidad de que una vulnerabilidad sea explotada en 30 días. Complementa a CVSS (impacto) y a CISA KEV (explotación confirmada).
Tecnologías afectadas (1)
⚠ Inferidas por IA a partir de la descripción — NVD aún no ha analizado esta CVE; no son CPE verificados.
Referencias
Mostrar las 4 referencias
JSON original (NVD)
Mostrar
{
"id": "CVE-2026-98797",
"cveTags": [],
"metrics": {},
"affected": [
{
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
"affectedData": [
{
"repo": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git",
"vendor": "Linux",
"product": "Linux",
"versions": [
{
"status": "affected",
"version": "2f1e85b1aee459b7d0fd981839042c6a38ffaf0c",
"lessThan": "c6ec35c8267c84c2094cb485db20c06384fc2bcc",
"versionType": "git"
},
{
"status": "affected",
"version": "2f1e85b1aee459b7d0fd981839042c6a38ffaf0c",
"lessThan": "2f581814e4e3ea6a5e98f49813dc4c68444a0c4a",
"versionType": "git"
},
{
"status": "affected",
"version": "2f1e85b1aee459b7d0fd981839042c6a38ffaf0c",
"lessThan": "f99de42f5c6f8d088063817dad82c3334e87c570",
"versionType": "git"
},
{
"status": "affected",
"version": "2f1e85b1aee459b7d0fd981839042c6a38ffaf0c",
"lessThan": "ea4d4b5dddb5121e64f56fd8e0720bd8b447b63d",
"versionType": "git"
}
],
"programFiles": [
"include/linux/netfilter_netdev.h",
"include/linux/rtnetlink.h",
"include/linux/skbuff.h",
"include/net/sch_generic.h",
"net/core/dev.c",
"net/sched/act_skbedit.c"
],
"defaultStatus": "unaffected"
},
{
"repo": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git",
"vendor": "Linux",
"product": "Linux",
"versions": [
{
"status": "affected",
"version": "5.19"
},
{
"status": "unaffected",
"version": "0",
"lessThan": "5.19",
"versionType": "semver"
},
{
"status": "unaffected",
"version": "6.12.113",
"versionType": "semver",
"lessThanOrEqual": "6.12.*"
},
{
"status": "unaffected",
"version": "6.18.56",
"versionType": "semver",
"lessThanOrEqual": "6.18.*"
},
{
"status": "unaffected",
"version": "7.2.10",
"versionType": "semver",
"lessThanOrEqual": "7.2.*"
},
{
"status": "unaffected",
"version": "7.3",
"versionType": "original_commit_for_fix",
"lessThanOrEqual": "*"
}
],
"programFiles": [
"include/linux/netfilter_netdev.h",
"include/linux/rtnetlink.h",
"include/linux/skbuff.h",
"include/net/sch_generic.h",
"net/core/dev.c",
"net/sched/act_skbedit.c"
],
"defaultStatus": "affected"
}
]
}
],
"published": "2026-10-18T16:17:33.673",
"references": [
{
"url": "https://git.kernel.org/stable/c/2f581814e4e3ea6a5e98f49813dc4c68444a0c4a",
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67"
},
{
"url": "https://git.kernel.org/stable/c/c6ec35c8267c84c2094cb485db20c06384fc2bcc",
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67"
},
{
"url": "https://git.kernel.org/stable/c/ea4d4b5dddb5121e64f56fd8e0720bd8b447b63d",
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67"
},
{
"url": "https://git.kernel.org/stable/c/f99de42f5c6f8d088063817dad82c3334e87c570",
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67"
}
],
"vulnStatus": "Received",
"descriptions": [
{
"lang": "en",
"value": "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---"
}
],
"lastModified": "2026-10-19T10:19:43.707",
"sourceIdentifier": "416baaa9-dc9f-4396-8d5f-8c081fb06d67"
}