DEV Community

Cover image for How to rust binding for libFuzzer
Ramin Farajpour Cami
Ramin Farajpour Cami

Posted on Edited on

How to rust binding for libFuzzer

How to Rust bindings for libFuzzer

You can use the Rust fuzz ecosystem, which provides a convenient way to integrate fuzz testing into your Rust projects

Here's a step-by-step guide on how to create Rust bindings for libFuzzer:

What is libfuzzer?

libFuzzer is a highly efficient coverage-guided fuzz testing tool that is part of the LLVM (Low-Level Virtual Machine) project. Fuzz testing, or fuzzing, is a software testing technique in which a program is subjected to a large volume of random, invalid, or unexpected input data to discover vulnerabilities, crashes, or other issues.

Download libfuzzer C++ source code :

https://github.com/llvm/llvm-project/tree/main/compiler-rt/lib/fuzzer

Rust version

[17:40] raminfp@zenbook: $ rustc -vV
rustc 1.75.0-nightly (75b064d26 2023-11-01)
binary: rustc
commit-hash: 75b064d26970ca8e7a487072f51835ebb057d575
commit-date: 2023-11-01
host: x86_64-unknown-linux-gnu
release: 1.75.0-nightly
LLVM version: 17.0.4
Enter fullscreen mode Exit fullscreen mode

Building for libfuzzer c++ code in the build.rs

        let mut build = cc::Build::new();
        let sources = ::std::fs::read_dir("libfuzzer")
            .expect("listable source directory")
            .map(|de| de.expect("file in directory").path())
            .filter(|p| p.extension().map(|ext| ext == "cpp") == Some(true))
            .collect::<Vec<_>>();
        for source in sources.iter() {
            build.file(source.to_str().unwrap());
        }
        build.flag("-std=c++11");
        build.flag("-fno-omit-frame-pointer");
        build.flag("-w");
        build.cpp(true);
        build.compile("libfuzzer.a");
Enter fullscreen mode Exit fullscreen mode

Add wrapper function LLVMFuzzerTestOneInput This wrapper function is designed to call a Rust function rust_fuzzer_test_input with input data obtained from the fuzzer.

use std::slice::from_raw_parts;

extern "C" {
    fn rust_fuzzer_test_input(data: *const u8, len: usize);
}

#[no_mangle]
pub extern "C" fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32 {

    let _ = unsafe { from_raw_parts(data, size) };

    unsafe {
        rust_fuzzer_test_input(data, size);
    }
    0
}
Enter fullscreen mode Exit fullscreen mode

Now starting fuzzing your libs with libfuzzer in rust.

$ cargo new --bin example
$ cd example
Enter fullscreen mode Exit fullscreen mode

Then add a dependency on the fuzzer

[dependencies]
myfuzzer = { path = ".." }
your_crate_libs = "*"
Enter fullscreen mode Exit fullscreen mode

And change code in your case ex: src/main.rs to fuzz my code:

#![no_main]
extern crate myfuzzer;

#[export_name="rust_fuzzer_test_input"]
pub fn fuzz(data: &[u8]) {
    // your fuzz code here
    // println!("{:?}", data);
    if data == b"A" {
        panic!("Oops!");
    }
}

Enter fullscreen mode Exit fullscreen mode

Finally, run the following commands:

$ [04:09] raminfp@zenbook:example (main) # pwd
/home/raminfp/Projects/Develop/libfuzzer/example
$ [03:18] raminfp@zenbook:example # cargo rustc -- \
    -C passes='sancov-module' \
    -C llvm-args='-sanitizer-coverage-level=3' \
    -C llvm-args='-sanitizer-coverage-inline-8bit-counters' \
    -Z sanitizer=address
$ ./target/debug/example
Enter fullscreen mode Exit fullscreen mode

Output:

[03:55] raminfp@zenbook:example # ./target/debug/example
INFO: Seed: 1157038402
INFO: Loaded 1 modules   (239 inline 8-bit counters): 239 [0x5556c7f79a80, 0x5556c7f79b6f), 
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2  INITED ft: 15 corp: 1/1b exec/s: 0 rss: 36Mb
#5  NEW    ft: 17 corp: 2/66b exec/s: 0 rss: 36Mb L: 65/65 MS: 3 CopyPart-CopyPart-InsertRepeatedBytes-
#58 REDUCE ft: 17 corp: 2/44b exec/s: 0 rss: 36Mb L: 43/43 MS: 3 InsertByte-InsertRepeatedBytes-EraseBytes-
#72 REDUCE ft: 17 corp: 2/25b exec/s: 0 rss: 36Mb L: 24/24 MS: 4 InsertByte-InsertByte-ChangeByte-EraseBytes-
#78 REDUCE ft: 17 corp: 2/24b exec/s: 0 rss: 36Mb L: 23/23 MS: 1 EraseBytes-
#80 REDUCE ft: 17 corp: 2/15b exec/s: 0 rss: 36Mb L: 14/14 MS: 2 CopyPart-EraseBytes-
#81 REDUCE ft: 17 corp: 2/14b exec/s: 0 rss: 36Mb L: 13/13 MS: 1 EraseBytes-
#133    REDUCE ft: 17 corp: 2/11b exec/s: 0 rss: 36Mb L: 10/10 MS: 2 ChangeBit-EraseBytes-
#171    REDUCE ft: 17 corp: 2/8b exec/s: 0 rss: 36Mb L: 7/7 MS: 3 ChangeBinInt-ChangeBit-EraseBytes-
#182    REDUCE ft: 17 corp: 2/6b exec/s: 0 rss: 36Mb L: 5/5 MS: 1 EraseBytes-
#184    REDUCE ft: 17 corp: 2/5b exec/s: 0 rss: 36Mb L: 4/4 MS: 2 InsertByte-EraseBytes-
#196    REDUCE ft: 17 corp: 2/3b exec/s: 0 rss: 36Mb L: 2/2 MS: 2 ChangeByte-EraseBytes-
thread '<unnamed>' panicked at src/main.rs:9:9:
Oops!
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
==44905== ERROR: libFuzzer: deadly signal
    #0 0x5556c7e6eaf1 in __sanitizer_print_stack_trace /rustc/llvm/src/llvm-project/compiler-rt/lib/asan/asan_stack.cpp:87:3
    #1 0x5556c7eac540 in fuzzer::Fuzzer::CrashCallback() /home/raminfp/Projects/Develop/libfuzzer-rs/libfuzzer/FuzzerLoop.cpp:233:38
    #2 0x5556c7eac3c6 in fuzzer::Fuzzer::StaticCrashSignalCallback() /home/raminfp/Projects/Develop/libfuzzer-rs/libfuzzer/FuzzerLoop.cpp:206:19
    #3 0x5556c7edb561 in fuzzer::CrashHandler(int, siginfo_t*, void*) /home/raminfp/Projects/Develop/libfuzzer-rs/libfuzzer/FuzzerUtilPosix.cpp:36:36
    #4 0x7efed284251f  (/lib/x86_64-linux-gnu/libc.so.6+0x4251f) (BuildId: a43bfc8428df6623cd498c9c0caeb91aec9be4f9)
    #5 0x7efed28969fb in __pthread_kill_implementation nptl/./nptl/pthread_kill.c:43:17
    #6 0x7efed28969fb in __pthread_kill_internal nptl/./nptl/pthread_kill.c:78:10
    #7 0x7efed28969fb in pthread_kill nptl/./nptl/pthread_kill.c:89:10
    #8 0x7efed2842475 in gsignal signal/../sysdeps/posix/raise.c:26:13
    #9 0x7efed28287f2 in abort stdlib/./stdlib/abort.c:79:7
    #10 0x5556c7ef8226 in std::sys::unix::abort_internal::haadb6d01b1ce5dcc /rustc/75b064d26970ca8e7a487072f51835ebb057d575/library/std/src/sys/unix/mod.rs:376:14
    #11 0x5556c7ddf7a6 in std::process::abort::hb7281d0b80ebbd50 /rustc/75b064d26970ca8e7a487072f51835ebb057d575/library/std/src/process.rs:2279:5
    #12 0x5556c7e945c5 in myfuzzer::test_input_wrap::_$u7b$$u7b$closure$u7d$$u7d$::h2f53ba4c17f7bd40 /home/raminfp/Projects/Develop/libfuzzer-rs/src/lib.rs:19:22
    #13 0x5556c7e94495 in core::option::Option$LT$T$GT$::map::h90a0dc092a0a2b55 /rustc/75b064d26970ca8e7a487072f51835ebb057d575/library/core/src/option.rs:1066:29
    #14 0x5556c7e94419 in LLVMFuzzerTestOneInput /home/raminfp/Projects/Develop/libfuzzer-rs/src/lib.rs:16:5
    #15 0x5556c7eadc73 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /home/raminfp/Projects/Develop/libfuzzer-rs/libfuzzer/FuzzerLoop.cpp:515:15
    #16 0x5556c7ead6d1 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) /home/raminfp/Projects/Develop/libfuzzer-rs/libfuzzer/FuzzerLoop.cpp:440:18
    #17 0x5556c7eae727 in fuzzer::Fuzzer::MutateAndTestOne() /home/raminfp/Projects/Develop/libfuzzer-rs/libfuzzer/FuzzerLoop.cpp:648:25
    #18 0x5556c7eaf3ae in fuzzer::Fuzzer::Loop(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, fuzzer::fuzzer_allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&) /home/raminfp/Projects/Develop/libfuzzer-rs/libfuzzer/FuzzerLoop.cpp:775:21
    #19 0x5556c7e9b3ac in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /home/raminfp/Projects/Develop/libfuzzer-rs/libfuzzer/FuzzerDriver.cpp:754:10
    #20 0x5556c7e94962 in main /home/raminfp/Projects/Develop/libfuzzer-rs/libfuzzer/FuzzerMain.cpp:20:30
    #21 0x7efed2829d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #22 0x7efed2829e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #23 0x5556c7de07f4 in _start (/home/raminfp/Projects/Develop/libfuzzer-rs/example/target/debug/example+0x127f4) (BuildId: 12917cdb5ef03b01c21eb307af3c31bd5867b94e)

NOTE: libFuzzer has rudimentary signal handlers.
      Combine libFuzzer with AddressSanitizer or similar for better crash reports.
SUMMARY: libFuzzer: deadly signal
MS: 1 ChangeByte-; base unit: adc83b19e793491b1c6ea0fd8b46cd9f32e592fc
0x41,
A
artifact_prefix='./'; Test unit written to ./crash-6dcd4ce23d88e2ee9568ba546c007c63d9131c1b
Base64: QQ==


Enter fullscreen mode Exit fullscreen mode

Crash file:


[03:20] raminfp@zenbook:example # exa
Cargo.lock  Cargo.toml  crash-6dcd4ce23d88e2ee9568ba546c007c63d9131c1b  src  target


[03:20] raminfp@zenbook:example # hexdump crash-6dcd4ce23d88e2ee9568ba546c007c63d9131c1b
0000000 0041                                   
0000001
Enter fullscreen mode Exit fullscreen mode

Thanks,

Source code : https://github.com/raminfp/libfuzzer-rs

Top comments (0)