-
Notifications
You must be signed in to change notification settings - Fork 180
Description
I've been experimenting with a version of Miri that can execute foreign functions by interpreting the LLVM bytecode that is produced during a crate's build process.
Miri found an instance of undefined behavior in flate2-rs at version 1.0.28. There seems to be a tree borrows violation in the constructors for ZlibEncoder
and ZlibDecoder
. I encountered this error when running several test cases for another crate, twmap, which depends on flate2. Here’s the full error message that occurs when running the test case custom_test_teeworlds07
from that crate
---- Foreign Error Trace ----
@ %75 = load i32, ptr %74, align 8, !dbg !955
libz-sys-1.1.12/src/zlib/deflate.c:1519:22
libz-sys-1.1.12/src/zlib/deflate.c:1941:13
libz-sys-1.1.12/src/zlib/deflate.c:1003:18
flate2-1.0.28/src/ffi/c.rs:316:27: 316:58
-----------------------------
error: Undefined Behavior: read access through <133277> is forbidden
|
= note: read access through <133277> is forbidden
= help: the accessed tag <133277> is a child of the conflicting tag <133273>
= help: the conflicting tag <133273> has state Disabled which forbids this child read access
help: the accessed tag <133277> was created here
--> .../twmap/src/compression.rs:31:23
|
31 | let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: the conflicting tag <133273> was created here, in the initial state Reserved
--> .../twmap/src/compression.rs:31:23
|
31 | let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: the conflicting tag <133273> later transitioned to Disabled due to a foreign write access at offsets [0x8..0xc]
--> .../twmap/compression.rs:32:5
|
32 | encoder.write_all(data).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^
= help: this transition corresponds to a loss of read and write permissions
It seems like zlib creates a cyclic structure on lines 306-307 in deflate.c within the body of the function deflateInit2_
.
int ZEXPORT deflateInit2_(z_streamp strm, int level, int method,
int windowBits, int memLevel, int strategy,
const char *version, int stream_size) {
...
strm->state = (struct internal_state FAR *)s;
s->strm = strm;
...
}
The parameter strm
is a given as a mutable borrow from Rust, on line 277 of c.rs in flate2
fn make(level: Compression, zlib_header: bool, window_bits: u8) -> Self {
unsafe {
let mut state = StreamWrapper::default();
let ret = mz_deflateInit2(
&mut *state,
...
);
...
}
}
If state
is mutated through a Deflate
or Inflate
object before the pointer stored in strm->state
is used, then I think that this pointer would become "Disabled," leading to the invalid read.