Can `Box::leak` be complemented with `Box::from_raw`

I have a String that I want to convert to a char* in order to pass to C code. For me it is crucial, that the Rust code gives up ownership and in the destructor reclaims it to deallocate the memory properly.

My attempt to create the char* looks like this:

let entry = CString::new(config.entry)?;
let ptr = Box::leak(entry.into_boxed_c_str()).as_ptr();

In the destructor I use the following:

Box::from_raw(ptr as *mut c_char);

This works flawlessly and the ASAN does not complain about it. But there are two things that are bugging me:

  1. It seems to me that from_raw is the second part of into_raw. I did not quite understand the difference between leak and into_raw, but with leak it is easier to get a char* pointer.
  2. I want to have the pointers as *const c_char because they are in fact immutable. But from_raw requires me to input a *mut c_char. Is this casting the correct thing to do?

You shouldn't use Box::leak if you're planning to call from_raw on it. Semantically, it's wrong, because it's not a memory leak if you intend to reclaim the memory. And it's just bad from a safety standpoint because &'static references can infiltrate long-lived data structures that will be invalidated when you call from_raw again. There are other subtle issues with using leak if you (perhaps inadvertently) keep the reference around after getting a pointer from it. Normally you should use into_raw if it's paired with from_raw.

This will create a Box<c_char>, which when dropped, will drop a single c_char. This is wrong (unless the original CString was empty) because a Box must be dropped with the same layout as it was created. You need to drop the whole buffer: not just a single char.

This isn't surprising. Most allocators (for some definition of "most") don't care if you don't keep track of the size of an allocation. But if you use a different allocator or run it in Miri you will see this is UB.

leak is for safely leaking memory: it gives you a &'static mut _. into_raw is for converting to a raw pointer: it gives you a *mut _. into_raw will generally be paired with some unsafe code; leak is something you might use to avoid unsafe code.

Don't go through Box: just use CString::into_raw.

// creation:
let ptr = entry.into_raw();
// destructor:
CString::from_raw(ptr)
16 Likes

Casting of pointer mutability for from_raw is fine.

Rust cares about strict semantics of & and &mut, but *const and *mut are mostly interchangeable and meaningless, like in C.


Casting of pointer type for from_raw is a huge mistake. It can cause UB by creating/destroying a wrong type. It can crash allocators by giving them invalid size or layout and making their internal structurs inconsistent.

If you've leaked/into_raw'd a CString, then you must re-create a CString, and nothing else. This is not a c_char.

5 Likes

Thanks everyone! :heart:

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.