FFI:return *mut c_void to c++ project

rust code

#[no_mangle]
unsafe extern "C" fn Estore_Cancel(
    rust: *mut ::std::os::raw::c_void,
    data: sys::CancelData,
) -> *mut ::std::os::raw::c_void {
    let api = unwrap_quote_spi(rust);
    let mut result = RT.block_on(async { api.cancel(data).await });

    let rsp = &mut result as *mut _ as *mut c_void;

    std::mem::forget(result);

    // if add this debug line, c++ not panic 
    // delete this debug line, c++ panic "has triggered a breakpoint"
    // debug!("1");

    rsp // CancelResult
}

c++ code

CancelResult cancel(CancelData data)
    {
        return *(CancelResult *)Estore_Cancel(this, data);
    }
void test_CancelTask(const char* taskId) {
	CancelData cdata;
	cdata.taskId = new char[128];
	cdata.token = new char[1];
	strcpy_s(cdata.token, 1, "");
	memcpy(cdata.taskId, taskId, strlen(taskId) + 1);
	CancelResult result = my.e.cancel(cdata);

	printf("result:%p code:%d msg:%s\n", (void *)&result, result.result.code, result.result.msg);

	delete cdata.taskId;
	delete cdata.token;
}

question

when i add "debug" after mem::forget in rust, c++ result read will not be panic.

i guess mem::forget is not work immediately, right?

i can't understand this.

rsp is a pointer to the local variable. Dereferencing it after the function returns is UB, means literally anything can happens including behaviors that doesn't make any sense.

If you haven't heard about what the UB is, try read this Wikipedia article.

3 Likes

thanks bro!

in my opinion, after use std::mem::forget(result), result is not local variable any more, right?

another thing, i use debug mode is ok, release panic.

Yes, which make it worse, since it's moved into the forget function's local variable. The original variable becomes the moved-out state, which means accessing here is UB.

This is one of the common symptoms of the UB. UB doesn't means it will crash or something predictable. It literally means the behavior is undefined - it may crash, work as you've imagined, silently corrupt some totally unrelated portion of memories, or mine some Bitcoin. It's the hole the language explicitly mentioned nothing guaranteed here. The Wikipedia article I mentioned contains more information why such thing even exist.

3 Likes

IOW, just because it happens not to panic doesn't mean it's correct. Your code should definitely be rewritten. If you need to return something complex through FFI, accept a pointer as an out parameter in which you write the results, or return a pointer pointing to a heap-allocated object. Returning a pointer to a stack-allocated object is never correct.

2 Likes

thanks, if i return a box::into_raw(result), i can't dealloc it by use c++ destructor.

I want to allocate objects through rust, and then use the destructor of C++ to release the objects.

That's simply not possible. What you allocated in Rust must be destroyed by Rust. You'll need to write a destructor function that performs Box::from_raw and you must call it from C++.

2 Likes

thanks, i will try other way.

thanks for your advice.
i will try another method, now it seems that this does not work

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.