Skip to content

Panic occurs when publish sequence types for Rust 1.78 #406

@Oscarchoi

Description

@Oscarchoi

I found that, in Rust 1.78, when trying to publish data of sequence types, such as std_msgs::msg::UInt8MultiArray or messages utilizing sensor_msgs::msg::PointCloud2, a panic occurs with the message:
unsafe precondition(s) violated: ptr::write_bytes requires that the destination pointer is aligned and non-null.

I tested ros2-rust 0.4.1 on ubuntu 22.04.1 + ros2 humble and checked on ubuntu 20.04.6 + ros2 foxy, too.
The source code I tested and the backtrace message are as follows:

source
use rclrs::{Context, Node};

fn main() {
    let context = Context::new([]).expect("failed to create context");

    let node = Node::new(&context, "rust_ros2_node").expect("failed to create node");

    let publisher = node
        .create_publisher::<std_msgs::msg::UInt8MultiArray>("u8_array", rclrs::QOS_PROFILE_DEFAULT)
        .expect("failed to create publisher");

    let layout = std_msgs::msg::MultiArrayLayout {
        dim: [
            std_msgs::msg::MultiArrayDimension {
                label: String::from("x"),
                size: 1,
                stride: 1,
            },
            std_msgs::msg::MultiArrayDimension {
                label: String::from("y"),
                size: 1,
                stride: 1,
            },
            std_msgs::msg::MultiArrayDimension {
                label: String::from("z"),
                size: 1,
                stride: 1,
            },
        ]
        .to_vec(),
        data_offset: 0,
    };

    let msg = std_msgs::msg::UInt8MultiArray {
        layout,
        data: vec![0u8, 1, 2],
    };
    publisher.publish(msg).expect("failed to publish message");

    println!("Published");
}
backtrace
$ ~/Workspace/rclrs-tutorial/sequence$ cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.06s
     Running `target/debug/sequence`
thread 'main' panicked at library/core/src/panicking.rs:156:5:
unsafe precondition(s) violated: ptr::write_bytes requires that the destination pointer is aligned and non-null
stack backtrace:
   0: rust_begin_unwind
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/panicking.rs:645:5
   1: core::panicking::panic_nounwind_fmt::runtime
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/panicking.rs:110:18
   2: core::panicking::panic_nounwind_fmt
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/panicking.rs:123:9
   3: core::panicking::panic_nounwind
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/panicking.rs:156:5
   4: core::intrinsics::write_bytes::precondition_check
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/intrinsics.rs:2799:21
   5: core::intrinsics::write_bytes
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/intrinsics.rs:3154:9
   6: rosidl_runtime_rs::sequence::<impl rosidl_runtime_rs::traits::SequenceAlloc for u8>::sequence_init
             at /home/oscarchoi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/rosidl_runtime_rs-0.4.1/src/sequence.rs:512:21
   7: rosidl_runtime_rs::sequence::Sequence<T>::new
             at /home/oscarchoi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/rosidl_runtime_rs-0.4.1/src/sequence.rs:252:13
   8: <rosidl_runtime_rs::sequence::Sequence<T> as core::iter::traits::collect::FromIterator<T>>::from_iter
             at /home/oscarchoi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/rosidl_runtime_rs-0.4.1/src/sequence.rs:202:23
   9: <rosidl_runtime_rs::sequence::Sequence<T> as core::convert::From<alloc::vec::Vec<T>>>::from
             at /home/oscarchoi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/rosidl_runtime_rs-0.4.1/src/sequence.rs:193:9
  10: <T as core::convert::Into<U>>::into
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/convert/mod.rs:759:9
  11: <std_msgs::msg::UInt8MultiArray as rosidl_runtime_rs::traits::Message>::into_rmw_message
             at /home/oscarchoi/ros2_ws/install/std_msgs/share/std_msgs/rust/src/msg.rs:3142:15
  12: rclrs::publisher::Publisher<T>::publish
             at /home/oscarchoi/.cargo/registry/src/index.crates.io-6f17d22bba15001f/rclrs-0.4.1/src/publisher.rs:148:27
  13: sequence::main
             at ./src/main.rs:38:5
  14: core::ops::function::FnOnce::call_once
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
thread caused non-unwinding panic. aborting.
Aborted (core dumped)

It seems that the panic arises at the following location:

unsafe {
// This allocates space and sets seq.size and seq.capacity to size
let ret = $init_func(seq as *mut _, size);
// Zero memory, since it will be uninitialized if there is no default value
std::ptr::write_bytes(seq.data, 0u8, size);
ret

In the current implementation, if the size is 0 (which happens when initialization), it's expected that seq.data will be null. However, according to the latest std documentation (https://doc.rust-lang.org/nightly/std/ptr/fn.write_bytes.html), even if the number of bytes being written is 0, the pointer should not be null. I believe this is causing the panic.

It seems that a null pointer is inevitable when the size is 0, due to the implementation of rosidl_runtime (https://github.com/ros2/rosidl/blob/4ba0effa201030ae8f45597b29d4ca685b2d50a1/rosidl_runtime_c/src/primitives_sequence_functions.c#L24-L43).

I'm quite new to rust and not sure what the correct solution is, I found that adding a null check before the write_bytes prevents the panic and makes the publish work. Oscarchoi@8b1e786

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions