Box from and to raw (SIGSEV - address not mapped to object)

Hello,

I'm using a function from C library in which I need to pass a callback function and some optional user data.

In my case I'm passing some user data. My rust user data is of type:

Arc<RwLock<SomeStruct>>

The code that sets the callback function looks like this:

let user_data = Box::new(self.data.clone());

unsafe { the_c_api_function(Self::my_callback_function, Box::into_raw(user_data));

In my callback I do:

let data = Box::from_raw(user_data)

let guard = data.read().unwrap() // <-- Here it seems to crash (that's what the debugger seems to be showing)

Here data is the data passed to the callback and it's of type:

Arc<RwLock<SomeStruct>>

Here is the stack:

  • core::sync::atomic::atomic_load atomic.rs:3950
  • [Inlined] core::sync::atomic::AtomicU32::load atomic.rs:2879
  • std::sys::sync::rwlock::futex::RwLock::read futex.rs:95
  • std::sync::poison::rwlock::RwLock::read rwlock.rs:394
  • my_module

Thank you very much in advance for any help and time

what does callbacks.read() do? what's the type signature of it?

What's the type of data here? Is it specified explicitly?

Thank you for your quick response.

Sorry for not being clear enough.

Like I said (I certainly did not indicated clearly enough) user_data is of type Arc<RwLock<SomeStruct>> so you can find the read method here

Sorry for the confusion and thank you for your time

s

so do you mean data.read() instead of callbacks.read() then? is this a typo?

Yes thank you for pointing that out.

I corrected my post from my silly mistake.

Thank you

How many times can this callback function be called from the C API?

1 Like

there's not much context to draw a decisive conclusion, so I will try to make some guesses.

first of all, are you sure it's a segfault instead of a panic? since you have an .unwrap() in the code, it might just be a logic error in your application.

other than that, the most likely cause I would guess is the callback is being called multiple times, but you converted the pointer back into a Box in the callback, which means the callback can only be called once. when it is first called, it will drop the Box at return, so subsequent calls would get a dangling pointer.

to verify this hypothesis, try something like this:

-let data = Box::from_raw(user_data);
+let data = unsafe { &*user_data };

BTW, you can convert Arc into a raw pointer directly, no need to box it again, see Arc::into_raw(). nevertheless, but the extra level of indirection should not cause segfault, it's just less efficient.

3 Likes

This function should be called multiple times (I don't have the control on when it should be called).

other than that, the most likely cause I would guess is the callback is being called multiple times, but you converted the pointer back into a Box in the callback, which means the callback can only be called once. when it is first called, it will drop the Box at return, so subsequent calls would get a dangling pointer.

Indeed the callback is called multiple times

which means the callback can only be called once

How can I correct the fact that the callback can only be called once ?

see the code snippet in my previous reply.

in summary, DON'T convert the raw pointer back into Box, borrow it instead.

3 Likes

Thank you

Box is exclusively owned and has only one instance. Box::from_raw undoes previous into_raw and makes the Box memory-managed by Rust again, and Rust destroys it before the function returns.

Box::from_raw can never be called more than once on the same pointer.

You usually would not use Box::into_raw with callbacks called more than once, because you won't have any way to properly free the memory after the last call of the callback.

If the data is stored/owned by something in Rust that outlives all the callbacks, then keep the data there and just pass a plain pointer/reference (no extra box).

When you already have Arc, you don't need Box either. You can take reference to the data inside the Arc if it outlives the callbacks, or use Arc::clone(&a).into_raw() like you'd use Box::into_raw (but the same applies about Arc::from_raw, it will decrement refcount after it's dropped)