diff --git a/dev/installation/installation/index.html b/dev/installation/installation/index.html index 36196ed0..733a5898 100644 --- a/dev/installation/installation/index.html +++ b/dev/installation/installation/index.html @@ -1,4 +1,4 @@ - Installation - Trident
Skip to content

Installation#

Tip

Docker Image down below.

Important

Prerequisite

It is expected that you have installed:

For supported versions check the Supported Versions

Install System Dependencies#

Update your package list and install the required packages:

sudo apt-get update
+ Installation - Trident      

Installation#

Important

Prerequisite

It is expected that you have installed:

For supported versions check the Supported Versions

Install System Dependencies#

Update your package list and install the required packages:

sudo apt-get update
 sudo apt-get install -y \
     curl \
     git \
@@ -18,4 +18,4 @@
 ``` --> 

Install Trident#

cargo install trident-cli
 

Supported versions#

Trident CLI Anchor Solana Rust Honggfuzz
develop 0.30.1 ^1.17.4 nightly 0.5.56
0.8.0 0.30.1 ^1.17.4 nightly 0.5.56
0.7.0 >=0.29.*1 ^1.17.4 nightly 0.5.56
0.6.0 >=0.29.*1 ^1.17 nightly 0.5.55
0.5.0 ~0.28.* =1.16.6 - -
0.4.0 ~0.27.* >=1.15 - -
0.3.0 ~0.25.* >=1.10 - -
0.2.0 ~0.24.* >=1.9 - -
  1. To use Trident with Anchor 0.29.0, run the following commands from your project's root directory after Trident initialization:
    cargo update anchor-client@0.30.0 --precise 0.29.0
     cargo update anchor-spl@0.30.0 --precise 0.29.0
    -

Docker Image#

TBD

\ No newline at end of file +
\ No newline at end of file diff --git a/dev/search/search_index.json b/dev/search/search_index.json index a252f0a3..acd599c8 100644 --- a/dev/search/search_index.json +++ b/dev/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":"Trident

Rust-based Fuzzing framework for Solana programs to help you ship secure code.

"},{"location":"#what-is-fuzzing","title":"What is Fuzzing ?","text":"

\"Fuzz testing is an automated technique that provides generated random, invalid, or unexpected input data to your program. This helps discover unknown bugs and vulnerabilities, potentially preventing zero-day exploits.\"

"},{"location":"CHANGELOG/","title":"Changelog","text":"

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning (SemVer).

Note: Version 0 of Semantic Versioning is handled differently from version 1 and above. The minor version will be incremented upon a breaking change and the patch version will be incremented for features.

"},{"location":"CHANGELOG/#dev-unreleased","title":"[dev] - Unreleased","text":""},{"location":"CHANGELOG/#080-2024-10-21","title":"[0.8.0] - 2024-10-21","text":"

Added

Changed

Removed

"},{"location":"CHANGELOG/#070-2024-08-14","title":"[0.7.0] - 2024-08-14","text":"

Added

Fixed

Removed

"},{"location":"CHANGELOG/#060-2024-05-20","title":"[0.6.0] - 2024-05-20","text":"

Added

Fixed

"},{"location":"CHANGELOG/#050-2023-08-28","title":"[0.5.0] - 2023-08-28","text":"

Added

"},{"location":"CHANGELOG/#041-2023-08-21","title":"[0.4.1] - 2023-08-21","text":"

Changed

Fixed

"},{"location":"CHANGELOG/#030-2022-09-23","title":"[0.3.0] - 2022-09-23","text":"

Changed

Added

"},{"location":"CHANGELOG/#020-2022-07-27","title":"[0.2.0] - 2022-07-27","text":"

Added

"},{"location":"commands/commands/","title":"Commands","text":""},{"location":"commands/commands/#trident-init","title":"trident init","text":""},{"location":"commands/commands/#trident-how","title":"trident how","text":""},{"location":"commands/commands/#trident-fuzz","title":"trident fuzz","text":""},{"location":"commands/commands/#trident-fuzz-run-hfuzz","title":"trident fuzz run-hfuzz","text":""},{"location":"commands/commands/#output","title":"Output","text":"

Important

The output provided by Honggfuzz is as follows:

  1. Number of Fuzzing Iterations.
  2. Feedback Driven Mode = Honggfuzz generates data based on the feedback (i.e. feedback based on Coverage progress).
  3. Average Iterations per second.
  4. Number of crashes it found (panics or failed invariant checks).
------------------------[  0 days 00 hrs 00 mins 01 secs ]----------------------\n  Iterations : 688 (out of: 1000 [68%]) # -- 1. --\n  Mode [3/3] : Feedback Driven Mode # -- 2. --\n      Target : trident-tests/fuzz_tests/fuzzing.....wn-linux-gnu/release/fuzz_0\n     Threads : 16, CPUs: 32, CPU%: 1262% [39%/CPU]\n       Speed : 680/sec [avg: 688] # -- 3. --\n     Crashes : 1 [unique: 1, blocklist: 0, verified: 0] # -- 4. --\n    Timeouts : 0 [10 sec]\n Corpus Size : 98, max: 1048576 bytes, init: 0 files\n  Cov Update : 0 days 00 hrs 00 mins 00 secs ago\n    Coverage : edge: 10345/882951 [1%] pc: 163 cmp: 622547\n---------------------------------- [ LOGS ] ------------------/ honggfuzz 2.6 /-\n
"},{"location":"commands/commands/#trident-fuzz-debug-hfuzz","title":"trident fuzz debug-hfuzz","text":""},{"location":"commands/commands/#output_1","title":"Output","text":"

Important

The debug output is at current development stage really verbose and contains lldb parts. We are working on improving this experience. In the picture below you can see an example of provided debug output.

  1. Series of Transaction Logs
  2. Structures of data send within the Instructions
  3. Panic or Crash, based on if the Fuzzing panicked within the Solana Program or Invariant Check failed.

"},{"location":"commands/commands/#trident-fuzz-add","title":"trident fuzz add","text":""},{"location":"commands/commands/#trident-clean","title":"trident clean","text":""},{"location":"examples/examples/","title":"Trident by Examples","text":""},{"location":"faq/faq/","title":"FAQ","text":""},{"location":"faq/faq/#is-trident-supported-only-with-anchor","title":"Is Trident supported only with Anchor ?","text":""},{"location":"faq/faq/#i-created-the-fuzz-test-what-should-i-do-next","title":"I created the Fuzz Test what should I do next ?","text":""},{"location":"faq/faq/#my-program-instruction-contains-custom-type-such-as-struct-or-enum-on-its-input-but-it-does-not-derive-arbitrary","title":"My program Instruction contains custom type such as Struct or Enum on its input, but it does not derive Arbitrary.","text":""},{"location":"faq/faq/#is-trident-open-source","title":"Is Trident open-source ?","text":""},{"location":"faq/faq/#i-would-like-to-report-issue-with-trident-what-should-i-do","title":"I would like to report Issue with Trident, what should I do ?","text":""},{"location":"faq/faq/#is-trident-deployed-on-mainnet-devnet-testenet","title":"Is Trident deployed on Mainnet / Devnet / Testenet ?","text":""},{"location":"faq/faq/#what-type-of-fuzzer-trident-is","title":"What type of Fuzzer Trident is ?","text":""},{"location":"features/account-storages/","title":"Account Storages","text":"

Trident allows developers to generate random accounts for fuzzing.

However, the Accounts are not completely random, and neither are the Account addresses.

Instead, Trident generates random AccountIDs which are indexes to Account Storages. Each unique Account contained within the Anchor generated IDL has its own AccountStorage. The FuzzAccounts containing the Accounts Storages is global to all Instructions to use.

Note

Details:

Always generating only random accounts would in most cases lead to a situation where the fuzzer would be stuck because the accounts would be almost every time rejected by your Anchor program. Therefore it is necessary to specify, what accounts should be used and also limit the number of newly created accounts to reduce the space complexity.

Important

Currently, supported types of Account Storages:

Then use the corresponding AccountsStorage.

pub struct FuzzAccounts {\n    signer: AccountsStorage<Keypair>,\n    some_pda: AccountsStorage<PdaStore>,\n    token_vault: AccountsStorage<TokenStore>,\n    mint: AccountsStorage<MintStore>,\n    // ...\n}\n

Tip

Keep in mind:

Tip

Consider checking the Examples section for more tips.

"},{"location":"features/arbitrary-data/","title":"Arbitrary Data","text":"

Trident allows you to customize Instruction Data to provide structure.

For example your Initialize Instruction expects two arguments start_at and end_at you know that in order for the Instruction to make sense, it is required that the start_at < end_at. Moreover, there should be significant difference between these two. This can be utilized with the Arbitrary crate.

#[derive(Arbitrary, Debug)]\npub struct InitVestingData {\n    pub recipient: AccountId,\n    #[arbitrary(\n        with = |u: &mut arbitrary::Unstructured| u.int_in_range(1..=1_000_000)\n    )]\n    pub amount: u64,\n    // we want start_at smaller than end_at\n    // and for testing purposes we can run tests with times from the past\n    #[arbitrary(\n        with = |u: &mut arbitrary::Unstructured| u.int_in_range(0..=1_000_000)\n    )]\n    pub start_at: u64,\n    #[arbitrary(\n        with = |u: &mut arbitrary::Unstructured| u.int_in_range(1_001_001..=1_050_000)\n    )]\n    pub end_at: u64,\n    #[arbitrary(\n        with = |u: &mut arbitrary::Unstructured| u.int_in_range(1..=1000)\n    )]\n    pub interval: u64,\n}\n
"},{"location":"features/arbitrary-data/#implement-arbitrary","title":"Implement Arbitrary","text":"

There are macros available to use with Arbitrary, however, it is possible to Implement the arbitrary function by yourself.

// -------------------------------------------------------------------\n// -------------------------------------------------------------------\n// Implement Arbitrary\nimpl<'a> Arbitrary<'a> for InitVestingData {\n    fn arbitrary(\n        u: &mut arbitrary::Unstructured<'a>\n    ) -> arbitrary::Result<Self> {\n        // obtain AccountId\n        let recipient = AccountId::arbitrary(u)?;\n\n        // limit the generated amount to the 1_000_000\n        let amount = u.int_in_range(1..=1_000_000)?;\n\n        // now we want to obtain\n        // - start_at\n        // - end_at\n        // - interval\n        // however we want to limit the data such that:\n        // - start_at < end_at\n        // - end_at - start_at > interval\n        // - interval has lower limit of 500 and upper limit of 1000.\n\n        let start_at: u64 = u.int_in_range(1_000_000..=5_000_000)?;\n        let end_at: u64 = u.int_in_range(1_000_000..=5_000_000)?;\n        let interval: u64 = u.int_in_range(500..=1000)?;\n\n        // ensure that start_at < end_at\n        if start_at >= end_at {\n            return Err(arbitrary::Error::IncorrectFormat);\n        }\n\n        // ensure that end_at - start_at > interval\n        match end_at.checked_sub(start_at) {\n            Some(diff) => {\n                if diff <= interval {\n                    return Err(arbitrary::Error::IncorrectFormat);\n                }\n            }\n            None => return Err(arbitrary::Error::IncorrectFormat),\n        }\n\n        Ok(InitVestingData {\n            recipient,\n            amount,\n            start_at,\n            end_at,\n            interval,\n        })\n    }\n    // -------------------------------------------------------------------\n    // -------------------------------------------------------------------\n}\n

Tip

Consider checking the Examples section for more tips.

"},{"location":"features/error-handlers/","title":"Error Handler","text":"

Trident allows you to specify custom error handler for each Instruction.

This can be particularly helpful:

Tip

The default behavior of the function is that the error is returned.

/// default implementation\nfn tx_error_handler(\n    &self,\n    e: FuzzClientErrorWithOrigin,\n    ix_data: Self::IxData,\n    pre_ix_acc_infos: &'info mut [Option<AccountInfo<'info>>],\n) -> Result<(), FuzzClientErrorWithOrigin> {\n    Err(e)\n}\n

To omit the Error and continue with the next Instruction in the iteration, you can do

/// default implementation\nfn tx_error_handler(\n    &self,\n    e: FuzzClientErrorWithOrigin,\n    ix_data: Self::IxData,\n    pre_ix_acc_infos: &'info mut [Option<AccountInfo<'info>>],\n) -> Result<(), FuzzClientErrorWithOrigin> {\n    Ok(())\n}\n
"},{"location":"features/features/","title":"Trident Features","text":"

Trident contains multiple features to enhance the fuzzing experience and increase ability to discover bugs.

"},{"location":"features/fuzz-instructions/","title":"Fuzz Instructions","text":"

Trident defines FuzzInstruction enum containing all available Instructions within your program.

The enum variants additionally contains their corresponding structures for Accounts and Input arguments.

#[derive(Arbitrary, DisplayIx, FuzzTestExecutor, FuzzDeserialize)]\npub enum FuzzInstruction {\n    Initialize(Initialize),\n    Update(Update),\n}\n#[derive(Arbitrary, Debug)]\npub struct Initialize {\n    pub accounts: InitializeAccounts,\n    pub data: InitializeData,\n}\n#[derive(Arbitrary, Debug)]\npub struct Update {\n    pub accounts: UpdateAccounts,\n    pub data: UpdateData,\n}\n// ...\n
"},{"location":"features/fuzz-instructions/#instruction-behavior","title":"Instruction behavior","text":"

Each Instruction variant has to define IxOps trait containing the following methods:

"},{"location":"features/fuzz-instructions/#get-program-id","title":"Get Program ID","text":"

This method specifies program ID to which the Instruction corresponds.

In case you have only one program in the Anchor Workspace it is not really important. The importance occurs when you have multiple programs in the Workspace and you want to call Instructions of every Program. In that case each Instruction Variant corresponds to its program by the Program ID.

"},{"location":"features/fuzz-instructions/#get-data","title":"Get Data","text":"

This method specifies what the Instruction Input Data should look like. You can use completely random data generated by the fuzzer, such as:

fn get_data(\n    &self,\n    _client: &mut impl FuzzClient,\n    _fuzz_accounts: &mut FuzzAccounts,\n) -> Result<Self::IxData, FuzzingError> {\n    let data = hello_world::instruction::InitializeFn {\n        input: self.data.input,\n    };\n    Ok(data)\n}\n

You can also use always constant values

fn get_data(\n    &self,\n    _client: &mut impl FuzzClient,\n    _fuzz_accounts: &mut FuzzAccounts,\n) -> Result<Self::IxData, FuzzingError> {\n    let data = hello_world::instruction::InitializeFn {\n        input: 5,\n    };\n    Ok(data)\n}\n

Or you can customize the Data using the Arbitrary crate. Check Arbitrary Data.

"},{"location":"features/fuzz-instructions/#custom-data-types","title":"Custom Data Types","text":"

If you use Custom Types as Instruction data arguments, you may encounter a problem that the Custom Type does not implement

"},{"location":"features/fuzz-instructions/#derive-debug-and-arbitrary-traits-inside-the-fuzz-test","title":"Derive Debug and Arbitrary traits inside the Fuzz Test","text":"

You can redefine the custom type within the fuzz_instructions.rs file, along with all the necessary traits.

// Redefine the Custom Type inside the fuzz_instructions.rs,\n// but this time with all of the required traits.\n#[derive(Arbitrary,Debug, Clone, Copy)]\npub enum CustomEnumInputFuzz {\n    InputVariant1,\n    InputVariant2,\n    InputVariant3,\n}\n

Then, you would also need to implement the std::convert::From<T> trait to enable conversion between the newly defined Custom Type and the Custom Type used within your program.

Tip

Consider checking the Examples section for more tips.

"},{"location":"features/fuzz-instructions/#get-accounts","title":"Get Accounts","text":"

This method specifies how the Accounts for the corresponding Instruction should be resolved. You can use accounts stored within the FuzzAccounts Account Storages, or you can define custom Account using the client.

Important

Source Code below

fn get_accounts(\n    &self,\n    client: &mut impl FuzzClient,\n    fuzz_accounts: &mut FuzzAccounts,\n) -> Result<(Vec<Keypair>, Vec<AccountMeta>), FuzzingError> {\n    let author = fuzz_accounts.author.get_or_create_account(\n        self.accounts.author,\n        client,\n        5 * LAMPORTS_PER_SOL,\n    );\n\n    let hello_world_account = fuzz_accounts\n        .hello_world_account\n        .get_or_create_account(\n            self.accounts.hello_world_account,\n            &[b\"hello_world_seed\"],\n            &hello_world::ID,\n        )\n        .unwrap();\n    let signers = vec![author.clone()];\n    let acc_meta = hello_world::accounts::InitializeContext {\n        author: author.pubkey(),\n        hello_world_account: hello_world_account.pubkey(),\n        system_program: solana_sdk::system_program::ID,\n    }\n    .to_account_metas(None);\n    Ok((signers, acc_meta))\n}\n
"},{"location":"features/fuzz-instructions/#create-an-arbitrary-account","title":"Create an arbitrary account","text":"

The AccountsStorage<T> type provides an implementation of the get_or_create_account method that helps you create new or read already existing accounts. There are different implementations for different types of storage (Keypair, TokenStore, MintStore, PdaStore) to simplify the creation of new accounts.

However, there are cases when the provided implementation is not sufficient and it is necessary to create an account manually. These cases can be (but are not limited to) for example:

In that case, you can use the storage method of the AccountsStorage<T> struct that exposes the underlying HashMap<AccountId, T> and you can add new accounts directly to it.

It is possible to create and store any kind of account. For example:

let state = fuzz_accounts\n    .state\n    // gets the storage of all `state` account variants\n    .storage()\n    // returns the Keypair of the `state` account with\n    // the given `AccountId` if it has been added previously\n    .entry(self.accounts.state)\n    .or_insert_with(|| {\n        let space = State::SIZE;\n        let rent_exempt_lamports = client.get_rent().unwrap()\n                            .minimum_balance(space);\n        let keypair = Keypair::new();\n        let account = AccountSharedData::new_data_with_space::<[u8; 0]>(\n            rent_exempt_lamports,\n            &[],\n            space,\n            &my_program::id(),\n        ).unwrap();\n        // insert the custom account also into the client\n        client.set_account_custom(&keypair.pubkey(), &account);\n        keypair\n    });\n
let rent_exempt_for_token_acc = client\n    .get_rent()\n    .unwrap()\n    .minimum_balance(anchor_spl::token::spl_token::state::Account::LEN);\n\nlet my_pda = fuzz_accounts\n    .my_pda\n    // gets the storage of all `my_pda` account variants\n    .storage()\n    // returns the PdaStore struct of the `my_pda` account with\n    // the given `AccountId` if it has been added previously\n    .entry(self.accounts.my_pda)\n    .or_insert_with(|| {\n        let seeds = &[b\"some-seeds\"];\n        let pda = Pubkey::find_program_address(seeds, &my_program::id()).0;\n        let account = AccountSharedData::new_data_with_space::<[u8; 0]>(\n            rent_exempt_for_token_acc,\n            &[],\n            0,\n            &SYSTEM_PROGRAM_ID,\n        ).unwrap();\n        // insert the custom account also into the client\n        client.set_account_custom(&pda, &account);\n        let vec_of_seeds: Vec<Vec<u8>> = seeds.iter().map(|&seed| seed.to_vec())\n                            .collect();\n        PdaStore {\n            pubkey: pda,\n            seeds: vec_of_seeds,\n        }\n    }).pubkey();\n
"},{"location":"features/fuzz-instructions/#check","title":"Check","text":"

This method provides Invariant Check for the corresponding Instruction. Check Invariant Checks.

"},{"location":"features/fuzz-instructions/#tx-error-handler","title":"Tx Error Handler","text":"

This method provides Tx Error Handler for the corresponding Instruction. Check Error Handler.

"},{"location":"features/fuzz-instructions/#example","title":"Example","text":"

Tip

Consider checking the Examples section for more tips.

"},{"location":"features/fuzzing-statistics/","title":"Fuzzing Statistics","text":"

Trident allows you to see statistics after the fuzzing session ended.

Important

In order to show statistics set fuzzing_with_stats within the Trident.toml to true.

[fuzz]\n# ...\nfuzzing_with_stats = true\n# ...\n
"},{"location":"features/fuzzing-statistics/#available-statistics","title":"Available Statistics","text":""},{"location":"features/fuzzing-statistics/#simple","title":"Simple","text":" Note

Keep in mind that the number of fuzz iterations does not directly correspond to the total number of invocations. In one fuzz iteration, the fuzzer might be unable to deserialize fuzz data into instructions, causing the entire iteration to be skipped.

On the other hand this is expected behavior as the underlying data are randomly (with coverage feedback) generated, so the Honggfuzz will not necessarily find appropriate data each iteration.

Tip

Consider checking the Examples section for more tips.

"},{"location":"features/genesis-accounts/","title":"Genesis","text":""},{"location":"features/genesis-accounts/#genesis-programs","title":"Genesis Programs","text":"

Trident allows you to use Cross Program Invocation of both Native and SBF programs.

"},{"location":"features/genesis-accounts/#native","title":"Native","text":"

In case of multiple programs within the Anchor Workspace. Make sure that all of the programs you would like to call Cross Program Invocation to are included in the initial state of the Fuzz Test Environment.

Important

Source code below:

// test_fuzz.rs\n\nfn fuzz_iteration<T: FuzzTestExecutor<U> + std::fmt::Display, U>(\n    fuzz_data: FuzzData<T, U>\n) {\n    let fuzzing_program_callee = FuzzingProgram::new(\n        PROGRAM_NAME_CALLEE,\n        &PROGRAM_ID_CALLEE,\n        processor!(convert_entry!(entry_callee)),\n    );\n\n    let fuzzing_program_caller = FuzzingProgram::new(\n        PROGRAM_NAME_CALLER,\n        &PROGRAM_ID_CALLER,\n        processor!(convert_entry!(entry_caller)),\n    );\n\n    let mut client =\n        ProgramTestClientBlocking::new(\n            &[fuzzing_program_callee, fuzzing_program_caller],\n            &[]\n        ).unwrap();\n\n    let _ = fuzz_data.run_with_runtime(&mut client);\n}\n
"},{"location":"features/genesis-accounts/#sbf","title":"SBF","text":"

In case of SBF targets, compiled or dumped from whatever cluster. You can also use these within the Fuzz Tests.

Tip

If you want to obtain Program from Mainnet use

# -u m specifies to dump from mainnet\nsolana program dump -u m <PROGRAM_ID> <PROGRAM_NAME>.so\n

Important

[[fuzz.programs]]\naddress = \"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s\"\nprogram = \"metaplex-program/metaplex-token-metadata.so\"\n
"},{"location":"features/genesis-accounts/#genesis-accounts","title":"Genesis Accounts","text":"

Trident allows you to include Accounts with data in base64 format.

Tip

If you want to obtain Account with base64 data format, use

# -u m specifies to dump from mainnet\nsolana account -u m <ADDRESS> --output json\n

Important

To include desired accounts in the fuzz testing environment, add then using the Trident.toml.

[[fuzz.accounts]]\naddress = \"6YG3J7PaxyMnnbU67ifyrgF3BzNzc7cD8hPkqK6ATweE\"\nfilename = \"tests/accounts/core_bridge_mainnet/guardian_set_5_mock.json\"\n

Tip

Consider checking the Examples section for more tips.

"},{"location":"features/instructions-sequences/","title":"Instructions Sequences","text":"

Trident allows you to specify Custom Instruction Sequences you would like to execute.

Possible Instruction sequences are split into 3 parts

For example if you program always needs to start with some kind of Initialization instruction, you can specify this Initialize Instruction in pre_ixs as shown in the source code below.

Tip

// test_fuzz.rs\n\n// do not forget to include the required structures\nuse fuzz_instructions::InitVesting;\nuse fuzz_instructions::WithdrawUnlocked;\n\nimpl FuzzDataBuilder<FuzzInstruction> for MyFuzzData {\n    fn pre_ixs(\n        u: &mut arbitrary::Unstructured\n    ) -> arbitrary::Result<Vec<FuzzInstruction>> {\n        let init_ix =\n            FuzzInstruction::InitVesting(InitVesting::arbitrary(u)?);\n\n        Ok(vec![init_ix])\n    }\n    fn ixs(\n        u: &mut arbitrary::Unstructured\n    ) -> arbitrary::Result<Vec<FuzzInstruction>> {\n        let withdraw_ix =\n            FuzzInstruction::WithdrawUnlocked(WithdrawUnlocked::arbitrary(u)?);\n\n        Ok(vec![withdraw_ix])\n    }\n    fn post_ixs(\n        _u: &mut arbitrary::Unstructured\n    ) -> arbitrary::Result<Vec<FuzzInstruction>> {\n        Ok(vec![])\n    }\n}\n

Tip

Consider checking the Examples section for more tips.

"},{"location":"features/invariant-checks/","title":"Invariant Checks","text":"

Trident allows you to (optionally) specify Invariant Checks for each Instruction.

The Invariant Check will be called after the Instruction was successfully invoked. Within the Invariant Check you can compare the contents of Accounts before and after the Instruction was called.

Important

Returning error in the Invariant Check is considered as detected undesired behavior (i.e. issue/crash detected).

fn check(\n    &self,\n    _pre_ix: Self::IxSnapshot,\n    post_ix: Self::IxSnapshot,\n    _ix_data: Self::IxData,\n) -> Result<(), FuzzingError> {\n    if let Some(hello_world_account) = post_ix.hello_world_account {\n        if hello_world_account.input == 253 {\n            return Err(FuzzingError::Custom(1));\n        }\n    }\n    Ok(())\n}\n

Tip

Consider checking the Examples section for more tips.

"},{"location":"features/lifecycle/","title":"Fuzz Test Lifecycle","text":"

In the sequence diagram below you can see a simplified fuzz test lifecycle.

Some diagram states are labeled with emojis:

"},{"location":"features/lifecycle/#lifecycle","title":"Lifecycle","text":"
  1. The fuzzer is running until:
    1. The maximal number of iterations is reached (if specified).
    2. A crash was detected and the exit_upon_crash parameter was set.
    3. User interrupted the test manually (for example by hitting CTRL+C).
  2. In each iteration, the fuzzer generates a sequence of random instructions to execute.
    1. User can optionally customize how the instructions are generated and can specify the instructions that should be executed at the beginning (pre_ixs), in the middle (ixs) and at the end (post_ixs) of each iteration. This can be useful for example if your program needs an initialization or you want to fuzz some specific program state.
  3. For each instruction:
    1. User defined mandatory method get_accounts() is called to collect necessary instruction accounts.
    2. User defined mandatory method get_data() is called to collect instruction data.
    3. A snapshot of all instruction accounts before the instruction execution is saved.
    4. The instruction is executed.
    5. A snapshot of all instruction accounts after the instruction execution is saved.
    6. User defined optional method check() is called to check accounts data and evaluate invariants.
fuzzer_iterations = 0fuzzer_iterations = 0fuzzer_iterations <\u00a0max_iterationsfuzzer_iterations <...donedonecreate pre-instruction\u00a0accounts snapshotscreate pre-instruction...execute instructionexecute instructioncreate post-instruction\u00a0accounts snapshotscreate post-instruction...check invariants \ud83d\udc64check invariants \ud83d\udc64fuzzer_iterations++fuzzer_iterations++Generate instructionspre_ixs \ud83d\udc64pre_ixs \ud83d\udc64ixs \ud83d\udc64ixs \ud83d\udc64post_ixs \ud83d\udc64post_ixs \ud83d\udc64endendfor ix in instructionsfor ix in instructionsget instruction accounts \u26a1get instruction accounts \u26a1get instruction data \u26a1get instruction data \u26a1next ixnext ixText is not SVG - cannot display"},{"location":"features/limitations/","title":"Current limitations","text":"

This section summarizes some known limitations in the current development stage. Further development will be focused on resolving these limitations.

"},{"location":"features/trident-manifest/","title":"Trident Manifest","text":"

You can pass supported parameters via the Trident.toml configuration file:

"},{"location":"features/trident-manifest/#fuzz","title":"[fuzz]","text":""},{"location":"features/trident-manifest/#programs","title":"programs","text":"
[[fuzz.programs]]\naddress = \"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s\"\nprogram = \"metaplex-program/metaplex-token-metadata.so\"\n
"},{"location":"features/trident-manifest/#accounts","title":"accounts","text":"
[[fuzz.accounts]]\naddress = \"6YG3J7PaxyMnnbU67ifyrgF3BzNzc7cD8hPkqK6ATweE\"\nfilename = \"tests/accounts/core_bridge_mainnet/guardian_set_5_mock.json\"\n
"},{"location":"features/trident-manifest/#allow_duplicate_txs","title":"allow_duplicate_txs","text":"
[fuzz]\n# Allow processing of duplicate transactions.\n# Setting to true might speed up fuzzing but can cause\n# false positive crashes (default: false)\nallow_duplicate_txs = false\n
"},{"location":"features/trident-manifest/#fuzzing_with_stats","title":"fuzzing_with_stats","text":"
[fuzz]\n# Trident will show statistics after the fuzzing session.\n# This option forces use of honggfuzz parameter\n# `keep_output` as true in order to be able to catch fuzzer stdout.\n# (default: false)\nfuzzing_with_stats = true\n
"},{"location":"features/trident-manifest/#honggfuzz","title":"[honggfuzz]","text":""},{"location":"features/trident-manifest/#timeout","title":"timeout","text":"
[honggfuzz]\n# Timeout in seconds (default: 10)\ntimeout = 10\n
"},{"location":"features/trident-manifest/#iterations","title":"iterations","text":"
[honggfuzz]\n# Number of fuzzing iterations (default: 0 [no limit])\niterations = 0\n
"},{"location":"features/trident-manifest/#threads","title":"threads","text":"
[honggfuzz]\n# Number of concurrent fuzzing threads (default: 0 [number of CPUs / 2])\nthreads = 0\n
"},{"location":"features/trident-manifest/#keep_output","title":"keep_output","text":"
[honggfuzz]\n# Don't close children's stdin, stdout, stderr; can be noisy (default: false)\nkeep_output = false\n
"},{"location":"features/trident-manifest/#verbose","title":"verbose","text":"
[honggfuzz]\n# Disable ANSI console; use simple log output (default: false)\nverbose = false\n
"},{"location":"features/trident-manifest/#exit_upon_crash","title":"exit_upon_crash","text":"
[honggfuzz]\n# Exit upon seeing the first crash (default: false)\nexit_upon_crash = false\n
"},{"location":"features/trident-manifest/#mutations_per_run","title":"mutations_per_run","text":"
[honggfuzz]\n# Maximal number of mutations per one run (default: 6)\nmutations_per_run = 6\n
"},{"location":"features/trident-manifest/#cargo_target_dir","title":"cargo_target_dir","text":"
[honggfuzz]\n# Target compilation directory,\n# (default: \"\" [\"trident-tests/fuzz_tests/fuzzing/honggfuzz/hfuzz_target\"]).\n# To not clash with cargo build's default target directory.\ncargo_target_dir = \"\"\n
"},{"location":"features/trident-manifest/#hfuzz_workspace","title":"hfuzz_workspace","text":"
[honggfuzz]\n# Honggfuzz working directory,\n# (default: \"\" [\"trident-tests/fuzz_tests/fuzzing/honggfuzz/hfuzz_workspace\"]).\nhfuzz_workspace = \"\"\n
"},{"location":"features/trident-manifest/#crashdir","title":"crashdir","text":"
[honggfuzz]\n# Directory where crashes are saved to (default: \"\" [workspace directory])\ncrashdir = \"\"\n
"},{"location":"features/trident-manifest/#extension","title":"extension","text":"
[honggfuzz]\n# Input file extension (e.g. 'swf'), (default: \"\" ['fuzz'])\nextension = \"\"\n
"},{"location":"features/trident-manifest/#run_time","title":"run_time","text":"
[honggfuzz]\n# Number of seconds this fuzzing session will last (default: 0 [no limit])\nrun_time = 0\n
"},{"location":"features/trident-manifest/#max_file_size","title":"max_file_size","text":"
[honggfuzz]\n# Maximal size of files processed by the fuzzer in bytes\n# (default: 1048576 = 1MB)\nmax_file_size = 1048576\n
"},{"location":"features/trident-manifest/#save_all","title":"save_all","text":"
[honggfuzz]\n# Save all test-cases\n# (not only the unique ones) by appending the current\n# time-stamp to the filename (default: false)\nsave_all = false\n

Tip

Consider checking the Examples section for more tips.

"},{"location":"get-help/get-help/","title":"Get Help","text":"

Need help writing Fuzz Tests? Do not hesitate to join our Discord server!

"},{"location":"installation/installation/","title":"Installation","text":"

Tip

Docker Image down below.

Important

Prerequisite

It is expected that you have installed:

For supported versions check the Supported Versions

"},{"location":"installation/installation/#install-system-dependencies","title":"Install System Dependencies","text":"

Update your package list and install the required packages:

sudo apt-get update\nsudo apt-get install -y \\\n    curl \\\n    git \\\n    build-essential \\\n    pkg-config \\\n    libssl-dev \\\n    npm \\\n    vim \\\n    nano \\\n    wget \\\n    binutils-dev \\\n    libunwind-dev \\\n    lldb\n
"},{"location":"installation/installation/#install-hongfuzz","title":"Install Hongfuzz","text":"

Install honggfuzz

cargo install honggfuzz\n
"},{"location":"installation/installation/#install-trident","title":"Install Trident","text":"
cargo install trident-cli\n
"},{"location":"installation/installation/#supported-versions","title":"Supported versions","text":"Trident CLI Anchor Solana Rust Honggfuzz develop 0.30.1 ^1.17.4 nightly 0.5.56 0.8.0 0.30.1 ^1.17.4 nightly 0.5.56 0.7.0 >=0.29.*1 ^1.17.4 nightly 0.5.56 0.6.0 >=0.29.*1 ^1.17 nightly 0.5.55 0.5.0 ~0.28.* =1.16.6 - - 0.4.0 ~0.27.* >=1.15 - - 0.3.0 ~0.25.* >=1.10 - - 0.2.0 ~0.24.* >=1.9 - -
  1. To use Trident with Anchor 0.29.0, run the following commands from your project's root directory after Trident initialization:
    cargo update anchor-client@0.30.0 --precise 0.29.0\ncargo update anchor-spl@0.30.0 --precise 0.29.0\n
"},{"location":"installation/installation/#docker-image","title":"Docker Image","text":"

TBD

"},{"location":"writing-fuzz-test/writing-fuzz-test/","title":"Writing fuzz test","text":""},{"location":"writing-fuzz-test/writing-fuzz-test/#initialize-trident","title":"Initialize Trident","text":"

Initialize Trident in the Anchor-based workspace.

trident init\n

Info

Trident under the hood

Tip

If you have Trident already initialized, you can add new fuzz test using trident fuzz add.

"},{"location":"writing-fuzz-test/writing-fuzz-test/#fill-the-fuzz-test-template","title":"Fill the Fuzz test Template","text":""},{"location":"writing-fuzz-test/writing-fuzz-test/#derive-accountssnapshots","title":"Derive AccountsSnapshots","text":"

For every Account Context specified in the Anchor project derive AccountsSnapshots such as:

// ...\n\nuse trident_derive_accounts_snapshots::AccountsSnapshots;\n\n// ...\n\n\n#[derive(Accounts, AccountsSnapshots)]\npub struct InitializeContext<'info> {\n    #[account(mut)]\n    pub author: Signer<'info>,\n    #[account(\n        init,\n        payer=author,\n        space=8+100,\n        seeds=[b\"hello_world_seed\"],\n        bump\n    )]\n    pub hello_world_account: Account<'info, StoreHelloWorld>,\n    pub system_program: Program<'info, System>,\n    // ...\n\n}\n
"},{"location":"writing-fuzz-test/writing-fuzz-test/#link-the-accountssnapshots","title":"Link the AccountsSnapshots","text":"

Fuzz Instructions use the derived AccountsSnapshots. You need to link the derived AccountsSnapshots to the corresponding aliases.

Important

Modules where the Contexts (with the derived AccountsSnapshots) are specified need to be public.

// fuzz_instructions.rs\n\nuse hello_world::trident_fuzz_initialize_context_snapshot::InitializeContextAlias;\n\ntype InitializeFnSnapshot<'info> = InitializeContextAlias<'info>;\n

Tip

For more examples, check the Examples.

"},{"location":"writing-fuzz-test/writing-fuzz-test/#define-fuzz-accounts","title":"Define Fuzz Accounts","text":"

Define AccountsStorage type for each Account you would like to use.

Important

Keep in mind:

#[doc = r\" Use AccountsStorage<T> where T can be one of:\"]\n#[doc = r\" Keypair, PdaStore, TokenStore, MintStore, ProgramStore\"]\n#[derive(Default)]\npub struct FuzzAccounts {\n    author: AccountsStorage<Keypair>,\n    hello_world_account: AccountsStorage<PdaStore>,\n    // No need to fuzz system_program\n    // system_program: AccountsStorage<todo!()>,\n}\n

Tip

For more details about the AccountsStorage check AccountsStorage.

"},{"location":"writing-fuzz-test/writing-fuzz-test/#implement-fuzz-instructions","title":"Implement Fuzz Instructions","text":"

Each Instruction in the Fuzz Test has to have defined the following functions:

Tip

"},{"location":"writing-fuzz-test/writing-fuzz-test/#execute","title":"Execute","text":""},{"location":"writing-fuzz-test/writing-fuzz-test/#run-fuzz-test","title":"Run Fuzz Test","text":"

To execute the desired fuzz test using the Honggfuzz, run:

# Replace <TARGET_NAME> with the name of particular\n# fuzz test (for example: \"fuzz_0\")\ntrident fuzz run-hfuzz <TARGET_NAME>\n
"},{"location":"writing-fuzz-test/writing-fuzz-test/#debug-fuzz-test","title":"Debug Fuzz Test","text":"

To debug your program using Honggfuzz with values from a crash file:

# fuzzer will run the <TARGET_NAME> with the specified <CRASH_FILE_PATH>\ntrident fuzz debug-hfuzz <TARGET_NAME> <CRASH_FILE_PATH>\n

Tip

By default, the crashfiles are stored in the

Tip

For more info about the fuzzing outputs chech the Commands

"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":"Trident

Rust-based Fuzzing framework for Solana programs to help you ship secure code.

"},{"location":"#what-is-fuzzing","title":"What is Fuzzing ?","text":"

\"Fuzz testing is an automated technique that provides generated random, invalid, or unexpected input data to your program. This helps discover unknown bugs and vulnerabilities, potentially preventing zero-day exploits.\"

"},{"location":"CHANGELOG/","title":"Changelog","text":"

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning (SemVer).

Note: Version 0 of Semantic Versioning is handled differently from version 1 and above. The minor version will be incremented upon a breaking change and the patch version will be incremented for features.

"},{"location":"CHANGELOG/#dev-unreleased","title":"[dev] - Unreleased","text":""},{"location":"CHANGELOG/#080-2024-10-21","title":"[0.8.0] - 2024-10-21","text":"

Added

Changed

Removed

"},{"location":"CHANGELOG/#070-2024-08-14","title":"[0.7.0] - 2024-08-14","text":"

Added

Fixed

Removed

"},{"location":"CHANGELOG/#060-2024-05-20","title":"[0.6.0] - 2024-05-20","text":"

Added

Fixed

"},{"location":"CHANGELOG/#050-2023-08-28","title":"[0.5.0] - 2023-08-28","text":"

Added

"},{"location":"CHANGELOG/#041-2023-08-21","title":"[0.4.1] - 2023-08-21","text":"

Changed

Fixed

"},{"location":"CHANGELOG/#030-2022-09-23","title":"[0.3.0] - 2022-09-23","text":"

Changed

Added

"},{"location":"CHANGELOG/#020-2022-07-27","title":"[0.2.0] - 2022-07-27","text":"

Added

"},{"location":"commands/commands/","title":"Commands","text":""},{"location":"commands/commands/#trident-init","title":"trident init","text":""},{"location":"commands/commands/#trident-how","title":"trident how","text":""},{"location":"commands/commands/#trident-fuzz","title":"trident fuzz","text":""},{"location":"commands/commands/#trident-fuzz-run-hfuzz","title":"trident fuzz run-hfuzz","text":""},{"location":"commands/commands/#output","title":"Output","text":"

Important

The output provided by Honggfuzz is as follows:

  1. Number of Fuzzing Iterations.
  2. Feedback Driven Mode = Honggfuzz generates data based on the feedback (i.e. feedback based on Coverage progress).
  3. Average Iterations per second.
  4. Number of crashes it found (panics or failed invariant checks).
------------------------[  0 days 00 hrs 00 mins 01 secs ]----------------------\n  Iterations : 688 (out of: 1000 [68%]) # -- 1. --\n  Mode [3/3] : Feedback Driven Mode # -- 2. --\n      Target : trident-tests/fuzz_tests/fuzzing.....wn-linux-gnu/release/fuzz_0\n     Threads : 16, CPUs: 32, CPU%: 1262% [39%/CPU]\n       Speed : 680/sec [avg: 688] # -- 3. --\n     Crashes : 1 [unique: 1, blocklist: 0, verified: 0] # -- 4. --\n    Timeouts : 0 [10 sec]\n Corpus Size : 98, max: 1048576 bytes, init: 0 files\n  Cov Update : 0 days 00 hrs 00 mins 00 secs ago\n    Coverage : edge: 10345/882951 [1%] pc: 163 cmp: 622547\n---------------------------------- [ LOGS ] ------------------/ honggfuzz 2.6 /-\n
"},{"location":"commands/commands/#trident-fuzz-debug-hfuzz","title":"trident fuzz debug-hfuzz","text":""},{"location":"commands/commands/#output_1","title":"Output","text":"

Important

The debug output is at current development stage really verbose and contains lldb parts. We are working on improving this experience. In the picture below you can see an example of provided debug output.

  1. Series of Transaction Logs
  2. Structures of data send within the Instructions
  3. Panic or Crash, based on if the Fuzzing panicked within the Solana Program or Invariant Check failed.

"},{"location":"commands/commands/#trident-fuzz-add","title":"trident fuzz add","text":""},{"location":"commands/commands/#trident-clean","title":"trident clean","text":""},{"location":"examples/examples/","title":"Trident by Examples","text":""},{"location":"faq/faq/","title":"FAQ","text":""},{"location":"faq/faq/#is-trident-supported-only-with-anchor","title":"Is Trident supported only with Anchor ?","text":""},{"location":"faq/faq/#i-created-the-fuzz-test-what-should-i-do-next","title":"I created the Fuzz Test what should I do next ?","text":""},{"location":"faq/faq/#my-program-instruction-contains-custom-type-such-as-struct-or-enum-on-its-input-but-it-does-not-derive-arbitrary","title":"My program Instruction contains custom type such as Struct or Enum on its input, but it does not derive Arbitrary.","text":""},{"location":"faq/faq/#is-trident-open-source","title":"Is Trident open-source ?","text":""},{"location":"faq/faq/#i-would-like-to-report-issue-with-trident-what-should-i-do","title":"I would like to report Issue with Trident, what should I do ?","text":""},{"location":"faq/faq/#is-trident-deployed-on-mainnet-devnet-testenet","title":"Is Trident deployed on Mainnet / Devnet / Testenet ?","text":""},{"location":"faq/faq/#what-type-of-fuzzer-trident-is","title":"What type of Fuzzer Trident is ?","text":""},{"location":"features/account-storages/","title":"Account Storages","text":"

Trident allows developers to generate random accounts for fuzzing.

However, the Accounts are not completely random, and neither are the Account addresses.

Instead, Trident generates random AccountIDs which are indexes to Account Storages. Each unique Account contained within the Anchor generated IDL has its own AccountStorage. The FuzzAccounts containing the Accounts Storages is global to all Instructions to use.

Note

Details:

Always generating only random accounts would in most cases lead to a situation where the fuzzer would be stuck because the accounts would be almost every time rejected by your Anchor program. Therefore it is necessary to specify, what accounts should be used and also limit the number of newly created accounts to reduce the space complexity.

Important

Currently, supported types of Account Storages:

Then use the corresponding AccountsStorage.

pub struct FuzzAccounts {\n    signer: AccountsStorage<Keypair>,\n    some_pda: AccountsStorage<PdaStore>,\n    token_vault: AccountsStorage<TokenStore>,\n    mint: AccountsStorage<MintStore>,\n    // ...\n}\n

Tip

Keep in mind:

Tip

Consider checking the Examples section for more tips.

"},{"location":"features/arbitrary-data/","title":"Arbitrary Data","text":"

Trident allows you to customize Instruction Data to provide structure.

For example your Initialize Instruction expects two arguments start_at and end_at you know that in order for the Instruction to make sense, it is required that the start_at < end_at. Moreover, there should be significant difference between these two. This can be utilized with the Arbitrary crate.

#[derive(Arbitrary, Debug)]\npub struct InitVestingData {\n    pub recipient: AccountId,\n    #[arbitrary(\n        with = |u: &mut arbitrary::Unstructured| u.int_in_range(1..=1_000_000)\n    )]\n    pub amount: u64,\n    // we want start_at smaller than end_at\n    // and for testing purposes we can run tests with times from the past\n    #[arbitrary(\n        with = |u: &mut arbitrary::Unstructured| u.int_in_range(0..=1_000_000)\n    )]\n    pub start_at: u64,\n    #[arbitrary(\n        with = |u: &mut arbitrary::Unstructured| u.int_in_range(1_001_001..=1_050_000)\n    )]\n    pub end_at: u64,\n    #[arbitrary(\n        with = |u: &mut arbitrary::Unstructured| u.int_in_range(1..=1000)\n    )]\n    pub interval: u64,\n}\n
"},{"location":"features/arbitrary-data/#implement-arbitrary","title":"Implement Arbitrary","text":"

There are macros available to use with Arbitrary, however, it is possible to Implement the arbitrary function by yourself.

// -------------------------------------------------------------------\n// -------------------------------------------------------------------\n// Implement Arbitrary\nimpl<'a> Arbitrary<'a> for InitVestingData {\n    fn arbitrary(\n        u: &mut arbitrary::Unstructured<'a>\n    ) -> arbitrary::Result<Self> {\n        // obtain AccountId\n        let recipient = AccountId::arbitrary(u)?;\n\n        // limit the generated amount to the 1_000_000\n        let amount = u.int_in_range(1..=1_000_000)?;\n\n        // now we want to obtain\n        // - start_at\n        // - end_at\n        // - interval\n        // however we want to limit the data such that:\n        // - start_at < end_at\n        // - end_at - start_at > interval\n        // - interval has lower limit of 500 and upper limit of 1000.\n\n        let start_at: u64 = u.int_in_range(1_000_000..=5_000_000)?;\n        let end_at: u64 = u.int_in_range(1_000_000..=5_000_000)?;\n        let interval: u64 = u.int_in_range(500..=1000)?;\n\n        // ensure that start_at < end_at\n        if start_at >= end_at {\n            return Err(arbitrary::Error::IncorrectFormat);\n        }\n\n        // ensure that end_at - start_at > interval\n        match end_at.checked_sub(start_at) {\n            Some(diff) => {\n                if diff <= interval {\n                    return Err(arbitrary::Error::IncorrectFormat);\n                }\n            }\n            None => return Err(arbitrary::Error::IncorrectFormat),\n        }\n\n        Ok(InitVestingData {\n            recipient,\n            amount,\n            start_at,\n            end_at,\n            interval,\n        })\n    }\n    // -------------------------------------------------------------------\n    // -------------------------------------------------------------------\n}\n

Tip

Consider checking the Examples section for more tips.

"},{"location":"features/error-handlers/","title":"Error Handler","text":"

Trident allows you to specify custom error handler for each Instruction.

This can be particularly helpful:

Tip

The default behavior of the function is that the error is returned.

/// default implementation\nfn tx_error_handler(\n    &self,\n    e: FuzzClientErrorWithOrigin,\n    ix_data: Self::IxData,\n    pre_ix_acc_infos: &'info mut [Option<AccountInfo<'info>>],\n) -> Result<(), FuzzClientErrorWithOrigin> {\n    Err(e)\n}\n

To omit the Error and continue with the next Instruction in the iteration, you can do

/// default implementation\nfn tx_error_handler(\n    &self,\n    e: FuzzClientErrorWithOrigin,\n    ix_data: Self::IxData,\n    pre_ix_acc_infos: &'info mut [Option<AccountInfo<'info>>],\n) -> Result<(), FuzzClientErrorWithOrigin> {\n    Ok(())\n}\n
"},{"location":"features/features/","title":"Trident Features","text":"

Trident contains multiple features to enhance the fuzzing experience and increase ability to discover bugs.

"},{"location":"features/fuzz-instructions/","title":"Fuzz Instructions","text":"

Trident defines FuzzInstruction enum containing all available Instructions within your program.

The enum variants additionally contains their corresponding structures for Accounts and Input arguments.

#[derive(Arbitrary, DisplayIx, FuzzTestExecutor, FuzzDeserialize)]\npub enum FuzzInstruction {\n    Initialize(Initialize),\n    Update(Update),\n}\n#[derive(Arbitrary, Debug)]\npub struct Initialize {\n    pub accounts: InitializeAccounts,\n    pub data: InitializeData,\n}\n#[derive(Arbitrary, Debug)]\npub struct Update {\n    pub accounts: UpdateAccounts,\n    pub data: UpdateData,\n}\n// ...\n
"},{"location":"features/fuzz-instructions/#instruction-behavior","title":"Instruction behavior","text":"

Each Instruction variant has to define IxOps trait containing the following methods:

"},{"location":"features/fuzz-instructions/#get-program-id","title":"Get Program ID","text":"

This method specifies program ID to which the Instruction corresponds.

In case you have only one program in the Anchor Workspace it is not really important. The importance occurs when you have multiple programs in the Workspace and you want to call Instructions of every Program. In that case each Instruction Variant corresponds to its program by the Program ID.

"},{"location":"features/fuzz-instructions/#get-data","title":"Get Data","text":"

This method specifies what the Instruction Input Data should look like. You can use completely random data generated by the fuzzer, such as:

fn get_data(\n    &self,\n    _client: &mut impl FuzzClient,\n    _fuzz_accounts: &mut FuzzAccounts,\n) -> Result<Self::IxData, FuzzingError> {\n    let data = hello_world::instruction::InitializeFn {\n        input: self.data.input,\n    };\n    Ok(data)\n}\n

You can also use always constant values

fn get_data(\n    &self,\n    _client: &mut impl FuzzClient,\n    _fuzz_accounts: &mut FuzzAccounts,\n) -> Result<Self::IxData, FuzzingError> {\n    let data = hello_world::instruction::InitializeFn {\n        input: 5,\n    };\n    Ok(data)\n}\n

Or you can customize the Data using the Arbitrary crate. Check Arbitrary Data.

"},{"location":"features/fuzz-instructions/#custom-data-types","title":"Custom Data Types","text":"

If you use Custom Types as Instruction data arguments, you may encounter a problem that the Custom Type does not implement

"},{"location":"features/fuzz-instructions/#derive-debug-and-arbitrary-traits-inside-the-fuzz-test","title":"Derive Debug and Arbitrary traits inside the Fuzz Test","text":"

You can redefine the custom type within the fuzz_instructions.rs file, along with all the necessary traits.

// Redefine the Custom Type inside the fuzz_instructions.rs,\n// but this time with all of the required traits.\n#[derive(Arbitrary,Debug, Clone, Copy)]\npub enum CustomEnumInputFuzz {\n    InputVariant1,\n    InputVariant2,\n    InputVariant3,\n}\n

Then, you would also need to implement the std::convert::From<T> trait to enable conversion between the newly defined Custom Type and the Custom Type used within your program.

Tip

Consider checking the Examples section for more tips.

"},{"location":"features/fuzz-instructions/#get-accounts","title":"Get Accounts","text":"

This method specifies how the Accounts for the corresponding Instruction should be resolved. You can use accounts stored within the FuzzAccounts Account Storages, or you can define custom Account using the client.

Important

Source Code below

fn get_accounts(\n    &self,\n    client: &mut impl FuzzClient,\n    fuzz_accounts: &mut FuzzAccounts,\n) -> Result<(Vec<Keypair>, Vec<AccountMeta>), FuzzingError> {\n    let author = fuzz_accounts.author.get_or_create_account(\n        self.accounts.author,\n        client,\n        5 * LAMPORTS_PER_SOL,\n    );\n\n    let hello_world_account = fuzz_accounts\n        .hello_world_account\n        .get_or_create_account(\n            self.accounts.hello_world_account,\n            &[b\"hello_world_seed\"],\n            &hello_world::ID,\n        )\n        .unwrap();\n    let signers = vec![author.clone()];\n    let acc_meta = hello_world::accounts::InitializeContext {\n        author: author.pubkey(),\n        hello_world_account: hello_world_account.pubkey(),\n        system_program: solana_sdk::system_program::ID,\n    }\n    .to_account_metas(None);\n    Ok((signers, acc_meta))\n}\n
"},{"location":"features/fuzz-instructions/#create-an-arbitrary-account","title":"Create an arbitrary account","text":"

The AccountsStorage<T> type provides an implementation of the get_or_create_account method that helps you create new or read already existing accounts. There are different implementations for different types of storage (Keypair, TokenStore, MintStore, PdaStore) to simplify the creation of new accounts.

However, there are cases when the provided implementation is not sufficient and it is necessary to create an account manually. These cases can be (but are not limited to) for example:

In that case, you can use the storage method of the AccountsStorage<T> struct that exposes the underlying HashMap<AccountId, T> and you can add new accounts directly to it.

It is possible to create and store any kind of account. For example:

let state = fuzz_accounts\n    .state\n    // gets the storage of all `state` account variants\n    .storage()\n    // returns the Keypair of the `state` account with\n    // the given `AccountId` if it has been added previously\n    .entry(self.accounts.state)\n    .or_insert_with(|| {\n        let space = State::SIZE;\n        let rent_exempt_lamports = client.get_rent().unwrap()\n                            .minimum_balance(space);\n        let keypair = Keypair::new();\n        let account = AccountSharedData::new_data_with_space::<[u8; 0]>(\n            rent_exempt_lamports,\n            &[],\n            space,\n            &my_program::id(),\n        ).unwrap();\n        // insert the custom account also into the client\n        client.set_account_custom(&keypair.pubkey(), &account);\n        keypair\n    });\n
let rent_exempt_for_token_acc = client\n    .get_rent()\n    .unwrap()\n    .minimum_balance(anchor_spl::token::spl_token::state::Account::LEN);\n\nlet my_pda = fuzz_accounts\n    .my_pda\n    // gets the storage of all `my_pda` account variants\n    .storage()\n    // returns the PdaStore struct of the `my_pda` account with\n    // the given `AccountId` if it has been added previously\n    .entry(self.accounts.my_pda)\n    .or_insert_with(|| {\n        let seeds = &[b\"some-seeds\"];\n        let pda = Pubkey::find_program_address(seeds, &my_program::id()).0;\n        let account = AccountSharedData::new_data_with_space::<[u8; 0]>(\n            rent_exempt_for_token_acc,\n            &[],\n            0,\n            &SYSTEM_PROGRAM_ID,\n        ).unwrap();\n        // insert the custom account also into the client\n        client.set_account_custom(&pda, &account);\n        let vec_of_seeds: Vec<Vec<u8>> = seeds.iter().map(|&seed| seed.to_vec())\n                            .collect();\n        PdaStore {\n            pubkey: pda,\n            seeds: vec_of_seeds,\n        }\n    }).pubkey();\n
"},{"location":"features/fuzz-instructions/#check","title":"Check","text":"

This method provides Invariant Check for the corresponding Instruction. Check Invariant Checks.

"},{"location":"features/fuzz-instructions/#tx-error-handler","title":"Tx Error Handler","text":"

This method provides Tx Error Handler for the corresponding Instruction. Check Error Handler.

"},{"location":"features/fuzz-instructions/#example","title":"Example","text":"

Tip

Consider checking the Examples section for more tips.

"},{"location":"features/fuzzing-statistics/","title":"Fuzzing Statistics","text":"

Trident allows you to see statistics after the fuzzing session ended.

Important

In order to show statistics set fuzzing_with_stats within the Trident.toml to true.

[fuzz]\n# ...\nfuzzing_with_stats = true\n# ...\n
"},{"location":"features/fuzzing-statistics/#available-statistics","title":"Available Statistics","text":""},{"location":"features/fuzzing-statistics/#simple","title":"Simple","text":" Note

Keep in mind that the number of fuzz iterations does not directly correspond to the total number of invocations. In one fuzz iteration, the fuzzer might be unable to deserialize fuzz data into instructions, causing the entire iteration to be skipped.

On the other hand this is expected behavior as the underlying data are randomly (with coverage feedback) generated, so the Honggfuzz will not necessarily find appropriate data each iteration.

Tip

Consider checking the Examples section for more tips.

"},{"location":"features/genesis-accounts/","title":"Genesis","text":""},{"location":"features/genesis-accounts/#genesis-programs","title":"Genesis Programs","text":"

Trident allows you to use Cross Program Invocation of both Native and SBF programs.

"},{"location":"features/genesis-accounts/#native","title":"Native","text":"

In case of multiple programs within the Anchor Workspace. Make sure that all of the programs you would like to call Cross Program Invocation to are included in the initial state of the Fuzz Test Environment.

Important

Source code below:

// test_fuzz.rs\n\nfn fuzz_iteration<T: FuzzTestExecutor<U> + std::fmt::Display, U>(\n    fuzz_data: FuzzData<T, U>\n) {\n    let fuzzing_program_callee = FuzzingProgram::new(\n        PROGRAM_NAME_CALLEE,\n        &PROGRAM_ID_CALLEE,\n        processor!(convert_entry!(entry_callee)),\n    );\n\n    let fuzzing_program_caller = FuzzingProgram::new(\n        PROGRAM_NAME_CALLER,\n        &PROGRAM_ID_CALLER,\n        processor!(convert_entry!(entry_caller)),\n    );\n\n    let mut client =\n        ProgramTestClientBlocking::new(\n            &[fuzzing_program_callee, fuzzing_program_caller],\n            &[]\n        ).unwrap();\n\n    let _ = fuzz_data.run_with_runtime(&mut client);\n}\n
"},{"location":"features/genesis-accounts/#sbf","title":"SBF","text":"

In case of SBF targets, compiled or dumped from whatever cluster. You can also use these within the Fuzz Tests.

Tip

If you want to obtain Program from Mainnet use

# -u m specifies to dump from mainnet\nsolana program dump -u m <PROGRAM_ID> <PROGRAM_NAME>.so\n

Important

[[fuzz.programs]]\naddress = \"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s\"\nprogram = \"metaplex-program/metaplex-token-metadata.so\"\n
"},{"location":"features/genesis-accounts/#genesis-accounts","title":"Genesis Accounts","text":"

Trident allows you to include Accounts with data in base64 format.

Tip

If you want to obtain Account with base64 data format, use

# -u m specifies to dump from mainnet\nsolana account -u m <ADDRESS> --output json\n

Important

To include desired accounts in the fuzz testing environment, add then using the Trident.toml.

[[fuzz.accounts]]\naddress = \"6YG3J7PaxyMnnbU67ifyrgF3BzNzc7cD8hPkqK6ATweE\"\nfilename = \"tests/accounts/core_bridge_mainnet/guardian_set_5_mock.json\"\n

Tip

Consider checking the Examples section for more tips.

"},{"location":"features/instructions-sequences/","title":"Instructions Sequences","text":"

Trident allows you to specify Custom Instruction Sequences you would like to execute.

Possible Instruction sequences are split into 3 parts

For example if you program always needs to start with some kind of Initialization instruction, you can specify this Initialize Instruction in pre_ixs as shown in the source code below.

Tip

// test_fuzz.rs\n\n// do not forget to include the required structures\nuse fuzz_instructions::InitVesting;\nuse fuzz_instructions::WithdrawUnlocked;\n\nimpl FuzzDataBuilder<FuzzInstruction> for MyFuzzData {\n    fn pre_ixs(\n        u: &mut arbitrary::Unstructured\n    ) -> arbitrary::Result<Vec<FuzzInstruction>> {\n        let init_ix =\n            FuzzInstruction::InitVesting(InitVesting::arbitrary(u)?);\n\n        Ok(vec![init_ix])\n    }\n    fn ixs(\n        u: &mut arbitrary::Unstructured\n    ) -> arbitrary::Result<Vec<FuzzInstruction>> {\n        let withdraw_ix =\n            FuzzInstruction::WithdrawUnlocked(WithdrawUnlocked::arbitrary(u)?);\n\n        Ok(vec![withdraw_ix])\n    }\n    fn post_ixs(\n        _u: &mut arbitrary::Unstructured\n    ) -> arbitrary::Result<Vec<FuzzInstruction>> {\n        Ok(vec![])\n    }\n}\n

Tip

Consider checking the Examples section for more tips.

"},{"location":"features/invariant-checks/","title":"Invariant Checks","text":"

Trident allows you to (optionally) specify Invariant Checks for each Instruction.

The Invariant Check will be called after the Instruction was successfully invoked. Within the Invariant Check you can compare the contents of Accounts before and after the Instruction was called.

Important

Returning error in the Invariant Check is considered as detected undesired behavior (i.e. issue/crash detected).

fn check(\n    &self,\n    _pre_ix: Self::IxSnapshot,\n    post_ix: Self::IxSnapshot,\n    _ix_data: Self::IxData,\n) -> Result<(), FuzzingError> {\n    if let Some(hello_world_account) = post_ix.hello_world_account {\n        if hello_world_account.input == 253 {\n            return Err(FuzzingError::Custom(1));\n        }\n    }\n    Ok(())\n}\n

Tip

Consider checking the Examples section for more tips.

"},{"location":"features/lifecycle/","title":"Fuzz Test Lifecycle","text":"

In the sequence diagram below you can see a simplified fuzz test lifecycle.

Some diagram states are labeled with emojis:

"},{"location":"features/lifecycle/#lifecycle","title":"Lifecycle","text":"
  1. The fuzzer is running until:
    1. The maximal number of iterations is reached (if specified).
    2. A crash was detected and the exit_upon_crash parameter was set.
    3. User interrupted the test manually (for example by hitting CTRL+C).
  2. In each iteration, the fuzzer generates a sequence of random instructions to execute.
    1. User can optionally customize how the instructions are generated and can specify the instructions that should be executed at the beginning (pre_ixs), in the middle (ixs) and at the end (post_ixs) of each iteration. This can be useful for example if your program needs an initialization or you want to fuzz some specific program state.
  3. For each instruction:
    1. User defined mandatory method get_accounts() is called to collect necessary instruction accounts.
    2. User defined mandatory method get_data() is called to collect instruction data.
    3. A snapshot of all instruction accounts before the instruction execution is saved.
    4. The instruction is executed.
    5. A snapshot of all instruction accounts after the instruction execution is saved.
    6. User defined optional method check() is called to check accounts data and evaluate invariants.
fuzzer_iterations = 0fuzzer_iterations = 0fuzzer_iterations <\u00a0max_iterationsfuzzer_iterations <...donedonecreate pre-instruction\u00a0accounts snapshotscreate pre-instruction...execute instructionexecute instructioncreate post-instruction\u00a0accounts snapshotscreate post-instruction...check invariants \ud83d\udc64check invariants \ud83d\udc64fuzzer_iterations++fuzzer_iterations++Generate instructionspre_ixs \ud83d\udc64pre_ixs \ud83d\udc64ixs \ud83d\udc64ixs \ud83d\udc64post_ixs \ud83d\udc64post_ixs \ud83d\udc64endendfor ix in instructionsfor ix in instructionsget instruction accounts \u26a1get instruction accounts \u26a1get instruction data \u26a1get instruction data \u26a1next ixnext ixText is not SVG - cannot display"},{"location":"features/limitations/","title":"Current limitations","text":"

This section summarizes some known limitations in the current development stage. Further development will be focused on resolving these limitations.

"},{"location":"features/trident-manifest/","title":"Trident Manifest","text":"

You can pass supported parameters via the Trident.toml configuration file:

"},{"location":"features/trident-manifest/#fuzz","title":"[fuzz]","text":""},{"location":"features/trident-manifest/#programs","title":"programs","text":"
[[fuzz.programs]]\naddress = \"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s\"\nprogram = \"metaplex-program/metaplex-token-metadata.so\"\n
"},{"location":"features/trident-manifest/#accounts","title":"accounts","text":"
[[fuzz.accounts]]\naddress = \"6YG3J7PaxyMnnbU67ifyrgF3BzNzc7cD8hPkqK6ATweE\"\nfilename = \"tests/accounts/core_bridge_mainnet/guardian_set_5_mock.json\"\n
"},{"location":"features/trident-manifest/#allow_duplicate_txs","title":"allow_duplicate_txs","text":"
[fuzz]\n# Allow processing of duplicate transactions.\n# Setting to true might speed up fuzzing but can cause\n# false positive crashes (default: false)\nallow_duplicate_txs = false\n
"},{"location":"features/trident-manifest/#fuzzing_with_stats","title":"fuzzing_with_stats","text":"
[fuzz]\n# Trident will show statistics after the fuzzing session.\n# This option forces use of honggfuzz parameter\n# `keep_output` as true in order to be able to catch fuzzer stdout.\n# (default: false)\nfuzzing_with_stats = true\n
"},{"location":"features/trident-manifest/#honggfuzz","title":"[honggfuzz]","text":""},{"location":"features/trident-manifest/#timeout","title":"timeout","text":"
[honggfuzz]\n# Timeout in seconds (default: 10)\ntimeout = 10\n
"},{"location":"features/trident-manifest/#iterations","title":"iterations","text":"
[honggfuzz]\n# Number of fuzzing iterations (default: 0 [no limit])\niterations = 0\n
"},{"location":"features/trident-manifest/#threads","title":"threads","text":"
[honggfuzz]\n# Number of concurrent fuzzing threads (default: 0 [number of CPUs / 2])\nthreads = 0\n
"},{"location":"features/trident-manifest/#keep_output","title":"keep_output","text":"
[honggfuzz]\n# Don't close children's stdin, stdout, stderr; can be noisy (default: false)\nkeep_output = false\n
"},{"location":"features/trident-manifest/#verbose","title":"verbose","text":"
[honggfuzz]\n# Disable ANSI console; use simple log output (default: false)\nverbose = false\n
"},{"location":"features/trident-manifest/#exit_upon_crash","title":"exit_upon_crash","text":"
[honggfuzz]\n# Exit upon seeing the first crash (default: false)\nexit_upon_crash = false\n
"},{"location":"features/trident-manifest/#mutations_per_run","title":"mutations_per_run","text":"
[honggfuzz]\n# Maximal number of mutations per one run (default: 6)\nmutations_per_run = 6\n
"},{"location":"features/trident-manifest/#cargo_target_dir","title":"cargo_target_dir","text":"
[honggfuzz]\n# Target compilation directory,\n# (default: \"\" [\"trident-tests/fuzz_tests/fuzzing/honggfuzz/hfuzz_target\"]).\n# To not clash with cargo build's default target directory.\ncargo_target_dir = \"\"\n
"},{"location":"features/trident-manifest/#hfuzz_workspace","title":"hfuzz_workspace","text":"
[honggfuzz]\n# Honggfuzz working directory,\n# (default: \"\" [\"trident-tests/fuzz_tests/fuzzing/honggfuzz/hfuzz_workspace\"]).\nhfuzz_workspace = \"\"\n
"},{"location":"features/trident-manifest/#crashdir","title":"crashdir","text":"
[honggfuzz]\n# Directory where crashes are saved to (default: \"\" [workspace directory])\ncrashdir = \"\"\n
"},{"location":"features/trident-manifest/#extension","title":"extension","text":"
[honggfuzz]\n# Input file extension (e.g. 'swf'), (default: \"\" ['fuzz'])\nextension = \"\"\n
"},{"location":"features/trident-manifest/#run_time","title":"run_time","text":"
[honggfuzz]\n# Number of seconds this fuzzing session will last (default: 0 [no limit])\nrun_time = 0\n
"},{"location":"features/trident-manifest/#max_file_size","title":"max_file_size","text":"
[honggfuzz]\n# Maximal size of files processed by the fuzzer in bytes\n# (default: 1048576 = 1MB)\nmax_file_size = 1048576\n
"},{"location":"features/trident-manifest/#save_all","title":"save_all","text":"
[honggfuzz]\n# Save all test-cases\n# (not only the unique ones) by appending the current\n# time-stamp to the filename (default: false)\nsave_all = false\n

Tip

Consider checking the Examples section for more tips.

"},{"location":"get-help/get-help/","title":"Get Help","text":"

Need help writing Fuzz Tests? Do not hesitate to join our Discord server!

"},{"location":"installation/installation/","title":"Installation","text":"

Important

Prerequisite

It is expected that you have installed:

For supported versions check the Supported Versions

"},{"location":"installation/installation/#install-system-dependencies","title":"Install System Dependencies","text":"

Update your package list and install the required packages:

sudo apt-get update\nsudo apt-get install -y \\\n    curl \\\n    git \\\n    build-essential \\\n    pkg-config \\\n    libssl-dev \\\n    npm \\\n    vim \\\n    nano \\\n    wget \\\n    binutils-dev \\\n    libunwind-dev \\\n    lldb\n
"},{"location":"installation/installation/#install-hongfuzz","title":"Install Hongfuzz","text":"

Install honggfuzz

cargo install honggfuzz\n
"},{"location":"installation/installation/#install-trident","title":"Install Trident","text":"
cargo install trident-cli\n
"},{"location":"installation/installation/#supported-versions","title":"Supported versions","text":"Trident CLI Anchor Solana Rust Honggfuzz develop 0.30.1 ^1.17.4 nightly 0.5.56 0.8.0 0.30.1 ^1.17.4 nightly 0.5.56 0.7.0 >=0.29.*1 ^1.17.4 nightly 0.5.56 0.6.0 >=0.29.*1 ^1.17 nightly 0.5.55 0.5.0 ~0.28.* =1.16.6 - - 0.4.0 ~0.27.* >=1.15 - - 0.3.0 ~0.25.* >=1.10 - - 0.2.0 ~0.24.* >=1.9 - -
  1. To use Trident with Anchor 0.29.0, run the following commands from your project's root directory after Trident initialization:
    cargo update anchor-client@0.30.0 --precise 0.29.0\ncargo update anchor-spl@0.30.0 --precise 0.29.0\n
"},{"location":"writing-fuzz-test/writing-fuzz-test/","title":"Writing fuzz test","text":""},{"location":"writing-fuzz-test/writing-fuzz-test/#initialize-trident","title":"Initialize Trident","text":"

Initialize Trident in the Anchor-based workspace.

trident init\n

Info

Trident under the hood

Tip

If you have Trident already initialized, you can add new fuzz test using trident fuzz add.

"},{"location":"writing-fuzz-test/writing-fuzz-test/#fill-the-fuzz-test-template","title":"Fill the Fuzz test Template","text":""},{"location":"writing-fuzz-test/writing-fuzz-test/#derive-accountssnapshots","title":"Derive AccountsSnapshots","text":"

For every Account Context specified in the Anchor project derive AccountsSnapshots such as:

// ...\n\nuse trident_derive_accounts_snapshots::AccountsSnapshots;\n\n// ...\n\n\n#[derive(Accounts, AccountsSnapshots)]\npub struct InitializeContext<'info> {\n    #[account(mut)]\n    pub author: Signer<'info>,\n    #[account(\n        init,\n        payer=author,\n        space=8+100,\n        seeds=[b\"hello_world_seed\"],\n        bump\n    )]\n    pub hello_world_account: Account<'info, StoreHelloWorld>,\n    pub system_program: Program<'info, System>,\n    // ...\n\n}\n
"},{"location":"writing-fuzz-test/writing-fuzz-test/#link-the-accountssnapshots","title":"Link the AccountsSnapshots","text":"

Fuzz Instructions use the derived AccountsSnapshots. You need to link the derived AccountsSnapshots to the corresponding aliases.

Important

Modules where the Contexts (with the derived AccountsSnapshots) are specified need to be public.

// fuzz_instructions.rs\n\nuse hello_world::trident_fuzz_initialize_context_snapshot::InitializeContextAlias;\n\ntype InitializeFnSnapshot<'info> = InitializeContextAlias<'info>;\n

Tip

For more examples, check the Examples.

"},{"location":"writing-fuzz-test/writing-fuzz-test/#define-fuzz-accounts","title":"Define Fuzz Accounts","text":"

Define AccountsStorage type for each Account you would like to use.

Important

Keep in mind:

#[doc = r\" Use AccountsStorage<T> where T can be one of:\"]\n#[doc = r\" Keypair, PdaStore, TokenStore, MintStore, ProgramStore\"]\n#[derive(Default)]\npub struct FuzzAccounts {\n    author: AccountsStorage<Keypair>,\n    hello_world_account: AccountsStorage<PdaStore>,\n    // No need to fuzz system_program\n    // system_program: AccountsStorage<todo!()>,\n}\n

Tip

For more details about the AccountsStorage check AccountsStorage.

"},{"location":"writing-fuzz-test/writing-fuzz-test/#implement-fuzz-instructions","title":"Implement Fuzz Instructions","text":"

Each Instruction in the Fuzz Test has to have defined the following functions:

Tip

"},{"location":"writing-fuzz-test/writing-fuzz-test/#execute","title":"Execute","text":""},{"location":"writing-fuzz-test/writing-fuzz-test/#run-fuzz-test","title":"Run Fuzz Test","text":"

To execute the desired fuzz test using the Honggfuzz, run:

# Replace <TARGET_NAME> with the name of particular\n# fuzz test (for example: \"fuzz_0\")\ntrident fuzz run-hfuzz <TARGET_NAME>\n
"},{"location":"writing-fuzz-test/writing-fuzz-test/#debug-fuzz-test","title":"Debug Fuzz Test","text":"

To debug your program using Honggfuzz with values from a crash file:

# fuzzer will run the <TARGET_NAME> with the specified <CRASH_FILE_PATH>\ntrident fuzz debug-hfuzz <TARGET_NAME> <CRASH_FILE_PATH>\n

Tip

By default, the crashfiles are stored in the

Tip

For more info about the fuzzing outputs chech the Commands

"}]} \ No newline at end of file