Description
Miri reported a pointer being dereferenced after its allocation was freed in a function using String::replace_range
in a test (ip::sanitize_ip_recognizes_subpages_of_ipv6_address
) in the mwtitle library, and this was reported in Wikimedia Phabricator by @legoktm.
I couldn't detect an error in the code, and the test runs fine in stable and nightly, but I reduced the Miri-error-producing code to this minimal example:
fn main() {
let mut input = "1".to_string();
input.replace_range(0..0, "0");
}
If I reduce the input "1"
or the replacement "0"
to a zero-length string, or if I change the range to 0..
or ..
, there is no error.
The code looks correct and it compiles and runs fine in nightly. I thought this might be a Rustc problem generating code that's incorrect (freeing an allocation early) but happens not to crash so is only caught by Miri, but I'm not very familiar with how Miri works, so I thought I'd post here first.
The full error from `cargo +nightly miri run`
Preparing a sysroot for Miri (target: x86_64-unknown-linux-gnu)... done Finished dev [unoptimized + debuginfo] target(s) in 0.01s Running `~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo-miri runner target/miri/x86_64-unknown-linux-gnu/debug/miri-mwtitle` error: Undefined Behavior: pointer to alloc1704 was dereferenced after this allocation got freed --> ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/iter.rs:127:1 | 127 | / iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, { 128 | | fn is_sorted_by(self, mut compare: F) -> bool 129 | | where 130 | | Self: Sized, ... | 136 | | } 137 | | }} | |__^ pointer to alloc1704 was dereferenced after this allocation got freed | = 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 ` as std::iter::ExactSizeIterator>::len` at ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/iter/macros.rs:25:22: 25:55 = note: inside ` as std::ops::Drop>::drop` at ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/vec/drain.rs:201:24: 201:34 = note: inside `std::ptr::drop_in_place::> - shim(Some(std::vec::Drain<'_, u8>))` at ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:490:1: 490:56 = note: inside `std::ptr::drop_in_place::>> - shim(Some(std::vec::Splice<'_, std::str::Bytes<'_>>))` at ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:490:1: 490:56 = note: inside `std::string::String::replace_range::>` at ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/string.rs:1826:80: 1826:81 note: inside `main` --> src/main.rs:3:5 | 3 | input.replace_range(0..0, "0"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `len` which comes from the expansion of the macro `iterator` (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 backtraceerror: aborting due to previous error
Versions
Probably doesn't matter, but I ran this in Ubuntu in WSL.
rustc +nightly --version --verbose && echo && cargo +nightly miri --version
:
rustc 1.68.0-nightly (9e75dddf6 2023-01-15)
binary: rustc
commit-hash: 9e75dddf609c0201d03f9792e850f95d6a283d11
commit-date: 2023-01-15
host: x86_64-unknown-linux-gnu
release: 1.68.0-nightly
LLVM version: 15.0.6
miri 0.1.0 (9e75ddd 2023-01-15)