Skip to content

Instantly share code, notes, and snippets.

@Aimeedeer
Created March 7, 2023 20:49
Show Gist options
  • Save Aimeedeer/10afb7985a2a4df1f31ebe5330812515 to your computer and use it in GitHub Desktop.
Save Aimeedeer/10afb7985a2a4df1f31ebe5330812515 to your computer and use it in GitHub Desktop.
Notes: Cargo fuzz coverage for Soroban timelock
# Cargo fuzz coverage for Soroban timelock
```
$ cargo +nightly fuzz coverage fuzz_target_2
warning: Patch `soroban-auth v0.6.0 (/rs-soroban-sdk/soroban-auth)` was not used in the crate graph.
Check that the patched package version and available features are compatible
with the dependency requirements. If the patch has a different version from
what is locked in the Cargo.lock file, run `cargo update` to use the new
version. This may also occur with an optional dependency that is not enabled.
Finished release [optimized + debuginfo] target(s) in 1.68s
Generating coverage data for "01ac89a78eba9e594865a5c872784b2b9058b92d"
warning: Patch `soroban-auth v0.6.0 (/rs-soroban-sdk/soroban-auth)` was not used in the crate graph.
Check that the patched package version and available features are compatible
with the dependency requirements. If the patch has a different version from
what is locked in the Cargo.lock file, run `cargo update` to use the new
version. This may also occur with an optional dependency that is not enabled.
Finished release [optimized + debuginfo] target(s) in 0.27s
Running `fuzz/target/x86_64-unknown-linux-gnu/release/fuzz_target_2 -artifact_prefix=/soroban-examples/timelock/fuzz/artifacts/fuzz_target_2/ /soroban-examples/timelock/fuzz/corpus/fuzz_target_2/01ac89a78eba9e594865a5c872784b2b9058b92d`
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 3549841290
INFO: Loaded 1 modules (1091042 inline 8-bit counters): 1091042 [0x562c0d1e59d0, 0x562c0d2effb2),
INFO: Loaded 1 PC tables (1091042 PCs): 1091042 [0x562c0d2effb8,0x562c0e395dd8),
fuzz/target/x86_64-unknown-linux-gnu/release/fuzz_target_2: Running 1 inputs 1 time(s) each.
Running: /soroban-examples/timelock/fuzz/corpus/fuzz_target_2/01ac89a78eba9e594865a5c872784b2b9058b92d
Executed /soroban-examples/timelock/fuzz/corpus/fuzz_target_2/01ac89a78eba9e594865a5c872784b2b9058b92d in 115 ms
***
*** NOTE: fuzzing was not performed, you have only
*** executed the target code on a fixed set of inputs.
***
Generating coverage data for "98ca8d9b3777aa379e144b8342f428ace51baaf0"
warning: Patch `soroban-auth v0.6.0 (/rs-soroban-sdk/soroban-auth)` was not used in the crate graph.
```
use `llvm-profdata`:
```
$ llvm-profdata merge -sparse fuzz/coverage/fuzz_target_2/raw -o fuzz/coverage/fuzz_target_2/coverage.profdata
```
use `llvm-cov show`:
<screenshot>
```
$ llvm-cov show target/x86_64-unknown-linux-gnu/coverage/x
86_64-unknown-linux-gnu/release/fuzz_target_2 -instr-profile=fuzz/coverage/fuzz_target_2/coverage.profdata
/rs-soroban-env/soroban-env-common/src/arbitrary.rs:
1| |//! Implementations of [`Arbitrary`] for contract types.
2| |
3| |#![cfg(feature = "testutils")]
4| |
5| |extern crate alloc;
6| |
7| |use crate::symbol::Symbol;
8| |use crate::xdr::{ScStatic, ScStatus};
9| |use crate::{BitSet, RawVal, Static, Status};
10| |use alloc::string::String;
11| |use alloc::vec::Vec as RustVec;
12| |use arbitrary::{Arbitrary, Unstructured};
13| |
14| |impl<'a> Arbitrary<'a> for Symbol {
15| 0| fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
16| 0| let choices = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
17| 0| let choices: RustVec<char> = choices.chars().collect();
18| 0|
19| 0| let mut buf = String::new();
20| |
21| 0| let len = u.int_in_range(0..=10)?;
22| |
23| 0| for _ in 0..len {
24| 0| let choice = u.choose(&choices);
25| 0| match choice {
26| 0| Ok(ch) => {
27| 0| buf.push(*ch);
28| 0| }
29| | Err(_) => {
30| 0| break;
31| | }
32| | }
33| | }
34| |
```
use `llvm-cov report`:
```
$ llvm-cov report target/x86_64-unknown-linux-gnu/coverage/x86_64-unknown-linux-gnu/release/fuzz_target_2 -instr-profile=fuzz/coverage/fuzz_target_2/coverage.profdata
Filename Regions Missed Regions Cover Functions Missed Functions Executed Lines Missed Lines Cover Branches Missed Branches Cover
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/rs-soroban-env/soroban-env-common/src/arbitrary.rs 28 28 0.00% 4 4 0.00% 37 37 0.00% 0 0 -
/rs-soroban-env/soroban-env-common/src/array.rs 38 38 0.00% 10 10 0.00% 46 46 0.00% 0 0 -
/rs-soroban-env/soroban-env-common/src/bitset.rs 12 12 0.00% 9 9 0.00% 25 25 0.00% 0 0 -
/rs-soroban-env/soroban-env-common/src/compare.rs 56 33 41.07% 9 3 66.67% 72 39 45.83% 0 0 -
/rs-soroban-env/soroban-env-common/src/env_val.rs 135 83 38.52% 24 17 29.17% 141 84 40.43% 0 0 -
/rs-soroban-env/soroban-env-common/src/invoker.rs 12 12 0.00% 7 7 0.00% 16 16 0.00% 0 0 -
...
--------------------------------------------------------------------------------------------
TOTAL 78785 73158 7.14% 23042 21502 6.68% 114236 103496 9.40% 0 0 -
```
`llvm-cov show` with filter `-name=timelock`:
```
$ llvm-cov show target/x86_64-unknown-linux-gnu/coverage/x86_64-unknown-linux-gnu/release/fuzz_target_2 -instr-profile=fuzz/coverage/fuzz_target_2/coverage.profdata -name=timelock
_RNvXNtCsebBT0MTtZjd_18soroban_env_common7env_valNtNtB4_7raw_val6RawValINtB2_10TryIntoValNtNtCs3kZVoCPPN4x_11soroban_sdk3env3EnvNtCscznx7R4neHg_25soroban_timelock_contract13TimeBoundKindE12try_into_valCscUqQUv7tGtL_13fuzz_target_2:
34| 32.6k| fn try_into_val(&self, env: &E) -> Result<T, Self::Error> {
35| 32.6k| T::try_from_val(env, self)
36| 32.6k| }
_RNvXNtCsebBT0MTtZjd_18soroban_env_common7env_valNtNtB4_7raw_val6RawValINtB2_10TryIntoValNtNtCs3kZVoCPPN4x_11soroban_sdk3env3EnvNtCscznx7R4neHg_25soroban_timelock_contract9TimeBoundE12try_into_valCscUqQUv7tGtL_13fuzz_target_2:
34| 32.6k| fn try_into_val(&self, env: &E) -> Result<T, Self::Error> {
35| 32.6k| T::try_from_val(env, self)
36| 32.6k| }
_RNvXNtCsebBT0MTtZjd_18soroban_env_common7env_valNtNtB4_7raw_val6RawValINtB2_10TryIntoValNtNtCs3kZVoCPPN4x_11soroban_sdk3env3EnvINtNtB1s_3vec3VecBK_EE12try_into_valCscznx7R4neHg_25soroban_timelock_contract:
34| 10.6k| fn try_into_val(&self, env: &E) -> Result<T, Self::Error> {
35| 10.6k| T::try_from_val(env, self)
36| 10.6k| }
_RNCNvXs7_NtCsebBT0MTtZjd_18soroban_env_common7env_valNtNtNtCskkZpHXSrmTK_11stellar_xdr4next9generated5ScValINtB7_10TryFromValNtNtCs3kZVoCPPN4x_11soroban_sdk3env3EnvNtNtB9_7raw_val6RawValE12try_from_val0Cscznx7R4neHg_25soroban_timelock_contract:
197| 0| let scob = ScObject::try_from_val(&env, &ob).map_err(|_| ConversionError)?;
_RINvNtCsebBT0MTtZjd_18soroban_env_common7env_val15log_err_convertyNtNtCs3kZVoCPPN4x_11soroban_sdk3env3EnvNtNtB4_7raw_val6RawValECscznx7R4neHg_25soroban_timelock_contract:
39| 0|pub(crate) fn log_err_convert<T>(env: &impl Env, val: &impl AsRef<RawVal>) {
40| 0| // Logging here is best-effort; ignore failures (they only arise if we're
41| 0| // out of gas or something otherwise-unrecoverable).
42| 0| let _ = env.log_static_fmt_val_static_str(
43| 0| "can't convert {} to {}",
44| 0| *val.as_ref(),
45| 0| core::any::type_name::<T>(),
46| 0| );
47| 0|}
...
```
use `llvm-cov show` with `-Xdemangler=rustfilt`:
```
$ llvm-cov show -Xdemangler=rustfilt target/x86_64-unknown-linux-gnu/coverage/x86_64-unknown-linux-gnu/release/fuzz_target_2 -instr-profile=fuzz/coverage/fuzz_target_2/coverage.profdata -name=timelock
<soroban_env_common::raw_val::RawVal as soroban_env_common::env_val::TryIntoVal<soroban_sdk::env::Env, soroban_timelock_contract::TimeBoundKind>>::try_into_val:
34| 32.6k| fn try_into_val(&self, env: &E) -> Result<T, Self::Error> {
35| 32.6k| T::try_from_val(env, self)
36| 32.6k| }
<soroban_env_common::raw_val::RawVal as soroban_env_common::env_val::TryIntoVal<soroban_sdk::env::Env, soroban_timelock_contract::TimeBound>>::try_into_val:
34| 32.6k| fn try_into_val(&self, env: &E) -> Result<T, Self::Error> {
35| 32.6k| T::try_from_val(env, self)
36| 32.6k| }
<soroban_env_common::raw_val::RawVal as soroban_env_common::env_val::TryIntoVal<soroban_sdk::env::Env, soroban_sdk::vec::Vec<soroban_env_common::raw_val::RawVal>>>::try_into_val:
34| 10.6k| fn try_into_val(&self, env: &E) -> Result<T, Self::Error> {
35| 10.6k| T::try_from_val(env, self)
36| 10.6k| }
<stellar_xdr::next::generated::ScVal as soroban_env_common::env_val::TryFromVal<soroban_sdk::env::Env, soroban_env_common::raw_val::RawVal>>::try_from_val::{closure#0}:
197| 0| let scob = ScObject::try_from_val(&env, &ob).map_err(|_| ConversionError)?;
soroban_env_common::env_val::log_err_convert::<u64, soroban_sdk::env::Env, soroban_env_common::raw_val::RawVal>:
39| 0|pub(crate) fn log_err_convert<T>(env: &impl Env, val: &impl AsRef<RawVal>) {
40| 0| // Logging here is best-effort; ignore failures (they only arise if we're
41| 0| // out of gas or something otherwise-unrecoverable).
42| 0| let _ = env.log_static_fmt_val_static_str(
43| 0| "can't convert {} to {}",
44| 0| *val.as_ref(),
45| 0| core::any::type_name::<T>(),
46| 0| );
47| 0|}
...
```
`llvm-cov show` with filter `-name=fuzz_target_2` and demangler:
```
$ llvm-cov show -Xdemangler=rustfilt target/x86_64-unknown-linux-gnu/coverage/x86_64-unknown-linux-gnu/release/fuzz_target_2 -instr-profile=fuzz/coverage/fuzz_target_2/coverage.profdata -name=fuzz_target_2
<soroban_env_common::raw_val::RawVal as soroban_env_common::env_val::TryIntoVal<soroban_sdk::env::Env, i128>>::try_into_val:
34| 32.6k| fn try_into_val(&self, env: &E) -> Result<T, Self::Error> {
35| 32.6k| T::try_from_val(env, self)
36| 32.6k| }
<u64 as soroban_env_common::env_val::TryFromVal<soroban_sdk::env::Env, soroban_env_common::raw_val::RawVal>>::try_from_val::{closure#0}:
90| 0| Ok(env.obj_to_u64(obj).map_err(|_| ConversionError)?)
<i128 as soroban_env_common::env_val::TryFromVal<soroban_sdk::env::Env, soroban_env_common::raw_val::RawVal>>::try_from_val:
111| 70.6k| fn try_from_val(env: &E, v: &RawVal) -> Result<Self, Self::Error> {
112| 70.6k| let v = *v;
113| 70.6k| let obj = v.try_into()?;
114| 70.6k| let lo = env.obj_to_i128_lo64(obj).map_err(|_| ConversionError)?;
115| 70.6k| let hi = env.obj_to_i128_hi64(obj).map_err(|_| ConversionError)?;
116| 70.6k| let u: u128 = (lo as u128) | ((hi as u128) << 64);
117| 70.6k| Ok(u as i128)
118| 70.6k| }
```
filter with `name=deposit`:
```
$ llvm-cov show -Xdemangler=rustfilt target/x86_64-unknown-linux-gnu/coverage/x86_64-unknown-linux-gnu/release/fuzz_target_2 -instr-profile=fuzz/coverage/fuzz_target_2/coverage.profdata -name=deposit
soroban_timelock_contract::__deposit::invoke_raw_slice:
58| 9.82k|#[contractimpl]
<soroban_timelock_contract::ClaimableBalanceContract>::deposit:
60| 9.82k| pub fn deposit(
61| 9.82k| env: Env,
62| 9.82k| from: Address,
63| 9.82k| token: BytesN<32>,
64| 9.82k| amount: i128,
65| 9.82k| claimants: Vec<Address>,
66| 9.82k| time_bound: TimeBound,
67| 9.82k| ) {
68| 9.82k| if claimants.len() > 10 {
69| 98| panic!("too many claimants");
70| 9.72k| }
71| 9.72k| if is_initialized(&env) {
72| 5.19k| panic!("contract has been already initialized");
73| 4.53k| }
74| 4.53k| // Make sure `from` address authorized the deposit call with all the
75| 4.53k| // arguments.
76| 4.53k| from.require_auth();
77| 4.53k|
78| 4.53k| // Transfer token from `from` to this contract address.
79| 4.53k| token::Client::new(&env, &token).xfer(&from, &env.current_contract_address(), &amount);
80| 4.53k| // Store all the necessary info to allow one of the claimants to claim it.
81| 4.53k| env.storage().set(
82| 4.53k| &DataKey::Balance,
83| 4.53k| &ClaimableBalance {
84| 4.53k| token,
85| 4.53k| amount,
86| 4.53k| time_bound,
87| 4.53k| claimants,
88| 4.53k| },
89| 4.53k| );
90| 4.53k| // Mark contract as initialized to prevent double-usage.
91| 4.53k| // Note, that this is just one way to approach initialization - it may
92| 4.53k| // be viable to allow one contract to manage several claimable balances.
93| 4.53k| env.storage().set(&DataKey::Init, &());
94| 4.53k| }
<soroban_timelock_contract::ClaimableBalanceContractClient>::try_deposit:
58| 0|#[contractimpl]
soroban_timelock_contract::__deposit::invoke_raw:
58| 9.82k|#[contractimpl]
<soroban_timelock_contract::ClaimableBalanceContractClient>::deposit:
58| 9.82k|#[contractimpl]
<soroban_timelock_contract::ClaimableBalanceContractClient>::try_deposit::{closure#0}:
58| 0|#[contractimpl]
<soroban_timelock_contract::ClaimableBalanceContractClient>::with_env::<(), <soroban_timelock_contract::ClaimableBalanceContractClient>::deposit::{closure#0}>:
58| 9.82k|#[contractimpl]
<soroban_timelock_contract::ClaimableBalanceContractClient>::deposit::{closure#0}:
58| 9.82k|#[contractimpl]
<soroban_timelock_contract::ClaimableBalanceContract>::spec_xdr_deposit:
58| 0|#[contractimpl]
<soroban_timelock_contract::ClaimableBalanceContractClient>::with_env::<core::result::Result<core::result::Result<(), soroban_env_common::raw_val::ConversionError>, core::result::Result<soroban_env_common::status::Status, core::convert::Infallible>>, <soroban_timelock_contract::ClaimableBalanceContractClient>::try_deposit::{closure#0}>:
58| 0|#[contractimpl]
```
filter with `name=claim`:
```
$ llvm-cov show -Xdemangler=rustfilt target/x86_64-unknown-linux-gnu/coverage/x86_64-unknown-linux-gnu/release/fuzz_target_2 -instr-profile=fuzz/coverage/fuzz_target_2/coverage.profdata -name=claim
fuzz_target_2::create_claimable_balance_contract:
271| 2.28k|fn create_claimable_balance_contract(e: &Env) -> ClaimableBalanceContractClient {
272| 2.28k| ClaimableBalanceContractClient::new(e, &e.register_contract(None, ClaimableBalanceContract {}))
273| 2.28k|}
<soroban_timelock_contract::ClaimableBalanceContract>::spec_xdr_claim:
58| 0|#[contractimpl]
<soroban_timelock_contract::ClaimableBalanceContractClient>::with_env::<core::result::Result<core::result::Result<(), soroban_env_common::raw_val::ConversionError>, core::result::Result<soroban_env_common::status::Status, core::convert::Infallible>>, <soroban_timelock_contract::ClaimableBalanceContractClient>::try_claim::{closure#0}>:
58| 0|#[contractimpl]
<soroban_timelock_contract::ClaimableBalanceContract>::claim:
96| 2.08k| pub fn claim(env: Env, claimant: Address) {
97| 2.08k| // Make sure claimant has authorized this call, which ensures their
98| 2.08k| // identity.
99| 2.08k| claimant.require_auth();
100| 2.08k|
101| 2.08k| let claimable_balance: ClaimableBalance =
102| 2.08k| env.storage().get_unchecked(&DataKey::Balance).unwrap();
103| 2.08k|
104| 2.08k| if !check_time_bound(&env, &claimable_balance.time_bound) {
105| 692| panic!("time predicate is not fulfilled");
106| 1.39k| }
107| 1.39k|
108| 1.39k| let claimants = &claimable_balance.claimants;
109| 1.39k| if !claimants.contains(&claimant) {
110| 90| panic!("claimant is not allowed to claim this balance");
111| 1.30k| }
112| 1.30k|
113| 1.30k| // Transfer the stored amount of token to claimant after passing
114| 1.30k| // all the checks.
115| 1.30k| token::Client::new(&env, &claimable_balance.token).xfer(
116| 1.30k| &env.current_contract_address(),
117| 1.30k| &claimant,
118| 1.30k| &claimable_balance.amount,
119| 1.30k| );
120| 1.30k| // Remove the balance entry to prevent any further claims.
121| 1.30k| env.storage().remove(&DataKey::Balance);
122| 1.30k| }
<soroban_timelock_contract::ClaimableBalanceContractClient>::claim::{closure#0}:
58| 2.08k|#[contractimpl]
soroban_timelock_contract::__claim::invoke_raw:
58| 2.08k|#[contractimpl]
<soroban_timelock_contract::ClaimableBalanceContractClient>::with_env::<(), <soroban_timelock_contract::ClaimableBalanceContractClient>::claim::{closure#0}>:
58| 2.08k|#[contractimpl]
<soroban_timelock_contract::ClaimableBalanceContractClient>::claim:
58| 2.08k|#[contractimpl]
<soroban_timelock_contract::ClaimableBalanceContractClient>::try_claim:
58| 0|#[contractimpl]
soroban_timelock_contract::__claim::invoke_raw_slice:
58| 2.08k|#[contractimpl]
<soroban_timelock_contract::ClaimableBalanceContractClient>::try_claim::{closure#0}:
58| 0|#[contractimpl]
```
add file allowlist.txt:
```
<soroban_timelock_contract::ClaimableBalanceContract>::deposit
<soroban_timelock_contract::ClaimableBalanceContract>::claim
```
It doesn't work so far.
```
$ llvm-cov show -Xdemangler=rustfilt target/x86_64-unknown-linux-gnu/coverage/x86_64-unknown-linux-gnu/release/fuzz_target_2 -instr-profile=fuzz/coverage/fuzz_target_2/coverage.profdata -name-allowlist=fuzz/allowlist.txt
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment