Saltar al contenido principal

Fuzzing

Fuzzing es el proceso de proporcionar datos aleatorios a los programas para identificar comportamientos inesperados, como bloqueos y pánicos.

Las pruebas de fuzz también se pueden escribir como pruebas de propiedades que, en lugar de buscar pánicos y bloqueos, afirman que alguna propiedad permanece verdadera. El fuzzing, como se demuestra aquí y en otros lugares de estos documentos, utilizará principios de tanto la prueba de propiedades como el fuzzing, pero solo usará el término fuzzing para referirse a ambos.

Los siguientes pasos se pueden utilizar en cualquier espacio de trabajo de contrato Stellar. Si estás experimentando, pruébalos en el ejemplo de incremento. El contrato tiene una función increment que aumenta el valor de un contador en uno en cada invocación.

Cómo Escribir Pruebas de Fuzz

  1. Instala cargo-fuzz.

    cargo install --locked cargo-fuzz
  2. Inicializa un proyecto de fuzz ejecutando el siguiente comando dentro de tu directorio de contrato.

    cargo fuzz init
  3. Abre el archivo Cargo.toml del contrato. Agrega lib como un crate-type.

     [lib]
    -crate-type = ["cdylib"]
    +crate-type = ["lib", "cdylib"]
  4. Abre el archivo generado fuzz/Cargo.toml. Agrega la dependencia soroban-sdk.

     [dependencies]
    libfuzzer-sys = "0.4"
    +soroban-sdk = { version = "*", features = ["testutils"] }
  5. Abre el archivo generado fuzz/src/fuzz_target_1.rs. Se verá como el de abajo.

    #![no_main]
    use libfuzzer_sys::fuzz_target;

    fuzz_target!(|data: &[u8]| {
    // fuzzed code goes here
    });
  6. Completa la llamada fuzz_target! con la configuración de prueba y las afirmaciones. Por ejemplo, para el ejemplo de incremento:

    #![no_main]
    use libfuzzer_sys::fuzz_target;
    use soroban_increment_with_fuzz_contract::{IncrementContract, IncrementContractClient};
    use soroban_sdk::{
    testutils::arbitrary::{arbitrary, Arbitrary},
    Env,
    };

    #[derive(Debug, Arbitrary)]
    pub struct Input {
    pub by: u64,
    }

    fuzz_target!(|input: Input| {
    let env = Env::default();
    let id = env.register(IncrementContract, ());
    let client = IncrementContractClient::new(&env, &id);

    let mut last: Option<u32> = None;
    for _ in input.by.. {
    match client.try_increment() {
    Ok(Ok(current)) => assert!(Some(current) > last),
    Err(Ok(_)) => {} // Expected error
    Ok(Err(_)) => panic!("success with wrong type returned"),
    Err(Err(_)) => panic!("unrecognised error"),
    }
    }
    });
  7. Ejecuta el objetivo de fuzz.

    cargo +nightly fuzz run --sanitizer=thread fuzz_target_1
    información

    Si estás desarrollando en macOS, necesitas agregar el flag --sanitizer=thread para solucionar una incidencia conocida.

Esta prueba utiliza los mismos patrones usados en pruebas unitarias y pruebas de integración:

  1. Crea un entorno, el Env.
  2. Registra el contrato que se va a probar.
  3. Invoca funciones usando un cliente.
  4. Afirmar expectativas.
consejo

Para un ejemplo completo y detallado, consulta el ejemplo de fuzzing.

información

Hay otra herramienta para fuzzing de código Rust, cargo-afl. Consulta el [Rust Fuzz book] para un tutorial sobre cómo usarlo.

Cómo obtener la cobertura de código de las pruebas de fuzz

Obtener datos de cobertura de código para las pruebas de fuzz requiere herramientas diferentes a las que se utilizan para hacer lo mismo con las pruebas regulares de Rust.

  1. Ejecuta las pruebas de fuzz hasta que produzca un corpus, como en el paso 7 anterior.

    cargo +nightly fuzz run --sanitizer thread fuzz_target_1
  2. Instala las herramientas llvm para el compilador nocturno.

    rustup component add --toolchain nightly llvm-tools-preview
  3. Ejecuta el comando de cobertura de fuzz que ejecutará el corpus y escribirá los datos de cobertura en el directorio de cobertura en el formato profdata.

    cargo +nightly fuzz coverage --sanitizer thread fuzz_target_1
  4. Ejecuta el comando llvm-cov para convertir el archivo profdata a un archivo lcov.

    LLVM_TOOLS_PATH=$(dirname $(find $(rustc +nightly --print sysroot) -name 'llvm-cov'))
    $LLVM_TOOLS_PATH/llvm-cov export -instr-profile=fuzz/coverage/fuzz_target_1/coverage.profdata \
    -format=lcov \
    -object target/aarch64-apple-darwin/coverage/aarch64-apple-darwin/release/fuzz_target_1 \
    --ignore-filename-regex "rustc" > lcov.info

    Carga el archivo lcov.info en tu IDE utilizando su función de cobertura. En VSCode, esto se puede hacer instalando la extensión [Coverage Gutters] y ejecutando el comando Coverage Gutters: Watch.

consejo

Para medir la cobertura de código de pruebas regulares de Rust, consulta Cobertura de Código.