Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check if function pointer passed from C is non-NULL

Tags:

c

rust

ffi

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?

like image 463
ldanko Avatar asked Jul 17 '15 17:07

ldanko


People also ask

How do you check if a pointer is not null?

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.

Should you always check if a pointer is null?

If using pointers, always verify if they are NULL before using them.

Can function pointers be null?

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.

Does pointer become null after free?

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.


1 Answers

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)
    }
}
like image 119
huon Avatar answered Sep 23 '22 02:09

huon