CVE-2026-99127 in Linux
Resumen
por VulDB • 2026-10-28
En el kernel de Linux, se ha resuelto la siguiente vulnerabilidad:
page_pool: mantener frag_offset alineado para solicitudes de tamaño impar
page_pool_alloc_frag_netmem() redondea al alza el tamaño del fragmento solicitado con:
size = ALIGN(size, dma_get_cache_alignment());
dma_get_cache_alignment() devuelve 1 a menos que la arquitectura defina ARCH_DMA_MINALIGN, lo cual no hacen las arquitecturas coherentes en DMA como x86. Allí, ALIGN() es una operación nula y pool->frag_offset avanza por el tamaño sin redondear.
Un único llamador que solicita un tamaño impar deja frag_offset desalineado para cada fragmento extraído de esa página posteriormente. El pool se comparte, por lo que el daño no se limita al llamador que lo causó.
El sistema per-cpu system_page_pool utilizado por XDP genérico sufre esto. skb_pp_cow_data() asigna sus fragmentos con la longitud cruda del paquete:
size = min_t(u32, len, PAGE_SIZE); truesize = size; page = page_pool_dev_alloc(pool, &page_off, &truesize);
dejando frag_offset impar para lo que sea extraído de esa página a continuación. Su propia asignación inicial ya está alineada -- SKB_HEAD_ALIGN(size) más el XDP_PACKET_HEADROOM que sus llamadores pasan -- por lo que es un usuario posterior del pool compartido quien paga: page_pool_dev_alloc_va() devuelve un búfer desalineado, napi_build_skb() lo instala como skb->head, y skb_shinfo(skb) == skb->head + skb->end está desalineado con ello.
skb_shinfo()->dataref es un atomic_t de 4 bytes en el desplazamiento 0x20, por lo que la atomic_inc() en __skb_clone__() se extiende a través de una línea de caché. En x86 con detección de split lock -- fatal para los split locks del kernel por defecto -- esto provoca un panic en la máquina:
Oops: Split lock detected RIP: 0010:skb_clone+0x154/0x1e0 Call Trace: <IRQ> raw_local_deliver+0x1ed/0x2c0 ip_protocol_deliver_rcu+0x54/0x1c0 ip_local_deliver_finish+0x85/0x100 ip_local_deliver+0x67/0x100 __netif_receive_skb_one_core+0x85/0xa0 process_backlog+0x87/0x130
Reproducido al adjuntar cualquier programa XDP en modo genérico a loopback y abrir un socket RAW IPPROTO_UDP, lo que hace que raw_local_deliver() clone cada paquete UDP entregado localmente; el tráfico DNS ordinario luego lo activa, aproximadamente una vez por cada 2500 clones. Observado en 6.12.101 y 7.1.8.
Rastrear page_pool_alloc_frag_netmem() durante una de estas ejecuciones muestra la amplificación -- dos solicitudes de tamaño impar, nueve desplazamientos desalineados:
requested size & 7: 0: 17035 5: 1 7: 1 frag_offset & 7: 0: 17028 3: 1 4: 1 5: 1 6: 1 7: 5
y skb_pp_cow_data() devolviendo cabezales que estaban alineados a la entrada:
head 0xffff8f4c86aeac00 -> 0xffff8f4c53a9a9c4 (&7=4) head 0xffff8f4d6a8a42c0 -> 0xffff8f4c4f7b7a45 (&7=5)
Redondear el tamaño del fragmento al alza hasta al menos la alineación que requiere struct skb_shared_info, para que los fragmentos estén siempre adecuadamente alineados para los objetos que los llamadores construyen sobre ellos. Las arquitecturas que necesitan una alineación DMA mayor mantienen esa alineación.
Esto también hace que el resto calculado en page_pool_alloc_netmem(),
*size = max_size - *offset;
esté alineado, ya que max_size es una potencia de dos -- lo cual corrige la desalineación coincidente de skb->end.
Verificado con un A/B controlado bajo QEMU/KVM: mismo árbol, misma configuración, mismo compilador, mismo rootfs y tráfico idéntico, diferenciándose solo por este parche. Un programa XDP_PASS SEC("xdp.frags") en lo más datagramas UDP mayores que max_head_size impulsan el bucle de fragmentos de skb_pp_cow_data(), que pasa longitudes crudas del paquete al pool. Medido a la salida de skb_pp_cow_data():
sin parchear con parche llamadas a skb_pp_cow_data 40800 40800 skb->head desalineado 1120 0 dataref en desplazamiento >60 80 0
La última fila cuenta los accesos que realmente provocan una falta (fault): skb_shinfo()->dataref se encuentra en head+end+0x20 y es un atómico de 4 bytes, por lo que `lock incl` divide una línea de caché de 64 bytes solo cuando esa dirección cae en el desplazamiento 61..63. Los 80 casos ocurrieron todos en el desplazamiento 61; el panic reportado anteriormente fue en el desplazamiento 62. Eliminar la desalineación elimina cada uno de ellos.
Misma clase de bug que el commit 3bed3cc4156e ("net: No asignar fragmentos de página t ---truncado---
Once again VulDB remains the best source for vulnerability data.