
Macros Rust e subcomando do Cargo para automatizar fuzzing com afl.rs, incluindo geração de corpus e implementação de harness, integrado ao framework de testes do Rust.
test-fuzz é um subcomando do Cargo e uma coleção de macros Rust para automatizar certas tarefas relacionadas a fuzzing com [afl.rs], incluindo:
test-fuzz realiza essas tarefas (em parte) usando os recursos de teste do Rust. Por exemplo, para gerar um corpus de fuzzing, o test-fuzz registra os argumentos de um alvo cada vez que ele é chamado durante uma invocação de cargo test. Da mesma forma, o test-fuzz implementa um harness de fuzzing como um teste adicional em um binário gerado por cargo-test. Essa integração estreita com os recursos de teste do Rust é o que motiva o nome test-fuzz.
Conteúdo
test_fuzz macro]test_fuzz_impl macro]cargo test-fuzz]test-fuzz]Instale o cargo-test-fuzz e o [afl.rs] com o seguinte comando:```sh
cargo install cargo-test-fuzz cargo-afl
## Visão geral
Fuzzing com `test-fuzz` consiste essencialmente em três etapas:\*
1. **Identifique um alvo de fuzzing**:
- Adicione as seguintes `dependencies` ao arquivo `Cargo.toml` do crate alvo:
```toml
serde = "*"
test-fuzz = "*"
```
- Preceda a função alvo com a macro [`test_fuzz`]:
```rust
#[test_fuzz::test_fuzz]
fn foo(...) {
...
}
```
2. **Gere um corpus** executando `cargo test`: ```
cargo test
cargo test-fuzz]: ```
cargo test-fuzz foo
* Uma etapa adicional e preliminar pode ser necessária após uma reinicialização:```sh cargo afl system-config
Note que o comando acima executa `sudo` internamente. Portanto, você pode ser solicitado a inserir sua senha.
## Componentes
### Macro `test_fuzz`
Preceder uma função com a macro `test_fuzz` indica que a função é um alvo de fuzzing.
Os efeitos principais da macro `test_fuzz` são:
- Adiciona instrumentação ao alvo para serializar seus argumentos e escrevê-los em um arquivo de corpus cada vez que o alvo for chamado. A instrumentação é condicionada por `#[cfg(test)]` de modo que os arquivos de corpus são gerados apenas ao executar testes (no entanto, veja [`enable_in_production`] abaixo).
- Adiciona um teste para ler e desserializar argumentos da entrada padrão e aplicar o alvo a eles. O teste verifica uma variável de ambiente, definida por [`cargo test-fuzz`], para que o teste não bloqueie ao tentar ler da entrada padrão durante uma invocação normal de `cargo test`. O teste é encapsulado em um módulo para reduzir a probabilidade de colisão de nomes. Atualmente, o nome do módulo é `target_fuzz`, onde `target` é o nome do alvo (no entanto, veja [`rename`] abaixo).
#### Argumentos
##### `bounds = "where_predicates"`
Imponha `where_predicates` (por exemplo, limites de trait) na struct usada para serializar/desserializar argumentos. Isso pode ser necessário, por exemplo, se o tipo de argumento de um alvo for um tipo associado. Para um exemplo, veja [associated_type.rs] neste repositório.
##### `generic_args = "parameters"`
Use `parameters` como os parâmetros de tipo do alvo ao realizar fuzzing. Exemplo:```rust
#[test_fuzz(generic_args = "String")]
fn foo<T: Clone + Debug + Serialize>(x: &T) {
...
}
Nota: Os argumentos do alvo devem ser serializáveis para cada instanciação de seus parâmetros de tipo. Mas os argumentos do alvo são necessários desserializáveis apenas quando o alvo é instanciado com parameters.
impl_generic_args = "parameters"Use parameters como os parâmetros de tipo Self do alvo ao fazer fuzzing. Exemplo:```rust
#[test_fuzz_impl]
impl<T: Clone + Debug + Serialize> for Foo {
#[test_fuzz(impl_generic_args = "String")]
fn bar(&self, x: &T) {
...
}
}
Nota: os argumentos do alvo devem ser serializáveis para **cada** instanciação de seus parâmetros de tipo `Self`. Porém, os argumentos do alvo só precisam ser desserializáveis quando o `Self` do alvo for instanciado com `parameters`.
##### `convert = "X, Y"`
Ao serializar os argumentos do alvo, converta valores do tipo `X` para o tipo `Y` usando a implementação de `From<X>` em `Y`, ou do tipo `&X` para o tipo `Y` usando a implementação do trait não padrão `test_fuzz::FromRef<X>` em `Y`. Ao desserializar, converta esses valores de volta para o tipo `X` usando a implementação do trait não padrão `test_fuzz::Into<X>` em `Y`.
Ou seja, o uso de `convert = "X, Y"` deve ser acompanhado por determinadas implementações. Se `X` implementa [`Clone`], então `Y` pode implementar o seguinte:```rust
impl From<X> for Y {
fn from(x: X) -> Self {
...
}
}
Se X não implementar [Clone], então Y deve implementar o seguinte:```rust
impl test_fuzz::FromRef for Y {
fn from_ref(x: &X) -> Self {
...
}
}
Além disso, `Y` deve implementar o seguinte (independentemente de `X` implementar [`Clone`]):```rust
impl test_fuzz::Into<X> for Y {
fn into(self) -> X {
...
}
}
A definição de test_fuzz::Into é idêntica à de [std::convert::Into]. A razão para usar uma trait não padrão é evitar conflitos que possam surgir de implementações abrangentes de traits padrão.
enable_in_productionGere arquivos de corpus quando não estiver executando testes, desde que a variável de ambiente [TEST_FUZZ_WRITE] esteja definida. O padrão é gerar arquivos de corpus apenas durante a execução de testes, independentemente de [TEST_FUZZ_WRITE] estar definida. Ao executar um alvo fora do diretório do seu pacote, defina [TEST_FUZZ_MANIFEST_PATH] para o caminho do arquivo Cargo.toml do pacote.
AVISO: Definir enable_in_production pode introduzir um vetor de negação de serviço. Por exemplo, definir essa opção para uma função que é chamada muitas vezes com argumentos diferentes pode encher o disco. A verificação de [TEST_FUZZ_WRITE] tem a finalidade de fornecer alguma defesa contra essa possibilidade. Ainda assim, considere essa opção com cuidado antes de usá-la.
execute_with = "function"Em vez de chamar o alvo diretamente:
FnOnce() -> R, onde R é o tipo de retorno do alvo, de modo que chamar o closure chame o alvo;function com o closure.Chamar o alvo dessa maneira permite que function configure o ambiente da chamada. Isso pode ser útil, por exemplo, para fuzzing de [Substrate externalities].
no_auto_generateNão tente [auto-generate corpus files] para o alvo.
only_generic_argsRegistre os argumentos genéricos do alvo durante a execução de testes, mas não gere arquivos de corpus e não implemente um harness de fuzzing. Isso pode ser útil quando o alvo é uma função genérica, mas não está claro quais parâmetros de tipo devem ser usados para fuzzing.
O fluxo de trabalho pretendido é: habilite only_generic_args, depois execute cargo test seguido por cargo test-fuzz --display generic-args. Um dos argumentos genéricos resultantes pode ser utilizável como parameters de generic_args. Da mesma forma, argumentos genéricos resultantes de cargo test-fuzz --display impl-generic-args podem ser utilizáveis como parameters de impl_generic_args.
Observe, no entanto, que o fato de um alvo ter sido chamado com certos parâmetros durante os testes não implica que os argumentos do alvo sejam serializáveis/desserializáveis quando esses parâmetros são usados. Os resultados de --display generic-args/--display impl-generic-args são meramente sugestivos.
rename = "name"