Speykious / cve-rs

Blazingly 🔥 fast 🚀 memory vulnerabilities, written in 100% safe Rust. 🦀
Other
3.72k stars 94 forks source link

We've been defeated by Miri. #3

Open Speykious opened 4 months ago

Speykious commented 4 months ago

Miri is too strong. Even without any unsafe blocks, it detects problems.

Miri defeats our tests

cve on  main is 📦 v0.1.0 via 🦀 v1.76.0 
❯ cargo +nightly miri test  
Preparing a sysroot for Miri (target: x86_64-unknown-linux-gnu)... done
   Compiling cve-rs v0.1.0 (/home/speykious/Documents/programming/rust/cve)
    Finished test [unoptimized] target(s) in 0.07s
     Running unittests src/main.rs (target/miri/x86_64-unknown-linux-gnu/debug/deps/cve_rs-5d4da50aad7811b6)

running 1 test
test transmute::tests::test_transmute ... error: Undefined Behavior: constructing invalid value: encountered a dangling reference (use-after-free)
  --> src/transmute.rs:28:17
   |
28 |     let ref_to_b = get_dangling_ref::<B>();
   |                    ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (use-after-free)
   |
   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
   = note: BACKTRACE:
   = note: inside `transmute::transmute::<f32, i32>` at src/transmute.rs:28:17: 28:40
note: inside `transmute::tests::test_transmute`
  --> src/transmute.rs:51:5
   |
51 |                 transmute::transmute::<f32, i32>(420.69),
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: inside closure
  --> src/transmute.rs:45:21
   |
39 |     #[test]
   |     ------- in this procedural macro expansion
...
45 |     fn test_transmute() {
   |                        ^
   = note: this error originates in the attribute macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

Miri defeats our CLI

error: Undefined Behavior: constructing invalid value: encountered a dangling reference (use-after-free)
  --> src/segfault.rs:55:15
   |
55 |     let my_ref = get_dropped_box();
   |                  ^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (use-after-free)
   |
   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
   = note: BACKTRACE:
   = note: inside `segfault::segfault` at src/segfault.rs:55:15: 55:32
note: inside `main`
  --> src/main.rs:16:17
   |
16 |         "segfault" => segfault::segfault(),
   |                       ^^^^^^^^^^^^^^^^^^^^

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
vimpostor commented 4 months ago

Miri is too strong. Even without any unsafe blocks, it detects problems.

Well, at least we always have the alternative possibility to port this from using transmute shenanigans to using std::fs to write/read to /proc/self/mem: https://github.com/rust-lang/rust/issues/32670

AsafFisher commented 4 months ago

Miri is too strong. Even without any unsafe blocks, it detects problems.

Well, at least we always have the alternative possibility to port this from using transmute shenanigans to using std::fs to write/read to /proc/self/mem: https://github.com/rust-lang/rust/issues/32670

This is a bad approach as this will cause this project to be platform specific. The aim of the project is to give a platform agnostic/cross platform solution for safe vulnerabilities in your projects.

AmJayden commented 3 months ago

Miri is too strong. Even without any unsafe blocks, it detects problems.

Well, at least we always have the alternative possibility to port this from using transmute shenanigans to using std::fs to write/read to /proc/self/mem: rust-lang/rust#32670

Even if you were to do this, this is actually an unsafe operation at its core, there's no safety guarantees behind calling into operating system code and the underlying functionality for it is all implemented with unsafe, thus meaning that it's not 100% safe code either.

vimpostor commented 3 months ago

there's no safety guarantees behind calling into operating system code and the underlying functionality for it is all implemented with unsafe, thus meaning that it's not 100% safe code either.

Yes, it is safe, as it is using Rust's std::fsAPI without using the unsafe keyword once, meaning it enjoys:

AmJayden commented 3 months ago

there's no safety guarantees behind calling into operating system code and the underlying functionality for it is all implemented with unsafe, thus meaning that it's not 100% safe code either.

Yes, it is safe, as it is using Rust's std::fsAPI without using the unsafe keyword once, meaning it enjoys:

  • zero-cost abstractions
  • move semantics
  • guaranteed memory safety
  • threads without data races
  • trait-based generics
  • pattern matching
  • type inference
  • minimal runtime
  • efficient C bindings

No, this isn't the case. I advise you to look at your target's underlying filesystem implementation. The underlying standard library code cannot access your system's filesystem without making a syscall, which is unsafe. You aren't truly bypassing Rust's safety checks by just calling into code with underlying unsafety. Rust's safety model doesn't guarantee that your operating system will be free of bugs or ways to break memory safety, it guarantees safety within exclusively Rust code interacting with itself. You can check your target's filesystem implementation to see for yourself how they implement it, here's how it's done on Linux:

impl FileDesc {
    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
        let result = cvt(unsafe { abi::read(self.fd.as_raw_fd(), buf.as_mut_ptr(), buf.len()) })?;
        Ok(result as usize)
    }

    pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
        let mut me = self;
        (&mut me).read_to_end(buf)
    }

    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
        let result = cvt(unsafe { abi::write(self.fd.as_raw_fd(), buf.as_ptr(), buf.len()) })?;
        Ok(result as usize)
    }
    // ...
}

https://github.com/rust-lang/rust/blob/c5e7f45b6219a963e8e27ef6e15587f22e80a3f5/library/std/src/sys/pal/hermit/fd.rs#L29