Thread (7 messages) 7 messages, 2 authors, 7d ago

Re: [PATCH v9 2/3] HID: nintendo: Add rumble support for Switch 2 controllers

From: sashiko-bot@kernel.org
Date: 2026-07-01 07:17:39

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Use-After-Free on `ns2->rumble_buffer` during device teardown due to devres lifecycle mismatch.
- [Medium] Rumble work scheduling math error adds 2ms instead of subtracting, causing it to run late and risk buffer underruns.
- [Medium] Pro Controller rumble motors receive identical signals, failing to control them individually as claimed.
--

commit 0899ea0ef032bf60e7abf4c07b73da1a806a6406
Author: Vicki Pfau [off-list ref]
Date:   Wed Jul 1 06:56:03 2026 +0000

    HID: nintendo: Add rumble support for Switch 2 controllers
    
    This adds rumble support for both the "HD Rumble" linear resonant actuator
    type as used in the Joy-Cons and Pro Controller, as well as the eccentric
    rotating mass type used in the GameCube controller. Note that since there's
    currently no API for exposing full control of LRAs with evdev, it only
    simulates a basic rumble for now.
quoted hunk ↗ jump to hunk
diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c
index 5aacb8e88b704..96463e1586bbd 100644
--- a/drivers/hid/hid-nintendo.c
+++ b/drivers/hid/hid-nintendo.c
@@ -3205,6 +3259,125 @@ static void switch2_kref_put(struct kref *refcount)
[ ... ]
+static int switch2_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect)
+{
[ ... ]
+	if (ns2->ctlr_type == NS2_CTLR_TYPE_GC) {
+		ns2->rumble.sd.amplitude = max(effect->u.rumble.strong_magnitude,
+			(uint16_t) (effect->u.rumble.weak_magnitude >> 1));
+	} else {
+		ns2->rumble.hd.hi_freq = RUMBLE_HI_FREQ;
+		ns2->rumble.hd.lo_freq = RUMBLE_LO_FREQ;
+		ns2->rumble.hd.hi_amp = effect->u.rumble.weak_magnitude * RUMBLE_MAX >> 16;
+		ns2->rumble.hd.lo_amp = effect->u.rumble.strong_magnitude * RUMBLE_MAX >> 16;
+	}
+	spin_unlock_irqrestore(&ns2->rumble_lock, flags);
+
+	schedule_delayed_work(&ns2->rumble_work, 0);
+
+	return 0;
+}
+
+static void switch2_rumble_work(struct work_struct *work)
+{
[ ... ]
+	} else {
+		ns2->rumble_buffer[0] = 1;
+		switch2_encode_rumble(&ns2->rumble.hd, &ns2->rumble_buffer[0x2]);
+		active = ns2->rumble.hd.hi_amp || ns2->rumble.hd.lo_amp;
+		if (ns2->ctlr_type == NS2_CTLR_TYPE_PRO) {
+			/*
+			 * The Pro Controller contains separate LRAs on each
+			 * side that can be controlled individually.
+			 */
+			ns2->rumble_buffer[0] = 2;
+			ns2->rumble_buffer[0x11] = 0x50 | ns2->rumble_seq;
+			switch2_encode_rumble(&ns2->rumble.hd, &ns2->rumble_buffer[0x12]);
+		}
+	}
[Severity: Medium]
Does this code accurately provide independent control of the two rumble motors?

The commit message and the comment above mention that the Pro Controller's left
and right rumble motors can be controlled individually.

However, in switch2_play_effect(), the strong and weak magnitudes are mixed
into a single switch2_hd_rumble structure (ns2->rumble.hd). 

Then in switch2_rumble_work(), this exact same structure is encoded into both
the left (offset 0x2) and right (offset 0x12) rumble buffers. 

Since both physical motors receive the exact same mixed signal (both high and 
low frequencies simultaneously), doesn't this result in identical rumble on 
both sides rather than individual control?
+	ns2->rumble_seq = (ns2->rumble_seq + 1) & 0xF;
+	spin_unlock_irqrestore(&ns2->rumble_lock, flags);
+
+	if (active) {
+		unsigned long interval = msecs_to_jiffies(4);
+		uint64_t current_jiffies = get_jiffies_64();
+
+		if (!ns2->last_rumble_work)
+			ns2->last_rumble_work = current_jiffies;
+		else
+			ns2->last_rumble_work += interval;
+
+		/* Reschedule a little early to make sure the buffer never underruns */
+		interval += msecs_to_jiffies(2);
+		if (ns2->last_rumble_work + interval >= current_jiffies)
+			schedule_delayed_work(&ns2->rumble_work,
+				ns2->last_rumble_work + interval - current_jiffies);
[Severity: Medium]
Does this schedule the work early or late?

The comment mentions rescheduling slightly early to prevent buffer underruns.

Since interval starts at 4ms, adding 2ms to it with
interval += msecs_to_jiffies(2) makes the new interval 6ms. 

When scheduling for ns2->last_rumble_work + interval, this appears to target
a time 6ms after the last theoretical tick instead of the intended earlier
target (which would be 2ms after, or interval - 2ms).

Could this delay cause the work to run later than intended and lead to the 
buffer underruns the comment warns about?

[ ... ]
quoted hunk ↗ jump to hunk
@@ -3346,6 +3522,20 @@ static int switch2_init_input(struct switch2_controller *ns2)
[ ... ]
+#if IS_ENABLED(CONFIG_NINTENDO_FF)
+	ns2->rumble_buffer = devm_kzalloc(&input->dev, 64, GFP_KERNEL);
+	if (!ns2->rumble_buffer) {
+		input_free_device(input);
+		return -ENOMEM;
+	}
[Severity: High]
Can this lead to a use-after-free during device removal?

The rumble_buffer is allocated with devm_kzalloc() tied to the input device, 
so it gets freed when the input device is unregistered and its final reference
is dropped.

However, looking at the teardown sequence, if the input device is unregistered
(for example via switch2_controller_put() calling input_unregister_device())
before the delayed work is completely synced and canceled, the worker could run
and access the freed rumble_buffer.

Would it be safer to allocate this buffer without devm, or ensure the delayed
work is fully canceled prior to unregistering the input device?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260701065606.4169999-1-vi@endrift.com?part=2
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help