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

ShMem should probably not give out references #1748

Closed
langston-barrett opened this issue Dec 20, 2023 · 8 comments
Closed

ShMem should probably not give out references #1748

langston-barrett opened this issue Dec 20, 2023 · 8 comments
Labels
bug Something isn't working

Comments

@langston-barrett
Copy link
Contributor

ShMem::as_object{,_mut} returns a (mutable) reference. This lets the compiler make strong assumptions about ownership that do not apply. For example, consider the following program:

use libafl_bolts::shmem::{ShMem as _, ShMemProvider as _};

#[no_mangle]
pub fn setup() {
    let mut prov = libafl_bolts::shmem::StdShMemProvider::default();
    let mut shmem1 = unsafe { prov.new_shmem_object::<u32>().unwrap_unchecked() };
    let mut shmem2 = unsafe { prov.clone_ref(&shmem1).unwrap_unchecked() };
    let r1 = unsafe { shmem1.as_object_mut::<u32>() };
    let r2 = unsafe { shmem2.as_object_mut::<u32>() };
    go(r1, r2);
}

#[no_mangle]
pub fn go(r1: &mut u32, r2: &mut u32) {
    mut_r1(r1);
    mut_r2(r2);
    if *r1 == 1 {
        println!("r1 = {r1}");
    }
}

#[no_mangle]
pub fn mut_r1(r1: &mut u32) {
    *r1 = 0;
    *r1 = 1;
}

#[no_mangle]
pub fn mut_r2(r2: &mut u32) {
    *r2 = 32;
}

First, let's look at mut_r1:

cargo asm --lib --release mut_r1

.section .text.mut_r1,"ax",@progbits
	.globl	mut_r1
	.p2align	4, 0x90
	.type	mut_r1,@function
mut_r1:
	.cfi_startproc
	mov dword ptr [rdi], 1
	ret

The write of 0 has disappeared, because Rust assumes that no other thread/process can read the reference &mut r1.

That example is a bit artificial, but the behavior of this program is:

cargo run -qr

r1 = 32

This is very counter-intuitive, and probably happens because Rust/LLVM decide to elide the load before the if and/or reorder it with respect to the mutation in mut_r2, because it can tell that the condition "is always true"..

Now, you might say "but you used unsafe, of course everything went wrong"! The problem is that I believe that this usage of the API corresponds with how it's intended to be used: the fuzzer reads from it, while the fuzzee writes to it. And the documentation for as_object{,_mut} don't mention anything about aliasing, it only discusses initialization.

The solution is probably to deprecate as_object{,_mut} in favor of an API that uses raw pointers, and encourages reading/writing them with ptr::{read,write}_volatile.

@langston-barrett langston-barrett added the bug Something isn't working label Dec 20, 2023
@domenukk
Copy link
Member

domenukk commented Dec 21, 2023

Yes, this should probably return pointers instead, and potentially be renamed to something like as_ptr_of.
In any case I don't even think this needs to be part of ShMem but it might be extra trait or some helper function...

@domenukk
Copy link
Member

Do you want to fix this? The Bt observer will have to keep a reference as well, and only create a borrow when it's used

@langston-barrett
Copy link
Contributor Author

Do you want to fix this?

I think there are a two "levels" at which this could be fixed:

  • Remove the methods from ShMem, but adapt all the callsites in the example fuzzers to cast the raw pointer to a mutable reference. Requires minimal code change, and fixes the problem for other library consumers, but still has UB in the example fuzzers.
  • Come up with a comprehensive fix to all consumers of these references, e.g., AFLppCmpLogObserver. This would be quite difficult, as a data-race-proof fix would require some kind of IPC locking. Even a racy optimization-proof fix would require dealing with raw pointers, probably through ptr::{write,read}_volatile. Using these APIs properly would be quite difficult (for example, read_volatile creates a bitwise copy even for non-Copy types, and AFLppCmpLogMap is indeed non-Copy).

I can definitely do the former, but don't feel equipped to do the latter.

@domenukk
Copy link
Member

We don't need data-race-proof (since we use it single threaded), we need rust UB proof. For this, keeping a reference to the raw pointer in all users is enough, and casting it to a reference wherever we use it (for a short time)

My understanding is: we need to return a pointer here, then fix the consumers to store pointers instead of refs.

@langston-barrett
Copy link
Contributor Author

We don't need data-race-proof (since we use it single threaded)

I thought the point of ShMem was to use it for IPC (i.e., forking fuzzers)? So the fuzzer could conceivably be reading while the fuzzee is writing, leading to a data-race?

For this, keeping a reference to the raw pointer in all users is enough, and casting it to a reference wherever we use it (for a short time)

Yeah, I think this would be fine so long as we know that all of the accesses happen after the fuzzee completely terminates, so that the memory is not getting mutated while it's being read (and we have to assume that it never corrupts the shared memory, but that seems relatively safe, and it would be infeasible to do better).

@domenukk
Copy link
Member

If it's used for IPC people have to build protocols on top, yes.
I'll throw a PR together give me sec

domenukk added a commit that referenced this issue Dec 21, 2023
@domenukk
Copy link
Member

@langston-barrett can you take a look at my solution in #1751 - does that look better?

domenukk added a commit that referenced this issue Dec 22, 2023
* Fix as_object UB discussed in #1748

* More cleanup, more less UB

* Fix fixes

* Added uninit_on_shmem api

* clippy

* fmt

* trying to fix fuzzers, libfuzzer wrapper

* Add OwnedRefMit::owned constructor, libfuzzer fix

* Some more fixes

* Add BacktaceObserver::owned fn

* fmt

* more fmt
@domenukk
Copy link
Member

Fixed in #1751

rmalmain pushed a commit to rmalmain/LibAFL that referenced this issue Jan 2, 2024
* Fix as_object UB discussed in AFLplusplus#1748

* More cleanup, more less UB

* Fix fixes

* Added uninit_on_shmem api

* clippy

* fmt

* trying to fix fuzzers, libfuzzer wrapper

* Add OwnedRefMit::owned constructor, libfuzzer fix

* Some more fixes

* Add BacktaceObserver::owned fn

* fmt

* more fmt
rmalmain added a commit that referenced this issue Mar 22, 2024
* Added paging filtering.
Reworked address range filtering to fit with new generic code.

* Fix: renamed remaining QemuInstrumentationFilter instances.

* Renamed sync breakpoint to sync exit.

* Split emu in systemmode.rs / usermode.rs for specific code.
EmuExitHandler implementation.

* sync_backdoor.rs removal.
Formatting.

* Updated `bindgen` and `which`.
Adapting code to work with update.

* fix: reconfigure cleanly if prior configure was interrupted abruptly.

* Enable sanitizers in QEMU during debug.

* Added target-usable files.

* Added breakpoint structure.

* Adapted other files to work with ExitHandler.

* Adapted existing fuzzer to work with new exit handler.

* fix: use get to avoid crashes.

* Updated README to indicate cargo-make should be installed.

* Added QEMU internal exit handler.

* Adapted qemu_systemmode example with new exit handler.

* Fixed fuzzers to work with new exit handler.

* Trying to fix CI (#1739)

* test

* dummy

* dummy

* Added new examples.

* Forgot to add build scripts.

* format

* format

* clang-format

* python emulator adaptation.

* fixed python bindings.

* clippy fixes.

* python bindings.

* fix qemu_sugar.

* fix fuzzbench.

* fixed import issues.

* misc fixes.

* renamed crate.

* Updated x86_64 stub bindings.

* Fixed static naming.

* binding fmt

* clippy

* clippy

* Removed useless return statement.

* removed advice to install cargo-make in individual repositories.

* symcc_update (#1749)

* Remove unused create_anymap_for_trait macro (fixes #1719) (#1752)

* Fix `as_object` UB discussed in #1748 (#1751)

* Fix as_object UB discussed in #1748

* More cleanup, more less UB

* Fix fixes

* Added uninit_on_shmem api

* clippy

* fmt

* trying to fix fuzzers, libfuzzer wrapper

* Add OwnedRefMit::owned constructor, libfuzzer fix

* Some more fixes

* Add BacktaceObserver::owned fn

* fmt

* more fmt

* Ignore SigPipe by default (#1741)

* Ignore SigPipe by default

* Fix no_std

* fmt

* Fix incorrect imports (#1758)

* Fix incorrect imports

https://doc.rust-lang.org/core/simd/trait.SimdOrd.html

* Fix

* Try fix ci

* Documentation fixes (#1761)

* Documentation fixes

* Fix InProcessExecutor url

* Update all urls to latest

* Miri ignores for M1 regex (#1762)

* Enabling DrCov on Windows (#1765)

* Enabling DrCov for Windows

* Dedup common code in scheduler (#1702)

* dedup common code in scheduler

* del eco

* fixing

* fix

* replace `Emulator::new_empty` by `Emulator::get` calls outside of `emu.rs` for safety. (#1763)

* Add mute_inprocess_target fn, SimpleFdLogger::set_logger, and more (#1754)

* Add mute_inprocess_target fn, SimpleFdLogger::set_logger, set_error_print_panic_hook

* Trying to fix #1753

* typo

* More fix

* Fix test?

* more testcase fixes

* Fix: renamed remaining QemuInstrumentationFilter instances.

* Split emu in systemmode.rs / usermode.rs for specific code.
EmuExitHandler implementation.

* format

* format

* format

* Replace sync_exit with sync_backdoor.

* Rework command system.

* fix bad import.

* format.

* cargo fmt

* disable af-xdp as well to avoid linking errors.

* End of merging.

* format.

* Adaptation for usermode.

* format.

* injection support.

* usermode fixes.
format.

* clippy

* clippy + format

* Do not unwrap emu + format.

* fix: entry_point breakpoint

* inital commit.

* clippy

* tests

* clippy

* adapt example

* systemmode.

* renaming

* fmt

* fix lints.

* more lint fix.

* even more lint fixes.

* always more lint fixes.

* lint fix.

* allow unused qualifications for crate when it could be confusing.

* Still lint fixes.

* Lint fixes on generated code.

* Some lint fixes.

* merge continue.

* renamed modules as well.

* fixing merge.

* systemmode compiling.

* fmt

* fix early emulator drop.

* fmt

* fix cast to c_void of the wrong object.

* Added global enum for snapshot managers.
Some renaming.

* move things around.

* WIP: generic inclusion of QEMU Executor in exit handler.

* * Moved extern calls to `libafl_qemu_sys`
* Replaced old `Emulator` by `Qemu` and only kept C functions wrappers
* Now `Emulator` is for higher-level interactions with QEMU. Kept old functions for compatibility calling to `Qemu` functions
* A direct side effect of this slit is the removal of the `IsEmuExitHandler` trait dependency added in many parts of the code.
* Removed old dirty casting for `QemuExecutor` helpers and used the brand-new access to `QemuExecutorState` instead.
* Minor changes to `Qemu` and `Emulator` `get` methods for cleaner getters.

* Add missing `Qemu` function.

* Updated `qemu_systemmode` example.

* Adapted QEMU fuzzers + renaming.

* Fixed python.

* fix libafl_sugar with new implementation.

* fix dangling RefCell.
adapt new examples.
TODO: merge `libafl_systemmode.*` examples.

* clippy.

* fix more fuzzers.

* clippy.

* Implement `HasInstrumentationFilter` generically.
Updated `StdInstrumentationFilter` accordingly.

* Renamed breakpoint functions for QEMU.
`qemu.run()` handling.

* Removed OnceCell / RefCell in signature.
more explicit `MmapPerms` method names.

* minor code refactoring

* Emulator::run_handle refactoring

* deprecated Emulator functions calling directly to QEMU functions.

* IsSnapshotManager -> SnapshotManager

* IsEmuExitHandler -> EmuExitHandler + fmt

* Generic register when it makes sense.

* reverted IsSnapshotManager -> SnapshotManager because of a collision.

* fix syntax + clippy

* fmt

---------

Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
Co-authored-by: Dominik Maier <domenukk@gmail.com>
Co-authored-by: lazymio <mio@lazym.io>
Co-authored-by: Bet4 <0xbet4@gmail.com>
Co-authored-by: mkravchik <mkravchik@hotmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants