Scope of *const c_char - e.g. CString::as_ptr


Here’s some FFI code that gives me SQLITE_CANTOPEN (taken from rust-sqlite3). Worked fine before, and now suddenly fails with the exact same DLL - so I thought the issue must be on Rust side. Debugging into it, it seems the string buffer isn’t valid memory anymore during the C call.

pub fn str_charstar<'a>(s: &'a str) -> std_ffi::CString {

pub fn in_memory() -> SqliteResult<DatabaseConnection> {
    struct InMemory;
    impl Access for InMemory {
        fn open(self, db: *mut *mut ffi::sqlite3) -> c_int {
            let c_memory = str_charstar(":memory:").as_ptr();
            let res = unsafe { ffi::sqlite3_open(c_memory, db) };

If I change to this:

        fn open(self, db: *mut *mut ffi::sqlite3) -> c_int {
            // Keep CString instance in scope, call as_ptr only in unsafe block
            let c_memory = str_charstar(":memory:");
            let res = unsafe { ffi::sqlite3_open(c_memory.as_ptr(), db) };

it works fine…

Now my question is whether this is expected? CString::as_ptr says

fn as_ptr(&self) -> *const c_char
Returns the inner pointer to this C string.

The returned pointer will be valid for as long as self is and points
to a contiguous region of memory terminated with a 0 byte to represent
the end of the string.

so to me this reads like the borrowing mechanism doesn’t apply here and the original code has an out-of-scope problem. Am I right on this, i.e. found a bug in the crate?


Yes, you are correct. When the CString is dropped, its destructor runs and frees its memory. In the original sample code, since the CString doesn’t get bound to any variable, it’s dropped at the end of this statement. Only the pointer is stored, and it now points to freed memory:

let c_memory = str_charstar(":memory:").as_ptr();

This is a common bug; we should probably add big warnings about it to the CString::as_ptr docs.