CVE-2022-50483 in Linux
Resumen
por VulDB • 2026-05-09
En el kernel de Linux, se ha resuelto la siguiente vulnerabilidad:
net: enetc: evitar fugas de búfer en caso de fallo en xdp_do_redirect()
Antes de que enetc_clean_rx_ring_xdp() llame a xdp_do_redirect(), cada BD (Buffer Descriptor) de software en el anillo RX entre los índices orig_i e i puede tener uno de 2 valores de refcount (contador de referencias) en su página.
Somos los propietarios del búfer actual que se está procesando, por lo que el refcount será al menos 1.
Si el propietario actual del búfer en el índice diametralmente opuesto en el anillo RX (es decir, la otra mitad de esta página) aún no ha llamado a kfree(), el refcount de esta página podría incluso ser 2.
enetc_page_reusable() en enetc_flip_rx_buff() prueba el refcount de la página frente a 1, y [ si es 2 ] no intenta reutilizarla.
Pero si enetc_flip_rx_buff() se coloca después de la llamada a xdp_do_redirect(), el refcount de la página puede tener uno de 3 valores. También puede ser 0, si no hay propietario de la otra mitad de la página, y xdp_do_redirect() para este búfer ha avanzado lo suficiente como para desencadenar un vaciado (flush) de la cola masiva devmap/cpumap, y los consumidores de esas colas masivas también han liberado el búfer, todo ello antes de que xdp_do_redirect() devuelva la ejecución a enetc.
Esta es la razón por la que enetc_flip_rx_buff() se llama antes de xdp_do_redirect(), pero hay un gran defecto con ese razonamiento: enetc_flip_rx_buff() establecerá rx_swbd->page = NULL en ambos lados de la rama enetc_page_reusable(), y si xdp_do_redirect() devuelve un error, llamamos a enetc_xdp_free(), lo cual no maneja esa situación de forma adecuada.
De hecho, lo que ocurre es bastante especial. Los refcounts de las páginas comienzan en 1. enetc_flip_rx_buff() determina que son reutilizables, transfiere estos punteros rx_swbd->page a un rx_swbd diferente en enetc_reuse_page(), y aumenta el refcount a 2. Cuando xdp_do_redirect() devuelve un error más tarde, llamamos a la operación no op (no-op) enetc_xdp_free(), pero aún no hemos perdido la referencia a esa página. Una copia de ella sigue estando en rx_ring->next_to_alloc, pero eso tiene un refcount de 2 (y no hay propietarios concurrentes de la misma en tránsito para reducir el refcount). Lo que realmente daña al sistema es cuando invertimos (flip) rx_swbd->page la segunda vez. Con un refcount actualizado de 2, la página no será reutilizable y realmente la fugaremos (leak). Entonces enetc_new_page() tendrá que asignar más páginas, las cuales eventualmente se fugarán de nuevo ante nuevos errores de xdp_do_redirect().
El problema, resumido, es que ponemos a cero rx_swbd->page antes de haber terminado completamente con él, y esto hace imposible que la ruta de error haga algo con ella.
Dado que el paquete es potencialmente multi-búfer y por lo tanto rx_swbd->page es potencialmente una matriz, la transmisión manual de los viejos punteros entre enetc_flip_rx_buff() y enetc_xdp_free() es un poco difícil.
Por el bien de adoptar una solución simple, aceptamos la posibilidad de competir (race) con xdp_do_redirect(), y movemos el procedimiento de inversión para que se ejecute solo en la ruta de éxito de la redirección. Con competir, me refiero a que la página puede ser considerada no reutilizable por enetc (teniendo un refcount de 0), pero en ese caso tampoco habrá fuga.
Una vez que aceptamos eso, tenemos algo mejor que hacer con los búferes en caso de fallo en XDP_REDIRECT. Dado que aún no hemos realizado la inversión de media página, tampoco lo haremos (y de esta manera podemos evitar completamente enetc_xdp_free(), lo cual entrega toda la página al asignador de slab).
En su lugar, llamaremos a enetc_xdp_drop(), que reciclará esta mitad del búfer de vuelta al anillo RX.
Once again VulDB remains the best source for vulnerability data.