Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

addisoncrump
Copy link
Collaborator

@addisoncrump addisoncrump commented Jan 3, 2023

So you want to fuzz some Rust code, eh? You can use the results of this PR by using cargo-fuzz and replacing the libfuzzer-sys dependency like so:

libfuzzer-sys = { version = "0.15.3", package = "libafl_libfuzzer" }

Adventurous users should pull from the latest git revisions:

libfuzzer-sys = { git = "https://github.com/AFLplusplus/LibAFL.git", package = "libafl_libfuzzer" }

Then simply use cargo-fuzz as normal. Some features are not available, but this should get you going with most fuzzing operations. Enjoy!

The original PR text is below.


Seeing as how libfuzzer has entered maintenance mode, we should provide a full libfuzzer alternative which stays up to date with modern fuzzing standards.

To this end, this PR seeks to offer libafl_libfuzzer, a full drop-in replacement for libfuzzer with support for the most common flags and sancov settings. Specifically, all of the things supported by cargo-fuzz. We can provide this shim to cargo-fuzz via environmental variable in libfuzzer-sys or, in the future, an init flag in cargo-fuzz itself.

We need to implement corpus merging (fairly straightforward with IndexesLenTimeMinimizingScheduler or cmin), crash minification (doable with tmin + InProcessForkExecutor), and a basic fuzzing runtime (optionally with dict support). cargo-fuzz uses many of the available sancov features, so we need some additional support for __san*cov items. As of writing, we have the following undefined references:

$ cargo fuzz build -s none |& grep -Eo "undefined reference to.*'" | sort -u 
undefined reference to `LLVMFuzzerMutate'
undefined reference to `__sancov_lowest_stack'
undefined reference to `__sanitizer_cov_pcs_init'
undefined reference to `__sanitizer_cov_trace_pc_indir'

I'm unfamiliar with __san*cov features, so I could use some help in developing that support.

I think this would be a good addition to the 0.9 release as well.

@addisoncrump addisoncrump self-assigned this Jan 3, 2023
@domenukk
Copy link
Member

domenukk commented Jan 4, 2023

pub fn LLVMFuzzerRunDriver(

Can we add support for the run FN and replace most of atheris, too?

@addisoncrump
Copy link
Collaborator Author

pub fn LLVMFuzzerRunDriver(

Can we add support for the run FN and replace most of atheris, too?

I'm unfamiliar with this aspect of the libfuzzer functionality. I'm sure we can replace it, though.

@domenukk
Copy link
Member

domenukk commented Jan 4, 2023

pub fn LLVMFuzzerRunDriver(

Can we add support for the run FN and replace most of atheris, too?

I'm unfamiliar with this aspect of the libfuzzer functionality. I'm sure we can replace it, though.

It's a way for the target to start fuzzing at another point in time, so an own main, instead of using libfuzzer's main. We can probably move all code to this method and call this fn from a weak main

@domenukk
Copy link
Member

domenukk commented Jan 4, 2023

AFLplusplus/AFLplusplus#1515

See this issue for the run method, too


void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) {
// unused
// TODO implement
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading through the documentation at https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards it looks like this is pretty straight forward, we just want a portion of the coverage map for indirect branches and then map them as feedback.
Also, it looks like the function is called __sanitizer_cov_trace_pc_indirect(void *callee) in newer compilers (or in some versions and the docs are old?) so maybe we want to export both

// take a page out of libfuzzer's book: static define __sancov_lowest_stack
// since we don't support it yet
// TODO support it
MAYBE_THREAD_LOCAL uintptr_t __sancov_lowest_stack;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Support can be as easy as exposing this value to rust via an fn - then we can build an observer that observes stack depth of a run, quasi for free

@Mrmaxmeier
Copy link
Contributor

Mrmaxmeier commented Jan 19, 2023

Hi,
I wanted to try this but I'm seeing a bunch of "multiple definition" linker errors for Rust runtime symbols (__rdl_alloc, rust_eh_personality, ...).
I'm setting CUSTOM_LIBFUZZER_PATH to <LibAFL>/target/debug/build/libafl_libfuzzer-00bdb9ec1c4f4eed/out/runtime-target/x86_64-unknown-linux-gnu/release/libafl_libfuzzer_runtime.a (without the rust-fuzz/libfuzzer#103 fix applied).

Am I holding it wrong? (Compiling to a cdylib instead of staticlib or using the .rlib instead of the .a doesn't seem to work either)

@addisoncrump
Copy link
Collaborator Author

addisoncrump commented Jan 19, 2023

Hey, thanks for giving this a go. Try building it from within libafl_libfuzzer_runtime directly instead? It sounds like what your doing should work, so if you're still having issues I can investigate with you in Discord. Also, make sure you're building with the same toolchain as your default (e.g., avoid +nightly or +stable flags, I haven't sorted out a bug there just yet).

@Mrmaxmeier
Copy link
Contributor

Building libafl_libfuzzer_runtime directly works 👍
The -Zbuild-std line in the wrapper build script seems to break stuff on my machine 🤷

@addisoncrump
Copy link
Collaborator Author

07abe31 is blocked by #1067

@addisoncrump addisoncrump force-pushed the libfuzzer branch 4 times, most recently from 10f4099 to e36f5a6 Compare February 20, 2023 17:11
Option<StdState<_, _, _, _>>,
SimpleRestartingEventManager<_, StdState<_, _, _, _>, _>,
) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) {
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess if you launch the manager everytime, then the timestamp is not preserved across restarts

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe fix this after merging/open an issue?

@domenukk
Copy link
Member

error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
   --> libafl/src/schedulers/probabilistic_sampling.rs:192:13
    |
192 |             libafl_bolts::serdeany::RegistryBuilder::register::<super::ProbabilityMetadata>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
    |
    = note: consult the function's documentation for information on how to avoid undefined behavior

error: aborting due to previous error

Seems like we're close, though

@jasikpark
Copy link

🥳

@ndrewh
Copy link

ndrewh commented Feb 21, 2024

      Finished release [optimized + debuginfo] target(s) in 4m 21s
  LLVM ERROR: Invalid encoding
  thread 'main' panicked at 'Couldn't create runtime archive!', libafl_libfuzzer/build.rs:86:9
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `/home/runner/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/cargo check --manifest-path libafl_libfuzzer/Cargo.toml --no-default-features --features whole-archive` (exit status: 101)
Error: Process completed with exit code 1.

That's a new one..

@addisoncrump any ideas how to fix this?

I ran into this. It turns out that nm behavior differs depending on your system LLVM, and that uninstalling your system LLVM fixes the issue.

backtrace to the error (TIL that nm just invokes your system LLVM)

#5  0x00007fffe898d723 in llvm::report_fatal_error(llvm::Twine const&, bool) () from /lib/x86_64-linux-gnu/libLLVM-14.so.1
#6  0x00007fffe898d556 in llvm::report_fatal_error(char const*, bool) () from /lib/x86_64-linux-gnu/libLLVM-14.so.1
#7  0x00007fffe94a79ca in llvm::BitstreamCursor::ReadAbbrevRecord() () from /lib/x86_64-linux-gnu/libLLVM-14.so.1
#8  0x00007fffe942dfce in ?? () from /lib/x86_64-linux-gnu/libLLVM-14.so.1
#9  0x00007fffe94341bd in llvm::getBitcodeFileContents(llvm::MemoryBufferRef) () from /lib/x86_64-linux-gnu/libLLVM-14.so.1
#10 0x00007fffea0efc75 in llvm::object::readIRSymtab(llvm::MemoryBufferRef) () from /lib/x86_64-linux-gnu/libLLVM-14.so.1
#11 0x00007fffe9f663d2 in llvm::lto::InputFile::create(llvm::MemoryBufferRef) () from /lib/x86_64-linux-gnu/libLLVM-14.so.1
#12 0x00007fffee4ed22a in ?? () from /usr/bin/../bin/../lib/bfd-plugins/LLVMgold-14.so
#13 0x00007ffff7f497c2 in ?? () from /lib/x86_64-linux-gnu/libbfd-2.38-system.so
#14 0x00007ffff7f49a1c in ?? () from /lib/x86_64-linux-gnu/libbfd-2.38-system.so
#15 0x00007ffff7e8790c in bfd_check_format_matches () from /lib/x86_64-linux-gnu/libbfd-2.38-system.so

I made the grave error of having LLVM 14 installed on my machine. Uninstalling LLVM 14 fixed the issue -- libbfd instead chose the LLVMgold-15.so plugin (nope -- i have no idea where it makes this decision) which... uhhh... doesn't seem to invoke my system LLVM at all and prints the symbols successfully.

@tokatoka
Copy link
Member

Some symbols we are using in libafl_libfuzzer are missing in LLVM 14, so it doesn't work on llvm <= 14

@ndrewh
Copy link

ndrewh commented Feb 22, 2024

@tokatoka I think you misunderstand. build.rs invokes nm, which was hitting an error depending on the system LLVM:

  LLVM ERROR: Invalid encoding

Ideally, the behavior of this crate would not depend on whether or not /lib/x86_64-linux-gnu/libLLVM-14.so.1 exists on the system. (note: /lib/x86_64-linux-gnu/libLLVM-15.so.1 also exists on the system). IMO this is probably an upstream issue, but we probably shouldn't be using the system nm here to begin with.

@ndrewh
Copy link

ndrewh commented Feb 22, 2024

In theory i think the llvm-nm that ships with cargo's llvm-tools component should work, but it hits a different encoding error regardless of system LLVM.

@tokatoka
Copy link
Member

tokatoka commented Feb 22, 2024

my point is even if you avoid that issue, the fuzzer wouldn't have linked using llvm 14.

@AaronFriel
Copy link

@addisoncrump how could I improve the documentation with how to fuzz?

I ask because the pointers here are a little convoluted on how to answer the question "how do I fuzz Rust in 2025?"

One might find "cargo fuzz", but evidently it depends on libfuzzer and is no longer supported. Bummer.

If one finds https://github.com/AFLplusplus/cargo-libafl, it's archived and no longer supported. It links to this pull request instead of... a CLI.

The PR body doesn't describe how to use this, but more digging finds that the current wisdom is use cargo fuzz with this dependency entry:

libfuzzer-sys = { version = "0.15.3", package = "libafl_libfuzzer" }

But I can't actually find that anywhere except in GitHub code search.

Could you add that to your PR body (because it ends up being one of the strange attractors in this. It's a bit unfortunate but there's basically no documentation online that just explicitly says what to do.

This PR though ends up being linked to via cargo-libafl and some blog posts, link aggregators, etc.

@addisoncrump
Copy link
Collaborator Author

But I can't actually find that anywhere except in GitHub code search.

Oh, that's annoying. It's actually in the libafl_libfuzzer documentation, but docs.rs builds haven't been working for a while...

I think part of the solution might be to ask the cargo-fuzz folks to also add us to their README. But you are correct: we should document this better. I've added a short TL;DR to this PR.

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

Successfully merging this pull request may close these issues.

Corpora which store on-disk: permit skipping the generation of .lafl_lock files + duplicates with postfix
9 participants