i meet a problem -> std::vec::Vec::from_raw_parts do return a Vec< u8 > and it points to the heap which is once owned by POINTER , then with the POINTER died ,the heap is freed . my vec's heap was free when the vec died ,vec free it again ,For this, i write a flag for POINTER so that it can know when to free its heap , i want to know if there is more elegant way ?
this is my code any help will be appreciated
use std::alloc::Layout;
use std::alloc::{alloc, dealloc};
#[derive(Debug)]
struct POINTER {
ptr: *mut u8,
layout: Layout,
flag: bool,
}
impl Drop for POINTER {
fn drop(&mut self) {
println!("I_AM_FREED :(");
if self.flag {
Self::heap_free(self);
return;
}
println!("I_AM_INTO :) Life change to another way");
}
}
impl POINTER {
pub fn as_ptr(&self) -> *mut u8 {
self.ptr
}
pub fn new(size: usize) -> Result<POINTER, ()> {
Self::heap_alloc(size)
}
pub fn len(&self) -> usize {
self.layout.size()
}
pub fn into_vec(mut self) -> Vec<u8> {
self.flag = false;
unsafe { return std::vec::Vec::from_raw_parts(self.ptr, self.len(), self.len()) }
}
fn heap_free(ptr: &mut POINTER) {
unsafe {
dealloc(ptr.ptr, ptr.layout);
}
}
fn heap_alloc(size: usize) -> Result<POINTER, ()> {
let layout = match Layout::array::<u8>(size) {
Ok(layout) => layout,
Err(_) => return Err(()),
};
let ptr;
unsafe {
ptr = alloc(layout);
}
if ptr.is_null() {
return Err(());
}
let ptr = POINTER {
ptr,
layout,
flag: true,
};
Ok(ptr)
}
}
fn main() {
let p = POINTER::new(1_000).unwrap();
let pw = p.as_ptr();
unsafe {
pw.write(12);
pw.add(1).write(13);
pw.add(2).write(14);
}
println!("{} {:?}", unsafe { *p.as_ptr() }, p.as_ptr());
let v = p.into_vec();
println!("{:?}", v);
}
Maybe std::mem::forget() to the rescue?
since you have transferred the ownership to the Vec
, you can use mem::forget()
to prohibit the drop of the orignal pointer.
You're using uninitialized memory. You also need to initialize your allocation, maybe with zeroes, before you read from it, write to it, or turn it into a vec.
Use Miri to detect stuff like this.
For this type of application, it’s better to put the value in ManuallyDrop
instead. The advantage is that you can read out the pointer (or whatever data is to be extracted) while it’s in the ManuallyDrop
, which means that if there is any bug or edge case that causes a panic within the function, you have a leak instead of a double-free, and thus maintain memory-safety.
For such a cool thing, I also suggest using cargo miri to find UB, that would be "fun".
And this may help learning unsafe Rust.
It took some time for this statement to sink in, but it absolutely makes sense:
The default behavior for ManualDrop
is to not drop, which is actually memory safe. In contrast, using mem::forget()
implicitly defaults to drop when it is not called, which can cause issues in edge cases.
In other words, it might be easier to prove that ManualDrop::drop()
is called at most once, whereas proving that mem::forget()
is called in all necessary circumstances could be more difficult.
Thanks a lot for this helpful insight!
i try to use std::mem::forget , and i use ghidra to decompile the code , i found that std::mem::forget seems just tell rustc do not call drop in this variant ),
it works , thanks !!!
se std::alloc::Layout;
use std::alloc::{alloc, dealloc};
#[derive(Debug)]
struct POINTER {
ptr: *mut u8,
layout: Layout,
flag: bool,
}
impl Drop for POINTER {
fn drop(&mut self) {
println!("I_AM_FREED :(");
if self.flag {
Self::heap_free(self);
return;
}
println!("I_AM_INTO :) Life change to another way");
}
}
impl POINTER {
pub fn as_ptr(&self) -> *mut u8 {
self.ptr
}
pub fn new(size: usize) -> Result<POINTER, ()> {
Self::heap_alloc(size)
}
pub fn len(&self) -> usize {
self.layout.size()
}
pub fn into_vec(mut self) -> Vec<u8> {
// self.flag = false;
unsafe {
let v= std::vec::Vec::from_raw_parts(self.ptr, self.len(), self.len());
std::mem::forget(self.ptr);
return v;
}
}
fn heap_free(ptr: &mut POINTER) {
unsafe {
dealloc(ptr.ptr, ptr.layout);
}
}
fn heap_alloc(size: usize) -> Result<POINTER, ()> {
let layout = match Layout::array::<u8>(size) {
Ok(layout) => layout,
Err(_) => return Err(()),
};
let ptr;
unsafe {
ptr = alloc(layout);
}
if ptr.is_null() {
return Err(());
}
let ptr = POINTER {
ptr,
layout,
flag: true,
};
Ok(ptr)
}
}
fn main() {
let p = POINTER::new(1_000).unwrap();
let pw = p.as_ptr();
unsafe {
pw.write(12);
pw.add(1).write(13);
pw.add(2).write(14);
}
println!("{} {:?}", unsafe { *p.as_ptr() }, p.as_ptr());
let v = p.into_vec();
println!("{:?}", v);
}
I'm going to add some code to do this , thansk
i will try to use it thanks
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.