a1ien / rusb

A safe Rust wrapper for libusb.
Other
401 stars 83 forks source link

libusb_set_iso_packet_lengths panics on debug builds in newest nightly (2024-03-27) #199

Closed alufers closed 5 months ago

alufers commented 6 months ago

Hi!

I have created this simple reproducer program, which allocates a libusb transfer and then calls libusb_set_iso_packet_lengths on it:

use std::ptr::null_mut;

use rusb::ffi::{
    libusb_alloc_transfer, libusb_fill_iso_transfer, libusb_set_iso_packet_lengths, libusb_transfer,
};

fn main() {
    let transfer = unsafe { libusb_alloc_transfer(32) };
    if transfer.is_null() {
        panic!("Failed to allocate transfer");
    }

    extern "system" fn callback(transfer: *mut libusb_transfer) {}
    unsafe {
        libusb_fill_iso_transfer(
            transfer,
            null_mut(),
            0,
            null_mut(),
            0,
            32, // num_iso_packets, set to the same value as allocated
            callback,
            std::ptr::null_mut(),
            5000,
        );
    }

    unsafe { libusb_set_iso_packet_lengths(transfer, 2137) };
}

To my surprise this code works without panicking on current stable rust (1.77.1), but panics inside of libusb_set_iso_packet_lengths on nightly (2024-03-27). These are the logs:

thread 'main' panicked at library/core/src/panicking.rs:152:5:
unsafe precondition(s) violated: slice::get_unchecked_mut requires that the index is within the slice
stack backtrace:
   0: rust_begin_unwind
             at /rustc/c9f8f3438a8134a413aa5d4903e0196e44e37bbc/library/std/src/panicking.rs:646:5
   1: core::panicking::panic_nounwind_fmt::runtime
             at /rustc/c9f8f3438a8134a413aa5d4903e0196e44e37bbc/library/core/src/panicking.rs:110:18
   2: core::panicking::panic_nounwind_fmt
             at /rustc/c9f8f3438a8134a413aa5d4903e0196e44e37bbc/library/core/src/panicking.rs:120:5
   3: core::panicking::panic_nounwind
             at /rustc/c9f8f3438a8134a413aa5d4903e0196e44e37bbc/library/core/src/panicking.rs:152:5
   4: <usize as core::slice::index::SliceIndex<[T]>>::get_unchecked_mut::precondition_check
             at /rustc/c9f8f3438a8134a413aa5d4903e0196e44e37bbc/library/core/src/ub_checks.rs:66:21
   5: <usize as core::slice::index::SliceIndex<[T]>>::get_unchecked_mut
             at /rustc/c9f8f3438a8134a413aa5d4903e0196e44e37bbc/library/core/src/slice/index.rs:236:9
   6: core::slice::<impl [T]>::get_unchecked_mut
             at /rustc/c9f8f3438a8134a413aa5d4903e0196e44e37bbc/library/core/src/slice/mod.rs:728:24
   7: libusb1_sys::libusb_set_iso_packet_lengths
             at /home/alufers/.cargo/registry/src/index.crates.io-6f17d22bba15001f/libusb1-sys-0.6.4/src/lib.rs:661:9
   8: repro_libusb_panic::main
             at ./examples/repro_libusb_panic.rs:28:14
   9: core::ops::function::FnOnce::call_once
             at /rustc/c9f8f3438a8134a413aa5d4903e0196e44e37bbc/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.
[1]    137247 IOT instruction (core dumped)  RUST_BACKTRACE=1 cargo run --example repro_libusb_panic

I have bisected it with cargo-bisect-rustc, and this is the rust commit that breaks this function https://github.com/rust-lang/rust/commit/2b43e75c98cc5ae32328c8b49657bcd882eb5e75

It appears that rust started adding runtime checks to unchecked unsafe functions (https://github.com/rust-lang/rust/issues/120848), and it breaks libusb_set_iso_packet_lengths which calls get_unchecked_mut on a zero length slice.