What changed, and why it matters
This commit adds a new Rust wrapper around an existing C ring buffer library used inside the BitBox02 hardware wallet firmware. It does not change any user-facing behavior, fix a bug, or alter security logic. It only exposes an internal data structure so it can be used in future code and tested today.
No security action required. Treat as routine infrastructure/refactoring. If this wrapper is later enabled for production use, review callers for buffer lifetime correctness and unsafe block soundness.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change introduces bitbox02::ringbuffer::RingBuffer, a thin safe-ish Rust wrapper over the ASF4 ringbuffer C type. It adds the C type and five C functions to the bindgen allowlists, includes the C header, registers the new module, and implements new() and len() for production plus put(), get(), and flush() gated under #[cfg(test)]. The wrapper carries a lifetime marker tying the Rust struct to the backing &mut [u8] buffer. Unit tests verify basic ring-buffer semantics.
Changed components
src/rust/bitbox02-sys/build.rssrc/rust/bitbox02-sys/wrapper.hsrc/rust/bitbox02/src/lib.rssrc/rust/bitbox02/src/ringbuffer.rsInspect captured patch +133 / −0
diff --git a/src/rust/bitbox02-sys/build.rs b/src/rust/bitbox02-sys/build.rs
index b7e7e80..015d984 100644
--- a/src/rust/bitbox02-sys/build.rs
+++ b/src/rust/bitbox02-sys/build.rs
@@ -42,6 +42,7 @@ const ALLOWLIST_TYPES: &[&str] = &[
"delay_t",
"event_slider_data_t",
"event_types",
+ "ringbuffer",
"securechip_error_t",
"trinary_input_string_params_t",
"UG_COLOR",
@@ -124,6 +125,11 @@ const ALLOWLIST_FNS: &[&str] = &[
"reboot_to_bootloader",
"reboot",
"reset_ble",
+ "ringbuffer_flush",
+ "ringbuffer_get",
+ "ringbuffer_init",
+ "ringbuffer_num",
+ "ringbuffer_put",
"screen_clear",
"screen_init",
"screen_print_debug",
diff --git a/src/rust/bitbox02-sys/wrapper.h b/src/rust/bitbox02-sys/wrapper.h
index f3415b9..730f2f4 100644
--- a/src/rust/bitbox02-sys/wrapper.h
+++ b/src/rust/bitbox02-sys/wrapper.h
@@ -38,6 +38,7 @@
#include <usb/usb.h>
#include <usb/usb_processing.h>
#include <util.h>
+#include <utils_ringbuffer.h>
#if defined(TESTING)
#include <fake_memory.h>
diff --git a/src/rust/bitbox02/src/lib.rs b/src/rust/bitbox02/src/lib.rs
index 9d9228f..0da3f85 100644
--- a/src/rust/bitbox02/src/lib.rs
+++ b/src/rust/bitbox02/src/lib.rs
@@ -31,6 +31,7 @@ pub mod memory;
#[cfg(feature = "simulator-graphical")]
pub mod queue;
pub mod random;
+pub mod ringbuffer;
#[cfg(feature = "simulator-graphical")]
pub mod screen;
pub mod screen_saver;
diff --git a/src/rust/bitbox02/src/ringbuffer.rs b/src/rust/bitbox02/src/ringbuffer.rs
new file mode 100644
index 0000000..fcee257
--- /dev/null
+++ b/src/rust/bitbox02/src/ringbuffer.rs
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use bitbox02_sys::{ringbuffer, ringbuffer_init};
+use core::marker::PhantomData;
+
+/// A wrapper around ASF4 `ringbuffer` type
+pub struct RingBuffer<'a> {
+ pub(crate) inner: ringbuffer,
+ _marker: PhantomData<&'a mut [u8]>,
+}
+
+impl<'a> RingBuffer<'a> {
+ /// `buf` length must be a power of 2
+ pub fn new(buf: &'a mut [u8]) -> Self {
+ debug_assert!(buf.len().is_power_of_two());
+ let mut inner = ringbuffer {
+ buf: core::ptr::null_mut(),
+ size: 0,
+ read_index: 0,
+ write_index: 0,
+ };
+ unsafe {
+ ringbuffer_init(
+ &mut inner as *mut _,
+ buf as *mut _ as *mut _,
+ buf.len() as u32,
+ );
+ };
+ RingBuffer {
+ inner,
+ _marker: PhantomData,
+ }
+ }
+
+ /// Bytes currently used
+ pub fn len(&self) -> u32 {
+ unsafe { bitbox02_sys::ringbuffer_num(&self.inner as *const _) }
+ }
+}
+
+// These are currently only used in unit tests.
+#[cfg(test)]
+impl RingBuffer<'_> {
+ pub fn put(&mut self, data: u8) -> Result<(), i32> {
+ let result = unsafe { bitbox02_sys::ringbuffer_put(&mut self.inner as *mut _, data) };
+ if result == 0 { Ok(()) } else { Err(result) }
+ }
+
+ pub fn get(&mut self) -> Result<u8, i32> {
+ let mut out = 0u8;
+ let result = unsafe { bitbox02_sys::ringbuffer_get(&mut self.inner as *mut _, &mut out) };
+ if result == 0 { Ok(out) } else { Err(result) }
+ }
+
+ pub fn flush(&mut self) -> Result<(), u32> {
+ let result = unsafe { bitbox02_sys::ringbuffer_flush(&mut self.inner as *mut _) };
+ if result == 0 { Ok(()) } else { Err(result) }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_new_len_is_zero() {
+ let mut buf = [0u8; 8];
+ let rb = RingBuffer::new(&mut buf);
+ assert_eq!(rb.len(), 0);
+ }
+
+ #[test]
+ fn test_put_get_len() {
+ let mut buf = [0u8; 8];
+ let mut rb = RingBuffer::new(&mut buf);
+
+ rb.put(1).unwrap();
+ assert_eq!(rb.len(), 1);
+
+ rb.put(2).unwrap();
+ assert_eq!(rb.len(), 2);
+
+ let out = rb.get().unwrap();
+ assert_eq!(out, 1);
+ assert_eq!(rb.len(), 1);
+ }
+
+ #[test]
+ fn test_overwrite_oldest() {
+ // Buf len must be a power of 2, and the ringbuffer capacity is `buf.len()`.
+ let mut buf = [0u8; 8];
+ let mut rb = RingBuffer::new(&mut buf);
+
+ for i in 0u8..9 {
+ rb.put(i).unwrap();
+ }
+ assert_eq!(rb.len(), 8);
+
+ let out = rb.get().unwrap();
+ assert_eq!(out, 1);
+ }
+
+ #[test]
+ fn test_get_empty_returns_error() {
+ let mut buf = [0u8; 8];
+ let mut rb = RingBuffer::new(&mut buf);
+
+ let result = rb.get();
+ assert!(result.is_err());
+ assert_eq!(rb.len(), 0);
+ }
+
+ #[test]
+ fn test_flush() {
+ let mut buf = [0u8; 8];
+ let mut rb = RingBuffer::new(&mut buf);
+
+ rb.put(1).unwrap();
+ rb.put(2).unwrap();
+ assert_eq!(rb.len(), 2);
+
+ rb.flush().unwrap();
+ assert_eq!(rb.len(), 0);
+ }
+}
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.