All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: clicedvly768 <harepro29@gmail.com>
Cc: linux-input@vger.kernel.org, dmitry.torokhov@gmail.com,
	ojeda@kernel.org, gary@garyguo.net
Subject: Re: [PATCH 2/3] rust: serio: add serio bus abstractions
Date: Fri, 06 Nov 2026 19:35:55 +0000	[thread overview]
Message-ID: <sashiko-outbox-202155@kernel.org> (raw)
In-Reply-To: <20261106191714.334055-3-harepro29@gmail.com>

Thank you for your contribution! Sashiko AI review found 8 potential
issues to consider:
- [High] rust: serio: rust_helper_serio_register_driver() uses wrong
  THIS_MODULE
- [High] rust: serio: Driver::ID_TABLE slice lacks sentinel causing OOB
  read
- [Medium] rust: serio: unguarded serio and input build fails when
  CONFIG_SERIO=m/n
- [Medium] rust: serio: connect_callback() negates negative errno and
  opens port late
- [Medium] rust: serio: Registration stores serio_driver without Opaque
  causing UB
- [Low] rust: serio: IrqReturn duplicates kernel::irq::IrqReturn with
  wrong paths
- [Low] rust: serio: Device and Registration bypass generic driver
  infrastructure
- [Low] rust: serio: missing inline attributes, non-vertical imports,
  and core::ffi
--

commit a79cbd7ccd6c2d027f39fc787dc865ce37a52295
Author: clicedvly768 <harepro29@gmail.com>

rust: serio: add serio bus abstractions

This commit adds Rust abstractions and C helper wrappers for the serio
bus subsystem, including DeviceId, Device, IrqReturn, Driver, and
Registration.

> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> index 7a11f5fa461d2..84ffdb015b6d9 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -66,6 +66,7 @@
>  #include "fs.c"
>  #include "fwctl.c"
>  #include "gpu.c"
> +#include "input.c"
>  #include "interrupt.c"
>  #include "io.c"
>  #include "irq.c"
> @@ -92,6 +93,7 @@
>  #include "scatterlist.c"
>  #include "security.c"
>  #include "serdev.c"
> +#include "serio.c"
>  #include "signal.c"
>  #include "slab.c"
>  #include "spinlock.c"

[ ... ]

> diff --git a/rust/helpers/serio.c b/rust/helpers/serio.c
> new file mode 100644
> index 0000000000000..cd061495d9b1d
> --- /dev/null
> +++ b/rust/helpers/serio.c
> @@ -0,0 +1,23 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/serio.h>
> +
> +__rust_helper void *rust_helper_serio_get_drvdata(struct serio *serio)
> +{
> +	return serio_get_drvdata(serio);
> +}
> +
> +__rust_helper void rust_helper_serio_set_drvdata(struct serio *serio, void *data)
> +{
> +	serio_set_drvdata(serio, data);
> +}
> +
> +__rust_helper int rust_helper_serio_register_driver(struct serio_driver *drv)
> +{
> +	return __serio_register_driver(drv, THIS_MODULE, KBUILD_MODNAME);
> +}

[Severity: High]
Does evaluating THIS_MODULE and KBUILD_MODNAME inside
rust_helper_serio_register_driver() leave drv->driver.owner as NULL for
loadable Rust serio drivers?

Because rust/helpers/serio.c is compiled into rust/helpers/helpers.o rather
than the registering driver module, THIS_MODULE expands to NULL (when
helpers.o is built-in) and KBUILD_MODNAME expands to "helpers".  When
__serio_register_driver() sets drv->driver.owner = owner, neither the
driver core nor serio_queue_event() (via try_module_get(drv->driver.owner))
can pin the driver module while ports are bound or kseriod events are
queued, allowing the module to be unloaded while still in use and causing a
use-after-free.

Since __serio_register_driver() and serio_unregister_driver() are already
exported non-inline C functions, these two helpers can be dropped and
Registration::new() can take a &'static ThisModule and pass
module.as_ptr() and name.as_char_ptr() directly to
bindings::__serio_register_driver().

> +
> +__rust_helper void rust_helper_serio_unregister_driver(struct serio_driver *drv)
> +{
> +	serio_unregister_driver(drv);
> +}
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index 559fc79b1e820..c9f82dce8f823 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -86,6 +86,7 @@
>  #[doc(hidden)]
>  pub mod impl_flags;
>  pub mod init;
> +pub mod input;
>  pub mod interop;
>  pub mod interrupt;
>  pub mod io;
> @@ -128,6 +129,7 @@
>  pub mod seq_file;
>  #[cfg(CONFIG_RUST_SERIAL_DEV_BUS_ABSTRACTIONS)]
>  pub mod serdev;
> +pub mod serio;
>  pub mod sizes;
>  #[cfg(CONFIG_SOC_BUS)]
>  pub mod soc;

[Severity: Medium]
Does building with CONFIG_SERIO=m or CONFIG_SERIO=n (or CONFIG_INPUT=m/n)
fail to link vmlinux?

CONFIG_SERIO and CONFIG_INPUT are tristate Kconfig options, and
include/linux/serio.h and include/linux/input.h declare
__serio_register_driver(), serio_unregister_driver(), serio_open(),
serio_close(), and the input_*() functions without inline stubs when
disabled.  Because rust/kernel/lib.rs and rust/helpers/helpers.c are always
built into vmlinux when CONFIG_RUST=y, compiling rust/helpers/serio.c,
rust/helpers/input.c, kernel::serio, and kernel::input without Kconfig
guards causes undefined symbol references at link time whenever
CONFIG_SERIO or CONFIG_INPUT is n or m.

[ ... ]

> diff --git a/rust/kernel/serio.rs b/rust/kernel/serio.rs
> new file mode 100644
> index 0000000000000..3b752ce4b64de
> --- /dev/null
> +++ b/rust/kernel/serio.rs
> @@ -0,0 +1,223 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Serio (Serial I/O) bus abstractions for safe Rust drivers.
> +//!
> +//! Provides a safe Rust interface for serio devices and drivers in the Linux kernel,
> +//! supporting input devices such as serial gamepads (e.g. Gravis Stinger), mice, and keyboards.
> +
> +use crate::{
> +    bindings,
> +    error::{to_result, Result},
> +    prelude::*,
> +    types::ForeignOwnable,
> +};
> +use core::{marker::PhantomData, pin::Pin, ptr::NonNull};
> +
> +/// Identifies supported serio hardware devices for auto-probing and matching.
> +#[repr(transparent)]
> +#[derive(Copy, Clone)]
> +pub struct DeviceId(bindings::serio_device_id);
> +
> +impl DeviceId {
> +    /// Creates a new serio device identifier.
> +    pub const fn new(type_: u8, extra: u8, id: u8, proto: u8) -> Self {
> +        Self(bindings::serio_device_id {
> +            type_,
> +            extra,
> +            id,
> +            proto,
> +        })
> +    }
> +}

[Severity: Low]
This isn't a bug, but there are a few style and convention issues in this
file:

- The imports at the top use horizontal grouping instead of the kernel
  vertical import style.
- Small functions in the kernel crate (DeviceId::new(), IrqReturn::to_c(),
  Device::as_raw(), and Device::phys()) are missing #[inline] attributes.
- connect_callback() and interrupt_callback() use core::ffi::{c_int,
  c_uchar, c_uint} instead of the crate::ffi / prelude C integer types.

> +
> +/// Status returned by a serio driver interrupt handler.
> +#[derive(Copy, Clone, PartialEq, Eq)]
> +pub enum IrqReturn {
> +    /// Interrupt was not handled by this driver.
> +    None,
> +    /// Interrupt was successfully handled.
> +    Handled,
> +}
> +
> +impl IrqReturn {
> +    fn to_c(self) -> bindings::irqreturn_t {
> +        match self {
> +            Self::None => bindings::irqreturn::IRQ_NONE,
> +            Self::Handled => bindings::irqreturn::IRQ_HANDLED,
> +        }
> +    }
> +}

[Severity: Low]
Can this reuse kernel::irq::IrqReturn instead of defining a second
IrqReturn enum?  Also, bindgen generates bindings::irqreturn_IRQ_NONE and
bindings::irqreturn_IRQ_HANDLED rather than bindings::irqreturn::IRQ_*.

> +
> +/// A serio port device instance passed to driver callbacks.
> +pub struct Device {
> +    ptr: NonNull<bindings::serio>,
> +}

[Severity: Low]
Should Device and Registration integrate with the generic device and
driver abstractions in kernel::device and kernel::driver?

Wrapping Opaque<bindings::serio> with a device::DeviceContext parameter and
implementing AsRef<device::Device<Ctx>> (and using driver::RegistrationOps
/ driver::DriverLayout for registration) would allow serio drivers to
obtain a &device::Device for setting parent devices (such as
input_dev->dev.parent = &serio->dev), logging via dev_info! / dev_err!, and
using devres.

[ ... ]

> +/// Trait implemented by serio device drivers.
> +pub trait Driver: Send + Sync + Sized {
> +    /// Context data stored per connected device instance.
> +    type Data: ForeignOwnable + Send + Sync;
> +
> +    /// Device ID match table.
> +    const ID_TABLE: &'static [DeviceId];

[Severity: High]
Can passing a plain &'static [DeviceId] slice to C as drv->id_table cause
an out-of-bounds read in serio_match_port()?

drivers/input/serio/serio.c:serio_match_port() walks the table until it
reaches a zeroed sentinel entry:

drivers/input/serio/serio.c:serio_match_port() {
	while (ids->type || ids->proto) {
		if ((ids->type == SERIO_ANY || ids->type == serio->id.type) &&
...
		ids++;
	}
}

Because Driver is a safe trait and &[DeviceId] does not enforce a trailing
zero sentinel (and an empty slice passes a dangling pointer from
as_ptr()), any safe Driver implementation that does not manually append a
zeroed DeviceId entry will cause serio_match_port() to read past the end of
ID_TABLE when probing a non-matching port.  DeviceId should implement
kernel::device_id::RawDeviceId and use kernel::device_id::IdTable /
IdArray so the zero terminator is guaranteed at compile time.

[ ... ]

> +    /// # Safety
> +    ///
> +    /// Must only be invoked by the serio subsystem when a matching port is probed.
> +    /// `serio` and `drv` must be valid non-null pointers provided by the kernel.
> +    unsafe extern "C" fn connect_callback(
> +        serio: *mut bindings::serio,
> +        drv: *mut bindings::serio_driver,
> +    ) -> core::ffi::c_int {
> +        let Some(serio_nn) = NonNull::new(serio) else {
> +            return -(crate::error::code::EINVAL.to_errno());
> +        };
> +
> +        let mut dev = Device { ptr: serio_nn };
> +
> +        let data = match T::connect(&mut dev) {
> +            Ok(d) => d,
> +            Err(e) => return e.to_errno(),
> +        };
> +
> +        let raw_data = data.into_foreign();
> +        // SAFETY: `serio` is valid; store driver data pointer.
> +        unsafe {
> +            bindings::serio_set_drvdata(serio, raw_data.cast());
> +        }
> +
> +        // SAFETY: Open serio port to start receiving characters.
> +        let ret = unsafe { bindings::serio_open(serio, drv) };

[Severity: Medium]
Does connect_callback() return a positive errno on NULL and invoke
T::connect() before the port is opened?

Error::to_errno() already returns a negative errno (-22 for EINVAL), so
-(crate::error::code::EINVAL.to_errno()) returns +22 instead of -EINVAL.

In addition, calling T::connect() before serio_set_drvdata() and
serio_open() means the serio port is not open during T::connect(), and a
driver that registers an input device inside T::connect() exposes the input
device to userspace before serio_open() has succeeded.

[ ... ]

> +impl<T: Driver> Drop for Registration<T> {
> +    fn drop(&mut self) {
> +        // SAFETY: Unregister driver with kernel.
> +        unsafe {
> +            bindings::serio_unregister_driver(self.drv.as_mut().get_mut());
> +        }
> +    }
> +}

[Severity: Medium]
Does self.drv.as_mut().get_mut() violate Rust's aliasing rules while the
serio driver is still registered with the kernel?

Registration<T> stores bindings::serio_driver directly in
Pin<KBox<bindings::serio_driver>> without wrapping it in
kernel::types::Opaque.  While the driver is registered, C code in the
serio core holds pointers to struct serio_driver and accesses or mutates
its fields (such as drv->manual_bind in bind_mode_store() and
serio_unregister_driver()), so creating an exclusive &mut
bindings::serio_driver before serio_unregister_driver() completes is
undefined behavior.  Wrapping bindings::serio_driver in Opaque avoids
forming an exclusive reference to C-owned memory.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261106191714.334055-1-harepro29@gmail.com?part=2

  reply	other threads:[~2026-11-06 19:35 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-06 19:17 [PATCH 0/3] rust: input: add input/serio abstractions and port Gravis Stinger driver clicedvly768
2026-11-06 19:17 ` [PATCH 1/3] rust: input: add input device abstractions clicedvly768
2026-11-06 19:29   ` sashiko-bot
2026-11-06 19:17 ` [PATCH 2/3] rust: serio: add serio bus abstractions clicedvly768
2026-11-06 19:35   ` sashiko-bot [this message]
2026-11-06 19:17 ` [PATCH 3/3] input: joystick: stinger: port Gravis Stinger driver to safe Rust clicedvly768
2026-11-06 19:42   ` sashiko-bot

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-202155@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=gary@garyguo.net \
    --cc=harepro29@gmail.com \
    --cc=linux-input@vger.kernel.org \
    --cc=ojeda@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.