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
next prev parent 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox