
Exploração para CVE-2021-3679 e documento para sua correção
Este é um programa de prova de conceito para acionar e provar um bug lógico em rb_per_cpu_empty. Programas que atingem o bug ficam presos em um loop infinito ocupado no espaço do kernel em tracing_read_pipe e é impossível suspendê-los ou terminá-los usando qualquer sinal UNIX (incluindo SIGKILL).
Apesar de exibir comportamento semelhante (loop infinito e consumo intenso de CPU) ao bug do cálculo de seq_buf_used corrigido no linux 4.5 (link), este programa visa provar a existência de outro bug com causa completamente diferente, que se acredita existir em kernels com versões de 3.10 a 5.14-rc1 (veja também o resultado de execução do POC).
AVISO: este programa de prova de conceito pode travar o seu linux, consumir muita energia e não pode ser encerrado usando sinal UNIX quando o bug for acionado. Use por sua conta e risco.
Estes pacotes ou comandos devem estar instalados antes de executar o script de prova de conceito.
gcc
realpath
nm
Comandos Bash e de processamento de strings, como awk e grep, também são necessários e provavelmente já vêm incluídos na sua distribuição.
Embora seja um script bash, a parte do código que especifica uprobe é dependente de plataforma (não podemos usar $argN em uprobe), e suportamos i386, x86_64, arm e aarch64. Sinta-se à vontade para adicionar suporte à sua própria plataforma.
Para executar a prova de conceito, basta executar o seguinte comando com privilégios de root:
./rbdetonate

Se o bug for acionado pela prova de conceito, um processo dd consumindo um núcleo inteiro de CPU será criado, e ele não pode ser morto apenas usando sinal UNIX, como SIGKILL.
O processo bash do script rbdetonate pode ser morto, mas enviar o sinal SIGINT pode terminá-lo ou não, dependendo da implementação do bash. E versões mais novas do bash só podem ser mortas por SIGINT quando o processo dd criado está em execução.

O rbdetonate pode sair e imprimir Nothing buggy has been detected se não conseguir criar o processo dd após várias tentativas. No entanto, isso pode levar algum tempo devido às nossas 8192 tentativas.
Comentário: este bug existe em todas as versões com o recurso de rastreamento em espaço de usuário (>=3.10) em teoria, mas versões mais antigas do kernel não têm o uprobe funcionando corretamente de muitas maneiras. Agradeceríamos se você pudesse nos ajudar a corrigir seu comportamento.
A: Basta executar o comando echo > /sys/kernel/debug/tracing/instances/rbdetonate/trace com privilégios de root para tirar o programa do loop infinito.
A: Não. As ferramentas de compilação usadas aqui servem apenas para compilar o programa rbwrite, que produz um resultado determinístico ao escrever apenas nas páginas do ring buffer na CPU#0, seguindo os passos que projetamos.
Trivialmente, para esta prova de conceito, uma versão compilada do programa rbwrite (como código) também pode ser usada, e o endereço do tracepoint poderia ser obtido depois disso.
Para usar o rastreamento do linux, se você não se importar com o ruído produzido pelas aplicações, adicionar tracepoints kprobe a syscalls e funções do kernel também gerará eventos. Mas para a prova de conceito, isso pode introduzir indeterminismo e não é recomendado.
| Versão | Reproduzido | Captura de tela |
|---|
| 5.14.0-rc2-00479-g86020194bc7e (Versão corrigida) | N | 5.14.0-rc2-00479-g86020194bc7e.png |
| 5.14.0-rc2-00478-g2734d6c1b1a0 (5.14.0-rc2) | S | 5.14.0-rc2-00478-g2734d6c1b1a0.png |
| 5.4.0-77-generic | S | 5.4.0-77-generic.png |
| 4.4.0-142-generic | S | 4.4.0-142-generic.png |