Alguns dos piores bugs se escondem onde as syscalls não chegam. Pense naquelas helpers internas do kernel — parsers de dados, conversores de formato, decodificadores ASN.1 — que quase nunca são exercitadas diretamente a partir da interface de sistema. O KFuzzTest, um framework leve de fuzzing in-kernel, nasce exatamente para esse ponto cego: ele permite que mantenedores fuzem funções internas in situ com pouquíssima infraestrutura. É o tipo de ferramenta que dá vontade de usar no mesmo dia.
Por quê isso importa? O fuzzing via syscall (ou até com encadeamento de reproduções) muitas vezes pena para “dirigir” o kernel até caminhos estreitos e de baixo estado. O KFuzzTest inverte a lógica: você declara um alvo de fuzz pequenino, ao lado do código que te interessa, conecta com um macro só, e alimenta esse alvo com bytes estruturados vindos do espaço de usuário. Resultado: adoção simples e um caminho direto até o coração do código.
A experiência de quem desenvolve: um macro, um arquivo, pronto
A API gira em torno de um único macro amigável: FUZZ_TEST(nome, tipo_da_struct)
. Você coloca isso num .c
(geralmente em /tests
do subsistema) e escreve o corpo do teste. O framework cuida do resto: cria um arquivo de escrita em /sys/kernel/debug/kfuzztest/<nome>/input
. O que você escrever ali vira a entrada “hidratada” para a sua função. Sem portar nada para userspace, sem “transformar biblioteca”, sem gambiarras de stub — é chamar a helper interna com dados estruturados e pronto.
Para ajudar os fuzzers, o KFuzzTest oferece dois temperos que você pode salpicar no harness:
- Restrições (
KFUZZTEST_EXPECT_*
): pré-condições aplicadas em runtime (ex.: “este ponteiro não pode ser NULL”) e, de quebra, dicas descobertas por ferramentas de userland. - Anotações (
KFUZZTEST_ANNOTATE_*
): pistas sem custo de runtime (ex.: “este campo é o comprimento daquele buffer”, “isto é string”).
Essas informações vão parar em seções ELF dedicadas — .kfuzztest_target
, .kfuzztest_constraint
, .kfuzztest_annotation
— para que ferramentas externas listem alvos e entendam como gerar entradas válidas com mais inteligência.
Debaixo do capô: serialização binária que respeita C

O pulo do gato é o formato binário de entrada. Em vez de alocar um zoológico de objetos no kernel, o KFuzzTest recebe um blob plano e “realoca” em uma estrutura C válida:
- Um cabeçalho de 8 bytes (magic + versão).
- Um array de regiões com offsets/tamanhos das partes lógicas da entrada (pense “struct + buffers apontados”).
- Uma tabela de relocações dizendo “há um ponteiro na região X, offset Y, que deve apontar para a região Z (ou NULL)”.
- O payload: bytes crus de todas as regiões, concatenados e alinhados.
As regiões são ordenadas por offset, cada uma começa com alinhamento adequado ao seu tipo C e a um alinhamento mínimo global, e — ponto crucial — os vãos entre regiões são envenenados (via KASAN) para que acessos OOB virem relatórios precisos. O valor de alinhamento mínimo também é exposto por debugfs, facilitando a vida de quem codifica os blobs do lado de fora. É enxuto e, sobretudo, “no estilo kernel”.
Como preparação, o patchset ainda introduz kasan_poison_range()
para envenenar intervalos arbitrários; isso garante que aqueles “acolchoamentos” entre regiões virem armadilhas perfeitas para o KASAN.
Uma ponte para blob fuzzers (e para humanos)
Não quer escrever um empacotador do zero? O conjunto traz uma ferramenta de espaço de usuário, a kfuzztest-bridge. Você descreve a entrada com uma DSL curtinha, parecida com C (regiões, arrays, ponteiros, campos “len de X”), aponta para um fluxo de bytes (por exemplo, /dev/urandom
ou um corpus), e ela cospe um blob validado para o arquivo …/input
. Ideal para smoke tests e para plugar fuzzers orientados a blob no seu alvo de Linux kernel fuzzing sem dor.
Funciona mesmo? O caso do PKCS#7
Para provar a ideia, os autores injetaram de propósito um off-by-one de leitura no caminho de parsing PKCS#7:
- ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen);
+ ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen + 1);
Depois criaram um alvo KFuzzTest para pkcs7_parse_message
e apontaram um fork de desenvolvimento do syzkaller para ele. Resultado: partindo do zero, o syzkaller estourou o bug dentro de asn1_ber_decoder
em menos de 30 segundos. Experimentos similares em outros alvos também acharam falhas injetadas rapidamente. A moral não é “o syzkaller é bom” (ele é), mas sim que fuzzing direto e ciente de estrutura em caminhos internos fica banal com o KFuzzTest.
O que muda para mantenedores
Se você já pensou “eu fuzaria esse parser se tivesse como alimentá-lo direito”, o KFuzzTest derruba a barreira quase a zero:
- Harnesses rápidos: um macro, uma pequena
struct
de entrada, e você já está chamando o que interessa. - Entradas melhores: restrições e anotações informam o fuzzer sobre formatos e relações — tipo
len == sizeof(buf)
— reduzindo ruído. - Visibilidade de memória: o acolchoamento envenenado entre regiões transforma OOB em relatórios KASAN limpinhos.
- Ganchos para tooling: com alvos e dicas visíveis nas seções
.kfuzztest_*
, ferramentas conseguem listar tudo e gerar blobs alinhados e bem-formados de maneira consistente.
Importante: KFuzzTest é para debug. Ele expõe funções internas ao userland via debugfs e não deve aparecer em builds de produção. A documentação reforça isso e recomenda habilitar KASAN/KCOV para ter o máximo proveito.
Onde isso se encaixa no ecossistema
O KFuzzTest não substitui o fuzzing por syscall; ele complementa. Syscalls continuam essenciais para cobrir integração, lifetimes e interações transversais. Mas para a classe “agulha no parser”, este framework dá uma ferramenta cirúrgica, barata de manter, fácil de rodar em CI e fácil de evoluir conforme o código muda. É um passo pequeno no esforço de Linux kernel fuzzing, mas com alto impacto prático: mais caminhos internos cobertos, mais cedo, com menos atrito.