rust-lang / miri

An interpreter for Rust's mid-level intermediate representation
Apache License 2.0
4.13k stars 318 forks source link

Trying to SeekFrom::Start past i64::MAX ICEs Miri #3680

Closed saethlin closed 1 week ago

saethlin commented 1 week ago

This program (reduced from an ICE encountered when running the tests for https://crates.io/crates/tempfile):

use std::io::Seek;

fn main() {
    let mut f = std::fs::File::create("test").unwrap();
    f.seek(std::io::SeekFrom::Start(i64::MAX as u64 + 1)).unwrap();
}

will ICE with:

thread 'rustc' panicked at src/tools/miri/src/shims/unix/fs.rs:398:51:
called `Result::unwrap()` on an `Err` value: TryFromIntError(())

The problem is the standard library casts our u64 SeekFrom to an i64 (off64_t is i64): https://github.com/rust-lang/rust/blob/55cac26a9ef17da1c9c77c0816e88e178b7cc5dd/library/std/src/sys/pal/unix/fs.rs#L1296-L1302

            // Casting to `i64` is fine, too large values will end up as
            // negative which will cause an error in `lseek64`.
            SeekFrom::Start(off) => (libc::SEEK_SET, off as i64),
            SeekFrom::End(off) => (libc::SEEK_END, off),
            SeekFrom::Current(off) => (libc::SEEK_CUR, off),
        };
        let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos as off64_t, whence) })?;

But we just convert the i64 to an i128 (with into): https://github.com/rust-lang/miri/blob/60a720040d6c60656ab9cac0980e587d19d9c07a/src/shims/unix/foreign_items.rs#L159-L161

Then we try to convert the i128 numerically into a u64: https://github.com/rust-lang/miri/blob/60a720040d6c60656ab9cac0980e587d19d9c07a/src/shims/unix/fs.rs#L392-L398 ... even though the standard library expects us to just return an error for a negative seek offset. We should do that.