Linux Input/HID development
 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 3/3] input: joystick: stinger: port Gravis Stinger driver to safe Rust
Date: Fri, 06 Nov 2026 19:42:11 +0000	[thread overview]
Message-ID: <sashiko-outbox-202161@kernel.org> (raw)
In-Reply-To: <20261106191714.334055-4-harepro29@gmail.com>

Thank you for your contribution! Sashiko AI review found 10 potential
issues to consider:
- [High] Input: stinger - built-in Rust input/serio fails link when
  INPUT/SERIO=m/n
- [Medium] Input: stinger - depends on RUST drops non-Rust support and
  breaks defconfig
- [Medium] Input: stinger - missing MODULE_DEVICE_TABLE breaks module
  autoloading
- [Medium] Input: stinger - missing input_dev parent in connect() breaks
  sysfs hierarchy
- [Medium] Input: stinger - connect() registers input_dev before
  serio_open()
- [Medium] Input: stinger - driver registration uses helper THIS_MODULE
  instead of owner
- [Medium] rust: input: unsound set_phys() and set_abs_params() on
  AllocatedDevice
- [Medium] rust: serio: missing sentinel guarantee on Driver::ID_TABLE
  slice
- [Medium] rust: serio: missing Opaque wrapper for serio_driver in
  Registration
- [Low] rust: serio: negated to_errno() in
  Registration::connect_callback()
--

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

input: joystick: stinger: port Gravis Stinger driver to safe Rust

This commit replaces the C Gravis Stinger serial gamepad driver
(drivers/input/joystick/stinger.c) with a Rust implementation
(drivers/input/joystick/stinger.rs) and makes CONFIG_JOYSTICK_STINGER
depend on CONFIG_RUST.

> diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
> index 7755e5b454d2c..e672a3bc6dccf 100644
> --- a/drivers/input/joystick/Kconfig
> +++ b/drivers/input/joystick/Kconfig
> @@ -188,6 +188,7 @@ config JOYSTICK_SPACEBALL
>  	  module will be called spaceball.
>  
>  config JOYSTICK_STINGER
> +	depends on RUST
>  	tristate "Gravis Stinger gamepad"
>  	select SERIO
>  	help

[Severity: Medium]
Does adding depends on RUST directly to JOYSTICK_STINGER while deleting
drivers/input/joystick/stinger.c cause a hardware support regression on
non-Rust configurations and architectures without HAVE_RUST?

For example, arch/powerpc/configs/ppc6xx_defconfig enables
CONFIG_JOYSTICK_STINGER=m without CONFIG_RUST, so the driver is silently
dropped from that defconfig. Documentation/input/joydev/joystick.rst also
still states that the device is supported by the stinger.c module.

[Severity: High]
Can enabling CONFIG_RUST=y with CONFIG_INPUT=m or CONFIG_SERIO=m (such as
CONFIG_JOYSTICK_STINGER=m, which only selects SERIO under if INPUT) break
the vmlinux link?

CONFIG_RUST is a bool option, so rust/kernel.o and rust/helpers/helpers.o
are always linked into vmlinux, and both unconditionally compile the input
and serio abstractions and C helpers without guarding on CONFIG_INPUT=y or
CONFIG_SERIO=y. When CONFIG_INPUT or CONFIG_SERIO is modular or disabled,
built-in code in rust/kernel/input.rs and rust/helpers/{input,serio}.c
references input_allocate_device(), input_register_device(), input_event(),
__serio_register_driver(), and serio_unregister_driver(), which are either
in loadable modules or not built at all.

[ ... ]

> diff --git a/drivers/input/joystick/stinger.rs b/drivers/input/joystick/stinger.rs
> new file mode 100644
> index 0000000000000..daa1994636315
> --- /dev/null
> +++ b/drivers/input/joystick/stinger.rs

[ ... ]

> +impl serio::Driver for StingerDriver {
> +    type Data = KBox<Stinger>;
> +
> +    const ID_TABLE: &'static [serio::DeviceId] = &[
> +        serio::DeviceId::new(
> +            bindings::SERIO_RS232 as u8,
> +            bindings::SERIO_ANY as u8,
> +            bindings::SERIO_ANY as u8,
> +            bindings::SERIO_STINGER as u8,
> +        ),
> +        serio::DeviceId::new(0, 0, 0, 0),
> +    ];

[Severity: Medium]
Does dropping MODULE_DEVICE_TABLE(serio, stinger_serio_ids) without a
replacement module_device_table! or module alias entry break automatic
module loading when CONFIG_JOYSTICK_STINGER=m?

Without the exported device table symbol, modpost no longer emits
MODULE_ALIAS("serio:ty02pr1Eid*ex*") into stinger.ko, so udev/kmod cannot
autoload the module when serio_uevent() reports a matching serio port.

[Severity: Medium]
Can a safe implementation of serio::Driver omit the trailing zero entry in
ID_TABLE and cause an out-of-bounds read in serio_match_port()?

In rust/kernel/serio.rs, Driver::ID_TABLE is typed as a plain slice
&'static [DeviceId] rather than a compile-time zero-terminated array, and
Registration::new() passes T::ID_TABLE.as_ptr() directly to the C serio
core:

rust/kernel/serio.rs:Registration::new() {
    let drv = bindings::serio_driver {
        description: name.as_char_ptr().cast(),
        id_table: T::ID_TABLE.as_ptr().cast::<bindings::serio_device_id>(),
}

Because serio_match_port() walks ids++ until both ids->type and ids->proto
are zero, any safe serio::Driver that omits the sentinel entry triggers an
out-of-bounds read past the slice.

> +
> +    fn connect(dev: &mut serio::Device) -> Result<Self::Data> {
> +        let mut input_alloc = AllocatedDevice::new()?;
> +
> +        input_alloc.set_name(DRIVER_DESC);
> +        input_alloc.set_phys(dev.phys().as_ptr());
> +        input_alloc.set_id(bus::RS232, bindings::SERIO_STINGER as u16, 0x0001, 0x0100);

[Severity: Medium]
Does StingerDriver::connect() register the input device with a NULL parent
instead of &serio->dev?

The C driver set input_dev->dev.parent = &serio->dev before calling
input_register_device(), whereas AllocatedDevice::new() wraps
input_allocate_device() without setting dev.parent. Registering with a NULL
parent places the device under /sys/devices/virtual/input/ instead of under
the serio port in sysfs, which breaks udev parent matching (such as ID_PATH
and seat assignment) and suspend/resume ordering.

[Severity: Medium]
Are AllocatedDevice::set_phys() and AllocatedDevice::set_abs_params() sound
as safe functions in rust/kernel/input.rs?

rust/kernel/input.rs:AllocatedDevice {
    pub fn set_phys(&mut self, phys: *const u8) {
        // SAFETY: `self.ptr` is valid.
        unsafe {
            (*self.ptr.as_ptr()).phys = phys.cast();
        }
    }
    ...
    pub fn set_abs_params(&mut self, axis: u32, min: i32, max: i32, fuzz: i32, flat: i32) {
        // SAFETY: `self.ptr` is valid.
        unsafe {
            bindings::input_set_abs_params(self.ptr.as_ptr(), axis, min, max, fuzz, flat);
        }
    }
}

set_phys() accepts an arbitrary raw pointer from safe code without any
validity, lifetime, or NUL-termination guarantee, even though the input core
dereferences dev->phys as a C string in input_dev_uevent(), sysfs, procfs,
and EVIOCGPHYS for the lifetime of the device. set_abs_params() passes an
unvalidated axis index to input_set_abs_params(), which executes
__set_bit(axis, dev->absbit) and indexes dev->absinfo[axis] without checking
that axis < ABS_CNT, allowing out-of-bounds writes from safe Rust.

> +
> +        // Configure supported buttons
> +        for btn in [
> +            bindings::BTN_A,
> +            bindings::BTN_B,
> +            bindings::BTN_C,
> +            bindings::BTN_X,
> +            bindings::BTN_Y,
> +            bindings::BTN_Z,
> +            bindings::BTN_TL,
> +            bindings::BTN_TR,
> +            bindings::BTN_START,
> +            bindings::BTN_SELECT,
> +        ] {
> +            input_alloc.set_capability(event_type::KEY, btn);
> +        }
> +
> +        // Configure 2-axis joystick with flat margin of 4
> +        input_alloc.set_capability(event_type::ABS, bindings::ABS_X);
> +        input_alloc.set_capability(event_type::ABS, bindings::ABS_Y);
> +        input_alloc.set_abs_params(bindings::ABS_X, -64, 64, 0, 4);
> +        input_alloc.set_abs_params(bindings::ABS_Y, -64, 64, 0, 4);
> +
> +        let registered = input_alloc.register()?;
> +
> +        let stinger = KBox::new(
> +            Stinger {
> +                input: registered,
> +                idx: 0,
> +                data: [0; STINGER_MAX_LENGTH],
> +            },
> +            GFP_KERNEL,
> +        )?;
> +
> +        Ok(stinger)
> +    }

[Severity: Medium]
Does StingerDriver::connect() publish the input device before the serio
port is opened?

In the C driver, stinger_connect() set the serio drvdata and called
serio_open() before calling input_register_device(). Here,
input_alloc.register() exposes the device to the input core and userspace
before KBox::new() allocates Stinger and before
Registration::connect_callback() calls serio_set_drvdata() and
serio_open():

rust/kernel/serio.rs:Registration::connect_callback() {
    let data = match T::connect(&mut dev) {
        Ok(d) => d,
        Err(e) => return e.to_errno(),
    };

    let raw_data = data.into_foreign();
    unsafe {
        bindings::serio_set_drvdata(serio, raw_data.cast());
    }
    let ret = unsafe { bindings::serio_open(serio, drv) };
}

Userspace can therefore open the input node before the underlying serio port
is opened, and a failure in KBox::new() or serio_open() registers and
immediately unregisters the input device, emitting spurious uevents.

[Severity: Low]
In Registration::connect_callback() in rust/kernel/serio.rs, does the null
check return a positive value instead of a negative errno?

rust/kernel/serio.rs:Registration::connect_callback() {
    let Some(serio_nn) = NonNull::new(serio) else {
        return -(crate::error::code::EINVAL.to_errno());
    };
}

Error::to_errno() already returns a negative errno (-22), so negating it
returns +22 instead of -EINVAL.

[ ... ]

> +struct StingerModule {
> +    _reg: serio::Registration<StingerDriver>,
> +}

[Severity: Medium]
Does dropping serio::Registration<T> violate Rust aliasing rules on
bindings::serio_driver?

rust/kernel/serio.rs:Registration {
    drv: Pin<KBox<bindings::serio_driver>>,
    _p: PhantomData<T>,
}

rust/kernel/serio.rs:Registration::drop() {
    unsafe {
        bindings::serio_unregister_driver(self.drv.as_mut().get_mut());
    }
}

Once serio_register_driver() publishes the driver, the C serio core and
sysfs attributes (such as bind_mode_store(), which writes
serio_drv->manual_bind without locking) hold pointers to the struct and may
read or mutate it concurrently. Storing bindings::serio_driver without
kernel::types::Opaque and calling self.drv.as_mut().get_mut() in drop()
creates an exclusive &mut reference while C still has active aliases.

> +
> +impl kernel::Module for StingerModule {
> +    fn init(_module: &'static ThisModule) -> Result<Self> {
> +        let reg = serio::Registration::<StingerDriver>::new(DRIVER_NAME)?;
> +        pr_info!("Gravis Stinger safe Rust driver initialized\n");
> +        Ok(Self { _reg: reg })
> +    }
> +}

[Severity: Medium]
Does serio::Registration::new() register the driver with the helper
translation unit's THIS_MODULE and KBUILD_MODNAME instead of the driver
module's owner and name?

StingerModule::init() ignores _module, and Registration::new() calls
rust_helper_serio_register_driver():

rust/helpers/serio.c:rust_helper_serio_register_driver() {
	return __serio_register_driver(drv, THIS_MODULE, KBUILD_MODNAME);
}

Because rust/helpers/serio.c is compiled into the built-in helpers object
rather than into stinger.ko, THIS_MODULE is NULL and KBUILD_MODNAME is
"helpers". When CONFIG_JOYSTICK_STINGER=m, drv->driver.owner is NULL and
drv->driver.mod_name is "helpers", so the driver core cannot pin the module
reference count and /sys/bus/serio/drivers/stinger/module linkage is
broken. Registration::new() should accept &'static ThisModule and pass
module.as_ptr() and module name to __serio_register_driver().

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

      reply	other threads:[~2026-11-06 19:42 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
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 [this message]

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-202161@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