Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TOB-FUEL-32: ABI decoder is crashing on certain inputs #1108

Closed
xgreenx opened this issue Aug 28, 2023 · 0 comments · Fixed by #1130
Closed

TOB-FUEL-32: ABI decoder is crashing on certain inputs #1108

xgreenx opened this issue Aug 28, 2023 · 0 comments · Fixed by #1130
Assignees
Labels
audit-report Related to the audit report

Comments

@xgreenx
Copy link
Contributor

xgreenx commented Aug 28, 2023

Description

A fuzzing campaign for the ABI decoder revealed several crashes. This finding serves as an umbrella finding to list all the different crashes we observed. The harness for the fuzzer is shown in the following figure.

Figure 32.1: Fuzz harness

#[test_fuzz::test_fuzz]
pub fn fuzz_abi_decode(bytes: &[u8], param_type: ParamType)
{
    ABIDecoder::decode_single(&param_type, bytes);
}

The findings are relevant for use cases where untrusted ABI definitions are parsed. All the findings below are due to malformed type definitions. These types are not controlled by Sway programs, but by the compiler which emits an ABI definition.
This finding is Undetermined because it was not part of the initial scope.

Division of zero

The following unit test panics with attempt to calculate the remainder with a divisor of zero at fuels-rs/packages/fuels-core/src/types/param_types.rs:76.

Figure 32.2: Unit test that panics

#[test]
pub fn div_zero() {
    use crate::types::param_types::ParamType::*;
    fuzz_abi_decode(&[], Vector(Box::new(Array(Box::new(U16), 0))));
}

Attempt to multiply with overflow

The following test panics at fuels-rs/packages/fuels-core/src/codec/abi_decoder.rs:256 with attempt to multiply with overflow.

Figure 32.3: Unit test that panics

#[test]
pub fn multiplay_overflow_enum() {
    use crate::types::enum_variants::EnumVariants;
    use crate::types::param_types::ParamType::*;
    fuzz_abi_decode(
        &[0, 8, 8, 8, 9, 8, 0, 8, 8, 8, 8, 8, 15, 8, 8, 8],
        Enum {
            variants: EnumVariants {
                param_types: vec![
                    Array(Box::new(Array(Box::new(RawSlice), 8)),
576469587185895432),
                    B256,
                    B256,
                    B256,
                    B256,
                    B256,
                    B256,
                    B256,
                    B256,
                    B256,
                    B256,
      ], },
                  generics: vec![U16],
              },
      ); 
}

The next panic is caused by fuels-rs/packages/fuels-core/src/types/param_types.rs:76.

Figure 32.4: Unit test that panics

#[test]
pub fn multiplay_overflow_vector() {
    use crate::types::param_types::ParamType::*;
    fuzz_abi_decode(
        &[8, 8, 10, 7, 229, 8, 8, 8],
        Vector(Box::new(Array(Box::new(Unit), 2308103648053880071))),
    );
}

The next unit tests panics at fuels-rs/packages/fuels-core/src/types/param_types.rs:155.

Figure 32.5: Unit test that panics

#[test]
pub fn multiplay_overflow_arith() {
    use crate::types::enum_variants::EnumVariants;
    use crate::types::param_types::ParamType;
    use crate::types::param_types::ParamType::*;
    let mut typ: ParamType = U16;
    for _ in 0..50 {
        typ = Array(Box::new(typ), 8);
    }
        fuzz_abi_decode(
            &[0, 8, 8, 51, 51, 51, 51, 51, 51, 51, 3, 8, 15, 8, 8, 8],
            Enum {
                variants: EnumVariants {
                    param_types: vec![typ],
    },
                generics: vec![U16],
            },
    ); 
}

Capacity overflow

The following unit tests panics with the message capacity overflow at fuels-rs/packages/fuels-core/src/codec/abi_decoder.rs:138.

Figure 32.6: Unit test that panics

#[test]
pub fn capacity_overflow() {
    use crate::types::param_types::ParamType::*;
    fuzz_abi_decode(
        &[13, 0, 1, 0, 0, 106, 242, 8],
        Array(
            Box::new(Array(Box::new(Tuple(vec![])), 7638104972323651592)),
        242, ),
        ); 
}

Stack overflow

The following test crashes with a stack overflow. It is possible to construct deeply nested ParamTypes which when consumed by the parser cause a stack overflow. Depending on the system configuration it might be required to change the 13500 in order to witness the crash.
The stack overflow here is caused by fuels-rs/packages/fuels-core/src/codec/abi_decoder.rs:60-63.

Figure 32.7: Unit test that crashes with a stack overflow.

#[test]
pub fn stack_overflow() {
    use crate::types::param_types::ParamType;
    use crate::types::param_types::ParamType::*;
    let mut typ: ParamType = U16;
    for _ in 0..13500 {
        typ = Vector(Box::new(typ));
    }
        fuzz_abi_decode(
            &[8, 9, 9, 9, 9, 9, 9, 9],
            typ
    ); 
}

Memory allocation

The following unit test can panic with a failed memory allocation at fuels-rs/packages/fuels-core/src/codec/abi_decoder.rs:138. This is related to the “Capacity overflow” already mentioned in this finding. If the array length is lower than the same attack vector might lead to a hang of the parser. If it is higher, then it might lead to a capacity overflow like described above.

Figure 32.8: Unit test that crashed due to large memory allocation.

#[test]
pub fn capacity_maloc() {
    use crate::types::param_types::ParamType::*;
    fuzz_abi_decode(
        &[8, 8, 7, 252, 201, 8, 8, 8],
        Array(Box::new(U8), 72340198607880449)
    );
}

Recommendations

Short term, avoid panicing by handling the errors appropriately. For the stack overflows, make sure to avoid recursive calls and instead implement parsing iteratively. Also, setup reasonable length limits for types which include a length like Arrays.
Long term, deploy a fuzzer for the ABI decoding parser. The above fuzz harness can be used together with Trail of Bit’s test-fuzz fuzzer. The instructions for setting it up can be found in the documentation of the project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
audit-report Related to the audit report
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants