Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Ayush Singh" <ayush@beagleboard.org>
Cc: devicetree@vger.kernel.org, robh@kernel.org, conor+dt@kernel.org,
	ojeda@kernel.org, gary@garyguo.net
Subject: Re: [PATCH v3 4/7] rust: kernel: Add greybus abstractions
Date: Tue, 13 Oct 2026 12:48:04 +0000	[thread overview]
Message-ID: <sashiko-outbox-169737@kernel.org> (raw)
In-Reply-To: <20261013-gb-uart-transport-v3-4-81d0a8a1acf7@beagleboard.org>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] greybus: safe Connection methods allow UAF of parent devices
- [High] greybus: rust: UAF of driver data due to eager drop in Registration::drop
- [Low] rust: missing #[inline] on trivial abstraction functions
- [Low] rust: missing // INVARIANT comment and size validation in GbOperationMsgHdr
--

commit 75320c1f601296bad85db7371e32bc7e97df53b7
Author: Ayush Singh <ayush@beagleboard.org>

rust: kernel: Add greybus abstractions

This commit adds Rust abstractions for the Greybus core to support implementing
Greybus host drivers in Rust. The abstractions cover the APIs used by gb-softsvc
and gb-uart-node drivers, including host device registration, connection management,
and SVC protocol structures. It also introduces lifetime and ownership rules for
Greybus messages and callbacks within the Rust bindings.

> diff --git a/rust/kernel/greybus/hd.rs b/rust/kernel/greybus/hd.rs
> --- /dev/null
> +++ b/rust/kernel/greybus/hd.rs

[ ... ]

> +    unsafe extern "C" fn message_send(
> +        hd: *mut bindings::gb_host_device,
> +        dest_cport_id: u16,
> +        msg: *mut bindings::gb_message,
> +        gfp_mask: bindings::gfp_t,
> +    ) -> c_int {
> +        // SAFETY: gb_host_device and HostDevice have the same layout.
> +        let hd = unsafe { Device::<device::CoreInternal<'_>>::from_raw(hd) };
> +        // SAFETY: message_send is only ever called after a successful call to
> +        // gb_hd_add, hence it's guaranteed that Device::set_drvdata() has been called
> +        // and stored a Pin<KBox<T>>.
> +        let data = unsafe { hd.as_ref().drvdata_borrow() };
> +        // SAFETY: The caller guarantees msg is valid for the duration of this call.
> +        let msg = unsafe { Message::from_raw(msg) };
> +
> +        match T::message_send(&data, dest_cport_id, msg, Flags::new(gfp_mask)) {

[Severity: High]
Does this code allow a use-after-free of the driver data?

Thread A could initiate a request through gb_operation_request_send(), which
calls this message_send wrapper without taking locks that serialize against
deletion. This wrapper borrows the driver data T via drvdata_borrow() and
invokes T::message_send().

If the hardware receives the request and replies immediately, the response can
be processed by gb_operation_work() in a workqueue, dropping the operation's
active count to 0. Meanwhile, if Thread B executes Registration::drop and
calls gb_hd_del(), it cancels operations. Since the active count is 0,
wait_event(!gb_operation_is_active(operation)) returns immediately without
waiting for message_send to return.

When gb_hd_del() finishes, Registration::drop eagerly extracts and drops
the driver data. If Thread A was preempted inside T::message_send or before
returning to C, it could resume execution and access the freed driver data.

[ ... ]

> +    /// Looks up the connection bound to cport on interface id.
> +    pub fn find_connection_by_intf(&self, id: u8, cport: u16) -> Option<ARef<Connection>> {

[Severity: Low]
This isn't a bug, but should this trivial abstraction function have an
#[inline] attribute? The Rust subsystem guidelines recommend #[inline] for
functions forwarding to a C binding call.

> +        // SAFETY: By the type invariant of Self, self.as_raw() is a pointer to a valid
> +        // struct gb_host_device.
> +        let ptr = NonNull::new(unsafe {
> +            bindings::gb_connection_hd_find_by_intf(self.as_raw(), id, cport)
> +        })?;
> +
> +        // SAFETY: ptr is a valid gb_connection
> +        Some(unsafe { ARef::from_raw(ptr.cast()) })
> +    }
> +}

[ ... ]

> +impl<T> Registration<T> {
> +    fn as_raw(&self) -> *mut bindings::gb_host_device {

[Severity: Low]
This isn't a bug, but this small wrapper function is missing the #[inline]
attribute, which the Rust subsystem guidelines recommend for trivial getters
in abstractions.

> +        self.ptr.as_ptr()
> +    }
> +}

[ ... ]

> +impl<T> Drop for Registration<T> {
> +    fn drop<'a>(&'a mut self) {
> +        // SAFETY: By the type invariant of Self, self.as_raw() is a pointer to a valid
> +        // struct gb_host_device.
> +        unsafe { bindings::gb_hd_del(self.as_raw()) }
> +
> +        {
> +            // SAFETY: hd is a valid pointer to gb_host_device.
> +            let hd_dev = unsafe { Device::<device::CoreInternal<'_>>::from_raw(self.as_raw()) };
> +            // SAFETY: The driver data was set to a T in Registration::new() and has not been
> +            // taken since, and this is the only place that takes it.
> +            drop(unsafe { hd_dev.as_ref().drvdata_obtain::<T>() });

[Severity: High]
Can the eager drop of driver data here lead to a use-after-free if
message_send is still executing concurrently?

Since gb_hd_del() may return without waiting for message_send if the
operation's active count drops to 0 (e.g., from an immediate hardware response),
we might drop the driver data while Thread A is still executing the wrapper
or driver code referencing it.

> +        }
> +
> +        // SAFETY: By the type invariant of Self, self.as_raw() is a pointer to a valid
> +        // struct gb_host_device.
> +        unsafe { bindings::gb_hd_put(self.as_raw()) };
> +    }
> +}

> diff --git a/rust/kernel/greybus/mod.rs b/rust/kernel/greybus/mod.rs
> --- /dev/null
> +++ b/rust/kernel/greybus/mod.rs

[ ... ]

> +    /// Returns the payload, i.e. the message without its operation header.
> +    pub const fn payload_bytes(&self) -> &[u8] {

[Severity: Low]
This isn't a bug, but should this small abstraction getter have an #[inline]
attribute? The Rust subsystem guidelines recommend it for trivial abstractions.

> +        // SAFETY: By the type invariant the message is valid.
> +        let payload = unsafe { (*self.0.get()).payload };

[ ... ]

> +    /// Returns the interface at the far end of the connection.  In cases like SVC connection,
> +    /// interface can be NULL.
> +    #[inline]
> +    pub const fn interface(&self) -> Option<&Interface> {
> +        // SAFETY: By the type invariant the connection is valid.
> +        let intf_ptr = unsafe { (*self.0.get()).intf };
> +
> +        if intf_ptr.is_null() {
> +            None
> +        } else {
> +            // SAFETY: By the previous check,  intf_ptr is valid.
> +            Some(unsafe { Interface::from_raw(intf_ptr) })

[Severity: High]
Does this code allow for a use-after-free of the parent interface?

Device::find_connection_by_intf returns an ARef<Connection>, which safely
increments the gb_connection kref. This allows the Rust driver to hold the
connection alive indefinitely.

In C, gb_connection_create() assigns raw pointers to its parent gb_interface
and gb_host_device but does not take a reference count on them.

During hardware removal, the Greybus core can destroy the interface and host
device when their own refcounts drop to zero. The gb_connection remains alive
due to the outstanding Rust ARef, but its intf and hd pointers become
dangling.

Calling interface() on the retained Connection unsafely derives a safe Rust
reference &Interface from a dangling pointer.

> +        }
> +    }
> +
> +    /// Returns the CPort id this connection uses on the interface.
> +    #[inline]
> +    pub const fn intf_cport_id(&self) -> u16 {
> +        // SAFETY: By the type invariant the connection is valid.
> +        unsafe { (*self.0.get()).intf_cport_id }
> +    }
> +
> +    /// Returns the CPort id this connection uses on the host device.
> +    #[inline]
> +    pub const fn hd_cport_id(&self) -> u16 {
> +        // SAFETY: By the type invariant the connection is valid.
> +        unsafe { (*self.0.get()).hd_cport_id }
> +    }
> +
> +    /// Returns the host device this connection belongs to.
> +    #[inline]
> +    pub const fn host_device(&self) -> &hd::Device {
> +        // SAFETY: By the type invariant the connection is valid, and its hd is alive for as
> +        // long as the connection is.
> +        unsafe { hd::Device::from_raw((*self.0.get()).hd) }

[Severity: High]
Does this code allow for a use-after-free of the host device?

Similar to interface(), the hd pointer in gb_connection is not kept alive
by the connection's own reference count. If the parent device goes away, this
can safely return a reference to a dangling pointer if the connection is still
held via ARef<Connection>.

> +    }
> +}

> diff --git a/rust/kernel/greybus/protocols.rs b/rust/kernel/greybus/protocols.rs
> --- /dev/null
> +++ b/rust/kernel/greybus/protocols.rs

[ ... ]

> +/// The header every Greybus message starts with.
> +///
> +/// # Invariants
> +///
> +/// The size field covers the header and the payload that follows it.
> +#[repr(transparent)]
> +pub struct GbOperationMsgHdr(bindings::gb_operation_msg_hdr);
> +
> +// SAFETY: gb_operation_msg_hdr is a POD type with no padding and no interior mutability.
> +unsafe impl kernel::transmute::AsBytes for GbOperationMsgHdr {}
> +
> +impl GbOperationMsgHdr {
> +    /// Builds a header. size is the whole message, this header included.
> +    #[inline]
> +    pub const fn new(size: u16, operation_id: u16, type_: u8, result: u8) -> Self {
> +        Self(bindings::gb_operation_msg_hdr {
> +            size: size.to_le(),

[Severity: Low]
Does this constructor violate the documented invariant for this struct?

The struct defines an # Invariants section documenting that the size field
covers the header and the payload that follows it. However, the constructor
accepts any size: u16 without validation (e.g., ensuring it is at least the
minimum header size), allowing safe code to construct a header that structurally
violates its own invariant.

Also, the Rust subsystem guidelines mandate that constructing a struct with an
# Invariants section must be accompanied by an // INVARIANT: comment
explaining how the invariant is upheld, which appears to be missing here.

> +            operation_id: operation_id.to_le(),
> +            type_,
> +            result,
> +            pad: [0u8; 2],
> +        })
> +    }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013-gb-uart-transport-v3-0-81d0a8a1acf7@beagleboard.org?part=4

  reply	other threads:[~2026-10-13 12:48 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-13 12:00 [PATCH v3 0/7] Add Greybus Sotfsvc and UART Node drivers Ayush Singh
2026-10-13 12:00 ` [PATCH v3 1/7] greybus: connection: Export gb_connection_get() and gb_connection_put() Ayush Singh
2026-10-13 12:00 ` [PATCH v3 2/7] greybus: connection: Add gb_connection_hd_find_by_intf() Ayush Singh
2026-10-13 12:00 ` [PATCH v3 3/7] rust: crc_ccitt: add CRC-CCITT abstraction Ayush Singh
2026-10-13 12:00 ` [PATCH v3 4/7] rust: kernel: Add greybus abstractions Ayush Singh
2026-10-13 12:48   ` sashiko-bot [this message]
2026-10-13 12:00 ` [PATCH v3 5/7] drivers: greybus: Add software SVC implementation Ayush Singh
2026-10-13 12:00 ` [PATCH v3 6/7] dt-bindings: beagle: Add BeagleConnect Freedom Ayush Singh
2026-10-13 12:00 ` [PATCH v3 7/7] greybus: Add Rust UART node driver Ayush Singh
2026-10-13 12:53 ` [PATCH v3 0/7] Add Greybus Sotfsvc and UART Node drivers Miguel Ojeda

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-169737@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=ayush@beagleboard.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=gary@garyguo.net \
    --cc=ojeda@kernel.org \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox