CVE-2020-36206
Description
The rusb crate before 0.7.0 lacks Send and Sync bounds on Device and DeviceHandle, enabling data races and memory corruption.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
The rusb crate before 0.7.0 lacks Send and Sync bounds on Device and DeviceHandle, enabling data races and memory corruption.
Vulnerability
The rusb crate (a safe Rust wrapper for libusb) before version 0.7.0 contains a soundness issue where Device and DeviceHandle implement Send and Sync for any T: UsbContext without requiring UsbContext itself to be Send or Sync [1][2]. This means a user can implement UsbContext in safe Rust code without thread-safety guarantees, yet the wrapper types can be sent across threads or shared, violating Rust's memory safety guarantees [4].
Exploitation
An attacker with local access and low privileges can exploit this by crafting a non-thread-safe UsbContext implementation and then using Device or DeviceHandle in a multi-threaded context. Because the Send/Sync implementations are unsound, this can lead to a data race when the same context is accessed concurrently from multiple threads [2]. The attack complexity is high, as it requires the attacker to control the UsbContext implementation and trigger concurrent access [2].
Impact
Successful exploitation can result in memory corruption, potentially leading to confidentiality, integrity, and availability impacts all rated high [2]. The CVSS v3.1 score is 7.0 (HIGH) with vector AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H [2].
Mitigation
The issue is fixed in rusb version 0.7.0 and later [2]. Users should update to the patched version. There is no known workaround; the crate's maintainers have addressed the problem by adding proper bounds to the Send/Sync implementations or sealing the UsbContext trait [4].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
rusbcrates.io | < 0.7.0 | 0.7.0 |
Affected products
2- Rust/rusb cratedescription
Patches
10614e2e17cb7Merge pull request #46 from a1ien/hotplug_fix_leak
4 files changed · +75 −13
build.rs+35 −0 added@@ -0,0 +1,35 @@ +use std::fs; +use std::path::{Path, PathBuf}; + +fn get_api_version(libusb_source: &Path) { + use std::io::BufRead; + if let Ok(f) = fs::File::open(libusb_source) { + let f = std::io::BufReader::new(f); + for line in f.lines() { + if let Ok(line) = line { + if line.starts_with("#define LIBUSB_API_VERSION") { + if let Some(api_version) = line.rsplit(' ').next().and_then(|s| { + if s.starts_with("0x") { + let s = &s[2..]; + u32::from_str_radix(s, 16).ok() + } else { + None + } + }) { + if api_version >= 0x01000108 { + println!("cargo:rustc-cfg=libusb_hotplug_get_user_data"); + } + } + break; + } + } + } + } +} + +fn main() { + if let Ok(include_path) = std::env::var("DEP_USB_1.0_INCLUDE") { + let path = PathBuf::from(include_path); + get_api_version(path.join("libusb.h").as_path()); + } +}
Cargo.toml+3 −2 modified@@ -1,6 +1,6 @@ [package] name = "rusb" -version = "0.6.5" +version = "0.7.0" authors = ["David Cuddeback <david.cuddeback@gmail.com>", "Ilya Averyanov <a1ien.n3t@gmail.com>"] description = "Rust library for accessing USB devices." license = "MIT" @@ -9,6 +9,7 @@ repository = "https://github.com/a1ien/rusb.git" readme = "README.md" keywords = ["usb", "libusb", "hardware", "bindings"] edition = "2018" +build = "build.rs" [badges] travis-ci = { repository = "a1ien/rusb" } @@ -17,7 +18,7 @@ travis-ci = { repository = "a1ien/rusb" } vendored = [ "libusb1-sys/vendored" ] [dependencies] -libusb1-sys = "0.4.1" +libusb1-sys = "0.5.0" libc = "0.2" [dev-dependencies]
examples/hotplug.rs+6 −2 modified@@ -1,4 +1,5 @@ use rusb::{Context, Device, UsbContext}; +use std::option::Option::Some; struct HotPlugHandler; @@ -15,10 +16,13 @@ impl<T: UsbContext> rusb::Hotplug<T> for HotPlugHandler { fn main() -> rusb::Result<()> { if rusb::has_hotplug() { let context = Context::new()?; - context.register_callback(None, None, None, Box::new(HotPlugHandler {}))?; - + let mut reg = + Some(context.register_callback(None, None, None, Box::new(HotPlugHandler {}))?); loop { context.handle_events(None).unwrap(); + if let Some(reg) = reg.take() { + context.unregister_callback(reg); + } } } else { eprint!("libusb hotplug api unsupported");
src/context.rs+31 −9 modified@@ -46,7 +46,29 @@ pub trait Hotplug<T: UsbContext> { fn device_left(&mut self, device: Device<T>); } -pub type Registration = c_int; +#[derive(Debug)] +pub struct Registration<T: UsbContext> { + context: T, + handle: libusb_hotplug_callback_handle, +} + +impl<T: UsbContext> Registration<T> { + fn get_handle(&self) -> libusb_hotplug_callback_handle { + self.handle + } +} + +impl<T: UsbContext> Drop for Registration<T> { + fn drop(&mut self) { + let _call_back: Box<CallbackData<T>>; + #[cfg(libusb_hotplug_get_user_data)] + unsafe { + let user_data = libusb_hotplug_get_user_data(self.context.as_raw(), self.get_handle()); + _call_back = Box::<CallbackData<T>>::from_raw(user_data as _); + } + unsafe { libusb_hotplug_deregister_callback(self.context.as_raw(), self.get_handle()) } + } +} pub trait UsbContext: Clone + Sized + Send + Sync { /// Get the raw libusb_context pointer, for advanced use in unsafe code. @@ -89,7 +111,7 @@ pub trait UsbContext: Clone + Sized + Send + Sync { product_id: Option<u16>, class: Option<u8>, callback: Box<dyn Hotplug<Self>>, - ) -> crate::Result<Registration> { + ) -> crate::Result<Registration<Self>> { let mut handle: libusb_hotplug_callback_handle = 0; let callback = CallbackData { context: self.clone(), @@ -116,14 +138,14 @@ pub trait UsbContext: Clone + Sized + Send + Sync { if n < 0 { Err(error::from_libusb(n)) } else { - Ok(handle) + Ok(Registration { + context: self.clone(), + handle, + }) } } - fn unregister_callback(&self, reg: Registration) { - // TODO: fix handler leak - unsafe { libusb_hotplug_deregister_callback(self.as_raw(), reg) } - } + fn unregister_callback(&self, _reg: Registration<Self>) {} fn handle_events(&self, timeout: Option<Duration>) -> crate::Result<()> { let n = unsafe { @@ -211,10 +233,10 @@ extern "system" fn hotplug_callback<T: UsbContext>( _ctx: *mut libusb_context, device: *mut libusb_device, event: libusb_hotplug_event, - reg: *mut c_void, + user_data: *mut c_void, ) -> c_int { unsafe { - let mut reg = Box::<CallbackData<T>>::from_raw(reg as _); + let mut reg = Box::<CallbackData<T>>::from_raw(user_data as _); let device = Device::from_libusb( reg.context.clone(), std::ptr::NonNull::new_unchecked(device),
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-9mxw-4856-9cm5ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2020-36206ghsaADVISORY
- github.com/a1ien/rusb/issues/44ghsaWEB
- rustsec.org/advisories/RUSTSEC-2020-0098.htmlghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.