
Explotación para CVE-2021-3679 y documento para su corrección
Este es un programa de prueba de concepto para desencadenar y demostrar un bug lógico en rb_per_cpu_empty. Los programas que alcanzan el bug se quedan colgados en un bucle infinito ocupado en espacio de kernel en tracing_read_pipe y es imposible suspenderlos o terminarlos usando cualquier señal UNIX (incluida SIGKILL).
A pesar de exhibir un comportamiento similar (bucle infinito y consumo de CPU) al bug del cálculo de seq_buf_used corregido en linux 4.5, este programa pretende demostrar la existencia de otro bug con una causa completamente diferente y que se cree que existe en kernels con versiones desde 3.10 hasta 5.14-rc1 (ver también el resultado de ejecución del POC).
DISCLAMER: este programa de prueba de concepto podría bloquear tu linux, consumir mucha energía y no puede ser terminado mediante señales UNIX cuando se activa; úsalo bajo tu propio riesgo.
Estos paquetes o comandos deben estar instalados antes de ejecutar el script de prueba de concepto.
gcc
realpath
nm
También se requieren Bash y comandos de procesamiento de cadenas como awk y , que probablemente ya vengan incluidos en tu distribución.
grepAunque al ser un script de bash, la parte del código que especifica uprobe depende de la plataforma (no podemos usar $argN en uprobe), y soportamos i386, x86_64, arm y aarch64. Siéntete libre de añadir soporte para tu propia plataforma.
Para ejecutar la prueba de concepto, simplemente ejecuta el siguiente comando con privilegios de root:
./rbdetonate

Si el bug es desencadenado por la prueba de concepto, se generará un proceso dd que consume un núcleo de CPU completo, y no puede ser eliminado simplemente usando una señal UNIX como SIGKILL.
El proceso bash del script rbdetonate puede ser eliminado, pero enviar la señal SIGINT podría terminarlo o no, dependiendo de la implementación de bash. Y las versiones más recientes de bash solo pueden ser eliminadas por SIGINT cuando el proceso dd generado está en ejecución.

El rbdetonate podría salir e imprimir Nothing buggy has been detected si no puede crear el proceso dd después de varios reintentos. Sin embargo, esto podría llevar algún tiempo debido a que reintentamos 8192 veces.
| Versión | Reproducido | Captura de pantalla |
|---|---|---|
| 5.14.0-rc2-00479-g86020194bc7e (Fix version) | N | 5.14.0-rc2-00479-g86020194bc7e.png |
| 5.14.0-rc2-00478-g2734d6c1b1a0 (5.14.0-rc2) | Y | 5.14.0-rc2-00478-g2734d6c1b1a0.png |
| 5.4.0-77-generic | Y | 5.4.0-77-generic.png |
| 4.4.0-142-generic | Y | 4.4.0-142-generic.png |
Comentario: este bug existe en teoría en todas las versiones que tienen la función de trazado en espacio de usuario (>=3.10), pero las versiones más antiguas del kernel no tienen uprobe funcionando correctamente en muchos aspectos. Agradeceríamos que pudieras ayudarnos a corregir su comportamiento.
R: Simplemente ejecuta el comando
echo > /sys/kernel/debug/tracing/instances/rbdetonate/trace
con privilegios de root; esto debería sacar al programa del bucle infinito.
R: No. Las herramientas de compilación utilizadas aquí son solo para compilar el programa rbwrite, que produce un resultado determinista al escribir en las páginas del ring buffer en la CPU#0, siguiendo los pasos que hemos diseñado.
De forma trivial para esta prueba de concepto, también se puede usar una versión compilada de un código como el del programa rbwrite, y después se podría recuperar la dirección del tracepoint.
Para usar el trazado de linux, si no te importa el ruido producido por las aplicaciones, añadir kprobe tracepoints a las llamadas al sistema y a las funciones del kernel también generará eventos. Pero para la prueba de concepto, esto podría introducir indeterminismo y no es recomendable.