Segfault in `Drop` implementation on macOS 12
dstaley opened this issue · 7 comments
I'm working on rewriting a C program using libusb into Rust with rusb. I have a version of my program that executes correctly, but ends with a segfault when performing the Drop
on the libusb context when run on macOS.
My program roughly looks like the following:
fn main() {
let mut context = Context::new().expect("couldn't instantiate libusb");
context.set_log_level(rusb::LogLevel::Debug);
let mut handle = context
.open_device_with_vid_pid(0x05ac, 0x120a)
.expect("could not open device");
let endpoint_d2h_address: u8 = 0x82;
let endpoint_h2d_address: u8 = 0x01;
handle.set_auto_detach_kernel_driver(true);
handle
.claim_interface(0)
.expect("could not claim interface");
// perform a series of handle.write_bulk() and handle.read_bulk() calls
handle
.release_interface(0)
.expect("could not release interface");
drop(handle);
println!("dropped handle");
drop(context);
println!("dropped context");
println!("done");
println!("reached end of main");
}
For debugging purposes, I've manually called drop
on handle
and context
, but the issue occurs when relying on the auto Drop
calls.
This issue doesn't occur on Linux, and doesn't occur (as far as I can tell) in my C program. Furthermore, it doesn't occur if I skip performing the write_bulk
and read_bulk
calls.
I've collected a few logs with the libusb log level set to debug:
Since this doesn't occur in my C program, I'm inclined to believe this is an issue with rusb not correctly disposing of things during Drop
. In particular, one difference I noticed between the segfaulting Rust log and the working C log is that the C program calls libusb_close
before releasing the interface, whereas rusb
will always release the interface before calling libusb_close
. Unfortunately my USB protocol knowledge isn't sufficient to say whether or not that's an issue.
System Info
OS: macos 12.3.1
libusb: v1.0.26 (Homebrew)
rusb: v0.9.1
rustc 1.60.0 (7737e0b5c 2022-04-04)
binary: rustc
commit-hash: 7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c
commit-date: 2022-04-04
host: x86_64-apple-darwin
release: 1.60.0
LLVM version: 14.0.0
I believe that is same bugs as found here libusb/libusb#1124
And close libusb before releasing the interface is incorrect
I still get the issue when linking against a version of libusb built from the current commit (libusb/libusb@ba69847).
$ otool -L target/debug/rs-ipod-sysinfo
target/debug/rs-ipod-sysinfo:
/usr/local/opt/libusb/lib/libusb-1.0.0.dylib (compatibility version 4.0.0, current version 4.0.0)
/usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1311.100.3)
/usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)
$ readlink -f /usr/local/opt/libusb/lib/libusb-1.0.dylib
/usr/local/Cellar/libusb/HEAD-ba69847/lib/libusb-1.0.0.dylib
Sorry for long waiting I in process of moving. Can you share example of C code and rust code?
I am still believe that's bug in libusb and not in rusb. If C code call libusb_release_interface
after libusb_close
that's bug in C code as describe in documentation to libusb_release_interface you should call it before libusb_close
.
No worries, nothing to apologize for :)
Here is the code for both the Rust and C program. The C program actually doesn't manually release the interface, so maybe the call to libusb_close
automatically does that?
Can you try remove call handle.release_interface(0)
in rust. When handle dropped they release interface. Also can you try add libusb_release_interface call to C code in usb_device_close function.
I haven't any macos device so it's hard make test on my side.
I can not reproduce the issue with the following code, using latest libusb release 1.0.26 version, or libusb git (libusb/libusb@c5aec6b), tested under macOS Ventura 13.3.1 (latest macOS version) and Mac Mini m1.
mcuee@mcuees-Mac-mini rusb % cat ./examples/issue134.rs
use rusb::{
Context, Device, UsbContext,
};
fn main() {
let mut context = Context::new().expect("couldn't instantiate libusb");
context.set_log_level(rusb::LogLevel::Debug);
let mut handle = context
.open_device_with_vid_pid(0x04d8, 0xfa2e)
.expect("could not open device");
handle.set_auto_detach_kernel_driver(true);
handle
.claim_interface(0)
.expect("could not claim interface");
// perform a series of handle.write_bulk() and handle.read_bulk() calls
handle
.release_interface(0)
.expect("could not release interface");
drop(handle);
println!("dropped handle");
drop(context);
println!("dropped context");
println!("done");
println!("reached end of main");
}
mcuee@mcuees-Mac-mini rusb % ./target/debug/examples/issue134
[timestamp] [threadID] facility level [function call] <message>
--------------------------------------------------------------------------------
[ 0.006918] [00010283] libusb: debug [libusb_get_device_list]
[ 0.006949] [00010283] libusb: debug [libusb_get_device_descriptor]
[ 0.006953] [00010283] libusb: debug [libusb_get_device_descriptor]
[ 0.006955] [00010283] libusb: debug [libusb_get_device_descriptor]
[ 0.006958] [00010283] libusb: debug [libusb_get_device_descriptor]
[ 0.006961] [00010283] libusb: debug [libusb_open] open 2.7
[ 0.007036] [00010283] libusb: debug [darwin_open] device open for access
[ 0.007044] [00010283] libusb: debug [libusb_claim_interface] interface 0
[ 0.007427] [00010283] libusb: debug [get_endpoints] building table of endpoints.
[ 0.007442] [00010283] libusb: debug [get_endpoints] interface: 0 pipe 1: dir: 0 number: 1
[ 0.007449] [00010283] libusb: debug [get_endpoints] interface: 0 pipe 2: dir: 1 number: 1
[ 0.007471] [00010283] libusb: debug [darwin_claim_interface] interface opened
[ 0.007475] [00010283] libusb: debug [libusb_release_interface] interface 0
[ 0.007769] [00010283] libusb: debug [libusb_close]
dropped handle
[ 0.007814] [00010283] libusb: debug [libusb_exit]
[ 0.007818] [00010283] libusb: debug [libusb_unref_device] destroy device 2.11
[ 0.007821] [00010283] libusb: debug [libusb_unref_device] destroy device 2.9
[ 0.007824] [00010283] libusb: debug [libusb_unref_device] destroy device 2.8
[ 0.007826] [00010283] libusb: debug [libusb_unref_device] destroy device 2.7
[ 0.007829] [00010283] libusb: debug [libusb_unref_device] destroy device 2.6
[ 0.007832] [00010283] libusb: debug [libusb_unref_device] destroy device 2.5
[ 0.007834] [00010283] libusb: debug [libusb_unref_device] destroy device 2.4
[ 0.007837] [00010283] libusb: debug [libusb_unref_device] destroy device 2.3
[ 0.007840] [00010283] libusb: debug [libusb_unref_device] destroy device 2.2
[ 0.007842] [00010283] libusb: debug [libusb_unref_device] destroy device 2.1
[ 0.007866] [00010284] libusb: debug [darwin_event_thread_main] darwin event thread exiting
[ 0.008872] [00010283] libusb: debug [usbi_remove_event_source] remove fd 3
dropped context
done
reached end of main
But my test is probably different as there is no transfer used.
If I look into the debug log, there is a complication here, libusb does not have exclusive access to the device. I am not so sure if that plays a part or not. The device is a USB mass storage device and by right libusb/rusb is not the right library to use for the device.
You may want to try what have been suggested by @a1ien to see if that helps. You can also try latest libusb git which have some fixes which may help.