Description
The issue reproduces only if FatPtr
code resides in a separate crate and only if compiled in release configuration.
I tried this code:
fat_ptr
's crate lib.rs
:
pub struct FatPtr {
ptr: *mut u8,
len: usize,
}
impl FatPtr {
pub fn new(len: usize) -> FatPtr {
let ptr = Box::into_raw(vec![42u8; len].into_boxed_slice()) as *mut u8;
FatPtr { ptr, len }
}
}
impl std::ops::Deref for FatPtr {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
}
}
impl std::ops::Drop for FatPtr {
fn drop(&mut self) {
println!("Drop");
}
}
test
's crate main.rs
:
use fat_ptr::FatPtr;
fn print(data: &[u8]) {
println!("{:#?}", data);
}
fn main() {
let ptr = FatPtr::new(20);
let data = unsafe { std::slice::from_raw_parts(ptr.as_ptr(), ptr.len()) };
print(data);
}
I expected to see this happen: 42
printed 20 times.
Instead, this happened: 42
printed 20 times in debug build, but not in release. Instead, slice length appears to be corrupted, so println
prints memory dump outside of allocated slice and eventually segfaults. However, if Drop
implementation for FatPtr
is removed, then slice is truncated to a single byte, which is still incorrect, but doesn't crash the process.
If print(data)
call is replaced with println!("{:#?}", data);
directly then everything works as intended even on release builds. If FatPtr
code placed in the same crate as main
then everything works as intended as well. Removing #[inline]
from deref
fixed the issue as well.
This reproduces on Linux and MacOS (haven't tried Windows).
Meta
rustc --version --verbose
:
rustc 1.46.0 (04488afe3 2020-08-24)
binary: rustc
commit-hash: 04488afe34512aa4c33566eb16d8c912a3ae04f9
commit-date: 2020-08-24
host: x86_64-apple-darwin
release: 1.46.0
LLVM version: 10.0
Backtrace
N/A