I knew that
CString::new().as_ptr() is dangerous, so I consciously avoided that convenient construct, and still, I got a use-after-free.
I needed a nullable string for a C function, so I wrote:
let option = Some("foo"); // Explicitly bind CString to a variable to keep it alive let c_option = option.and_then(|s| CString::new(s).ok()); // And it's not alive! This causes use-after-free, and Rust is doesn't say a word about it let p = c_option.map(|c| c.as_ptr()).unwrap_or(ptr::null()); // And this is (I guess?) correct code: let p = c_option.as_ref().map(|c| c.as_ptr()).unwrap_or(ptr::null());
Option::map moves the
CString and frees it immediately after it gives a (dangling) pointer.
That’s really, really bad. Even though the pointer isn’t dereferenced on Rust’s side so Rust can claim it’s not unsafe, it is a real safety problem for Rust programs.
Can something be done about it?
- e.g. return
&c_char, so that the lifetime is tracked, and then rely on implicit conversion to
- return a newtype, like
- special magic in the compiler to give a warning or extend the lifetime