Saltar al contenido principal

Fuzzing

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

Las pruebas de fuzz también pueden escribirse como pruebas de propiedades que, en lugar de buscar identificar pánicos y caídas, afirman que alguna propiedad se mantenga verdadera. El fuzzing, demostrado aquí y en otros lugares de estos documentos, utiliza principios tanto de pruebas de propiedades como de fuzzing, pero solo usará el término fuzzing para referirse a ambos.

Los siguientes pasos pueden usarse 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 un valor contador en uno en cada invocación.

Cómo escribir pruebas de fuzzing

  1. Instala la toolchain nightly de Rust. Rust nightly es necesario para ejecutar cargo-fuzz.

    rustup install nightly
  2. Instala cargo-fuzz.

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

    cargo fuzz init
  4. Abre el archivo Cargo.toml del contrato. Agrega lib como tipo de crate.

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

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

    #![no_main]
    use libfuzzer_sys::fuzz_target;

    fuzz_target!(|data: &[u8]| {
    // fuzzed code goes here
    });
  7. Completa la llamada fuzz_target! con la configuración de la prueba y las aserciones. 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"),
    }
    }
    });
  8. Ejecuta el objetivo de fuzz.

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

    Si estás desarrollando en MacOS, necesitas agregar la bandera --sanitizer=thread para solucionar un problema conocido.

Esta prueba usa los mismos patrones usados en pruebas unitarias e [integración]:

  1. Crea un ambiente, el Env.
  2. Registra el contrato que será probado.
  3. Invoca funciones usando un cliente.
  4. Afirma las expectativas.
consejo

Para un ejemplo detallado completo, ve 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 usarla.

Cómo obtener cobertura de código de pruebas de fuzzing

Obtener datos de cobertura de código para pruebas de fuzz requiere herramientas diferentes que al hacerlo para pruebas regulares de Rust.

  1. Ejecuta las pruebas de fuzz hasta que produzcan un corpus, igual que en el paso 7 arriba.

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

    rustup component add --toolchain nightly llvm-tools-preview
  3. Ejecuta el comando de cobertura fuzz que ejecutará el corpus y escribirá datos de cobertura en el directorio coverage en 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.

    $(find $(rustc --print sysroot) -name llvm-cov) export \
    -instr-profile=fuzz/coverage/fuzz_target_1/coverage.profdata \
    -object target/$(rustc -vV | sed -n 's|host: ||p')/coverage/$(rustc -vV | sed -n 's|host: ||p')/release/fuzz_target_1 \
    --ignore-filename-regex "rustc" \
    -format=lcov \
    > lcov.info

    Carga el archivo lcov.info en tu IDE usando su función de cobertura. En VSCode esto puede hacerse 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, ve Cobertura de código.