Panicked at misaligned pointer dereference

I started my first rust project in order to evaluate its suitability for another networking/backend project.
The rust project uses tmq/zmq and flatbuffers (had to pic an old version, otherwise it did not compile "0.7.0") + tokio. The sender (publish) side is written in C++ the receiver in rust (subscriber).

Receiving an empty message (I assume that's the cause, as messages with content are received successful and can be read), causes the following stacktrace:

thread 'tokio-runtime-worker' panicked at 'misaligned pointer dereference: address must be a multiple of 0x4 but is 0x7f8494003bf9', /home/chef/.cargo/registry/src/index.crates.io-6f17d22bba15001f/flatbuffers-0.7.0/src/endian_scalar.rs:176:22
stack backtrace:
   0: rust_begin_unwind
             at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library/std/src/panicking.rs:593:5
   1: core::panicking::panic_nounwind_fmt
             at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library/core/src/panicking.rs:96:14
   2: core::panicking::panic_misaligned_pointer_dereference
             at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library/core/src/panicking.rs:175:5
   3: flatbuffers::endian_scalar::read_scalar
             at .cargo/registry/src/index.crates.io-6f17d22bba15001f/flatbuffers-0.7.0/src/endian_scalar.rs:176:22
   4: <flatbuffers::primitives::ForwardsUOffset<T> as flatbuffers::follow::Follow>::follow
             at .cargo/registry/src/index.crates.io-6f17d22bba15001f/flatbuffers-0.7.0/src/primitives.rs:180:19
   5: <flatbuffers::primitives::SkipSizePrefix<T> as flatbuffers::follow::Follow>::follow
             at .cargo/registry/src/index.crates.io-6f17d22bba15001f/flatbuffers-0.7.0/src/primitives.rs:252:9
   6: flatbuffers::table::get_size_prefixed_root
             at .cargo/registry/src/index.crates.io-6f17d22bba15001f/flatbuffers-0.7.0/src/table.rs:64:5
   7: news::discovery::proto::main::com::get_root_as_message
             at ./news/src/discovery/proto/./main_generated.rs:1282:3

This is basically the location of the crash:

#[inline]
pub fn read_scalar<T: EndianScalar>(s: &[u8]) -> T {
    let sz = size_of::<T>();

    let p = (&s[..sz]).as_ptr() as *const T;
    let x = unsafe { *p }; // crashes here

    x.from_little_endian()
}

This is the user code, I wrote:

async fn run(&mut self) -> Result<()> {
        while let Some(msgs) = self.socket.next().await {
            if let Ok(msg_res) = msgs {
                for (i, m) in msg_res.into_iter().enumerate() {
                    if i == 0 {
                        tracing::info!("topic: {}", m.as_str().unwrap_or("not a topic"));
                    } else {
                        let de_msg =
                            get_root_as_message(m.get(0..).expect("could not get content"));

self.socket is a Subscribe from tmq.

Also strange, the buf looks the same to me (upper screen is form crashing, lower from not crashing):

What would be the process to tackle this issue down? The panic is caused in a function I can't change and I also tried this catch_unwind stuff, which still crashed. Is there anything else I can try?
Thanks.

1 Like

u8 has an alignment of 1, whereas your arbitrary T may (and apparently does) have a higher alignment. It's not allowed (UB) to dereference a pointer that's not sufficiently aligned.

Are you trying to read integers? Because you would be better off reading the whole thing byte-by-byte, in entirely safe code. Or use the byteorder crate. For more general byte-level transmutation, use bytemuck instead.

In general, if you don't even know how alignment works, you should stay away from unsafe for a long time.

catch_unwind() is for catching panics. You can't catch undefined behavior. Undefined behavior means that your program may do anything, including crashing, not crashing, panicking, not panicking, yielding a "correct"/expected result, silently yielding an incorrect result, or launching the nukes.


Edit: I now see that the crashing function is in actually one of your dependencies. That dependency is old. It's flatbuffers 0.7.0, whereas the current version of this library, according to docs.rs, is 23.5.26 (!!!), which apparently contains an improved implementation.

Can you upgrade your (transitive) dependencies and see whether that helps upgrading this ancient version of flatbuffers to something more recent?

8 Likes

I think it's better to put efforts to make the newer version compile, instead of trying to deal with old and buggy code.

2 Likes

Thanks for the answer. I actually understand alignment (I thought), but not why it is expecting 0x4...
You pointed out that T must be wrong, so get_root_as_message expects a [u8] as you can see, let's me think that I gave the wrong data to the function.

pub fn get_root_as_message<'a>(buf: &'a [u8]) -> Message<'a> {
  flatbuffers::get_root::<Message<'a>>(buf)
}

I changed the calling code now to:

                for (i, m) in msg_res.iter().enumerate() {
                    if i == 0 {
                        tracing::info!("topic: {}", m.as_str().unwrap_or("not a topic"));
                    } else {
                        let de_msg =
                            get_root_as_message(m.deref());

as I found:

impl Deref for Message {
    type Target = [u8];

    fn deref(&self) -> &[u8] {
        // This is safe because we're constraining the slice to the lifetime of
        // this message.
        unsafe {
            let ptr = &self.msg as *const _ as *mut _;
            let data = zmq_sys::zmq_msg_data(ptr);
            let len = zmq_sys::zmq_msg_size(ptr) as usize;
            slice::from_raw_parts(data as *mut u8, len)
        }
    }
}

Which looks like a match to me.
Does not help, getting the same panic... I don't get why the alignment is off.

@nerditation Changing the flatbuffers version is quite painful as it comes with it own code generator (which is not part if the crate, as far as I can tell, so finding matching versions is kind of hard).
But I will give it a try.

because T has a required alignment of 4. For example, a u32 has alignment 4. It's very conceivable that whoever is calling that code passes in u32 as the generic parameter T, because the returned value should contain the length of whatever data follows, given that the caller's name is get_size_prefixed_root.

It doesn't matter how you obtain the exact same [u8] slice. It will still have alignment 1. You can't fix this code by passing the slice differently.

it's the return type that requires alignement, not the input bytes.

Deref::deref() is normally unnecessary to call manually, unless you encounter some inference problem (e.g. in generic context).

1 Like

if the crash is from generated deserialization code, either the format definition doesn't match your actual data (e.g. protocol version mismatch), or the generator contains a bug. either way, you should try the up to date version first.

Thanks for the help, I somehow managed to get the latest flatbuffers version compiled, which crashes on a different location now, but the panic issue is gone. :slight_smile:

AFAICT, that's one of the nicer outcomes. Assuming you're in the center of the blast, you won't have to live out the rest of your life with Nasal Demons

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.