Example code below
The Rust part:
#[no_mangle]
pub extern fn call_c_function(value: i32, fun: fn(i32) -> i32) -> i32 {
fun(value)
}
And the C part:
int32_t call_c_function(int32_t value, int32_t (*fun)(int32_t));
int32_t triple(int32_t x)
{
return x*3;
}
int main(int argc, char *argv[])
{
int32_t value = 3;
int32_t result = call_c_function(value, triple);
printf("%d tripled is %d\n", value, result);
call_c_function(0, NULL); // Crash here
return EXIT_SUCCESS;
}
Of course second call of call_c_function
will crash.
Rust compiler will not complain about unsafe code inside call_c_function
, because from rust point of view this code is safe. Also it's not allowed to simply write:
if !fun.is_null() {
fun(value)
}
because fun
type is fn(i32) -> i32
(it's not a pointer).
So my question is, how I can protect call_c_function
against NULL pointer dereference? Is there any way to check if callback passed from C is not valid?
Maybe I have to change call_c_function
definition?
Use Pointer Value as Condition to Check if Pointer Is NULL in C++ Null pointers are evaluated as false when they are used in logical expressions. Thus, we can put a given pointer in the if statement condition to check if it's null.
If using pointers, always verify if they are NULL before using them.
Can I always reliably set a function pointer to NULL in C and C++? Yes, in any conforming C or C++ implementation this will work.
free() is a library function, which varies as one changes the platform, so you should not expect that after passing pointer to this function and after freeing memory, this pointer will be set to NULL.
You can use Option<...>
to represent nullable function pointers. It is incorrect to have a NULL value for a value of type fn(...)
so the Option
wrapper is required for cases like this.
For example,
#[no_mangle]
pub extern "C" fn call_c_function(value: i32, fun: Option<fn(i32) -> i32>) -> i32 {
if let Some(f) = fun {
f(value)
}
}
However, there's one extra point: fun
is a C function, but the type fn(...)
is a Rust function. They're not directly compatible (e.g. their calling conventions differ). One needs to use the extern "C" fn(...)
(a.k.a. extern fn(...)
) type when interacting with C function pointers:
#[no_mangle]
pub extern "C" fn call_c_function(value: i32, fun: Option<extern "C" fn(i32) -> i32>) -> i32 {
if let Some(f) = fun {
f(value)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With