Skip to content

Commit 3323863

Browse files
committed
Merge branch 'for-6.3/bigben' into for-linus
UAF protection in work struct (Pietro Borrello)
2 parents 94109c9 + b94335f commit 3323863

File tree

1 file changed

+63
-12
lines changed

1 file changed

+63
-12
lines changed

drivers/hid/hid-bigbenff.c

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ static __u8 pid0902_rdesc_fixed[] = {
174174
struct bigben_device {
175175
struct hid_device *hid;
176176
struct hid_report *report;
177+
spinlock_t lock;
177178
bool removed;
178179
u8 led_state; /* LED1 = 1 .. LED4 = 8 */
179180
u8 right_motor_on; /* right motor off/on 0/1 */
@@ -184,18 +185,39 @@ struct bigben_device {
184185
struct work_struct worker;
185186
};
186187

188+
static inline void bigben_schedule_work(struct bigben_device *bigben)
189+
{
190+
unsigned long flags;
191+
192+
spin_lock_irqsave(&bigben->lock, flags);
193+
if (!bigben->removed)
194+
schedule_work(&bigben->worker);
195+
spin_unlock_irqrestore(&bigben->lock, flags);
196+
}
187197

188198
static void bigben_worker(struct work_struct *work)
189199
{
190200
struct bigben_device *bigben = container_of(work,
191201
struct bigben_device, worker);
192202
struct hid_field *report_field = bigben->report->field[0];
193-
194-
if (bigben->removed || !report_field)
203+
bool do_work_led = false;
204+
bool do_work_ff = false;
205+
u8 *buf;
206+
u32 len;
207+
unsigned long flags;
208+
209+
buf = hid_alloc_report_buf(bigben->report, GFP_KERNEL);
210+
if (!buf)
195211
return;
196212

213+
len = hid_report_len(bigben->report);
214+
215+
/* LED work */
216+
spin_lock_irqsave(&bigben->lock, flags);
217+
197218
if (bigben->work_led) {
198219
bigben->work_led = false;
220+
do_work_led = true;
199221
report_field->value[0] = 0x01; /* 1 = led message */
200222
report_field->value[1] = 0x08; /* reserved value, always 8 */
201223
report_field->value[2] = bigben->led_state;
@@ -204,11 +226,22 @@ static void bigben_worker(struct work_struct *work)
204226
report_field->value[5] = 0x00; /* padding */
205227
report_field->value[6] = 0x00; /* padding */
206228
report_field->value[7] = 0x00; /* padding */
207-
hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT);
229+
hid_output_report(bigben->report, buf);
230+
}
231+
232+
spin_unlock_irqrestore(&bigben->lock, flags);
233+
234+
if (do_work_led) {
235+
hid_hw_raw_request(bigben->hid, bigben->report->id, buf, len,
236+
bigben->report->type, HID_REQ_SET_REPORT);
208237
}
209238

239+
/* FF work */
240+
spin_lock_irqsave(&bigben->lock, flags);
241+
210242
if (bigben->work_ff) {
211243
bigben->work_ff = false;
244+
do_work_ff = true;
212245
report_field->value[0] = 0x02; /* 2 = rumble effect message */
213246
report_field->value[1] = 0x08; /* reserved value, always 8 */
214247
report_field->value[2] = bigben->right_motor_on;
@@ -217,8 +250,17 @@ static void bigben_worker(struct work_struct *work)
217250
report_field->value[5] = 0x00; /* padding */
218251
report_field->value[6] = 0x00; /* padding */
219252
report_field->value[7] = 0x00; /* padding */
220-
hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT);
253+
hid_output_report(bigben->report, buf);
254+
}
255+
256+
spin_unlock_irqrestore(&bigben->lock, flags);
257+
258+
if (do_work_ff) {
259+
hid_hw_raw_request(bigben->hid, bigben->report->id, buf, len,
260+
bigben->report->type, HID_REQ_SET_REPORT);
221261
}
262+
263+
kfree(buf);
222264
}
223265

224266
static int hid_bigben_play_effect(struct input_dev *dev, void *data,
@@ -228,6 +270,7 @@ static int hid_bigben_play_effect(struct input_dev *dev, void *data,
228270
struct bigben_device *bigben = hid_get_drvdata(hid);
229271
u8 right_motor_on;
230272
u8 left_motor_force;
273+
unsigned long flags;
231274

232275
if (!bigben) {
233276
hid_err(hid, "no device data\n");
@@ -242,10 +285,13 @@ static int hid_bigben_play_effect(struct input_dev *dev, void *data,
242285

243286
if (right_motor_on != bigben->right_motor_on ||
244287
left_motor_force != bigben->left_motor_force) {
288+
spin_lock_irqsave(&bigben->lock, flags);
245289
bigben->right_motor_on = right_motor_on;
246290
bigben->left_motor_force = left_motor_force;
247291
bigben->work_ff = true;
248-
schedule_work(&bigben->worker);
292+
spin_unlock_irqrestore(&bigben->lock, flags);
293+
294+
bigben_schedule_work(bigben);
249295
}
250296

251297
return 0;
@@ -259,6 +305,7 @@ static void bigben_set_led(struct led_classdev *led,
259305
struct bigben_device *bigben = hid_get_drvdata(hid);
260306
int n;
261307
bool work;
308+
unsigned long flags;
262309

263310
if (!bigben) {
264311
hid_err(hid, "no device data\n");
@@ -267,17 +314,19 @@ static void bigben_set_led(struct led_classdev *led,
267314

268315
for (n = 0; n < NUM_LEDS; n++) {
269316
if (led == bigben->leds[n]) {
317+
spin_lock_irqsave(&bigben->lock, flags);
270318
if (value == LED_OFF) {
271319
work = (bigben->led_state & BIT(n));
272320
bigben->led_state &= ~BIT(n);
273321
} else {
274322
work = !(bigben->led_state & BIT(n));
275323
bigben->led_state |= BIT(n);
276324
}
325+
spin_unlock_irqrestore(&bigben->lock, flags);
277326

278327
if (work) {
279328
bigben->work_led = true;
280-
schedule_work(&bigben->worker);
329+
bigben_schedule_work(bigben);
281330
}
282331
return;
283332
}
@@ -307,8 +356,12 @@ static enum led_brightness bigben_get_led(struct led_classdev *led)
307356
static void bigben_remove(struct hid_device *hid)
308357
{
309358
struct bigben_device *bigben = hid_get_drvdata(hid);
359+
unsigned long flags;
310360

361+
spin_lock_irqsave(&bigben->lock, flags);
311362
bigben->removed = true;
363+
spin_unlock_irqrestore(&bigben->lock, flags);
364+
312365
cancel_work_sync(&bigben->worker);
313366
hid_hw_stop(hid);
314367
}
@@ -318,7 +371,6 @@ static int bigben_probe(struct hid_device *hid,
318371
{
319372
struct bigben_device *bigben;
320373
struct hid_input *hidinput;
321-
struct list_head *report_list;
322374
struct led_classdev *led;
323375
char *name;
324376
size_t name_sz;
@@ -343,14 +395,12 @@ static int bigben_probe(struct hid_device *hid,
343395
return error;
344396
}
345397

346-
report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
347-
if (list_empty(report_list)) {
398+
bigben->report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 8);
399+
if (!bigben->report) {
348400
hid_err(hid, "no output report found\n");
349401
error = -ENODEV;
350402
goto error_hw_stop;
351403
}
352-
bigben->report = list_entry(report_list->next,
353-
struct hid_report, list);
354404

355405
if (list_empty(&hid->inputs)) {
356406
hid_err(hid, "no inputs found\n");
@@ -362,6 +412,7 @@ static int bigben_probe(struct hid_device *hid,
362412
set_bit(FF_RUMBLE, hidinput->input->ffbit);
363413

364414
INIT_WORK(&bigben->worker, bigben_worker);
415+
spin_lock_init(&bigben->lock);
365416

366417
error = input_ff_create_memless(hidinput->input, NULL,
367418
hid_bigben_play_effect);
@@ -402,7 +453,7 @@ static int bigben_probe(struct hid_device *hid,
402453
bigben->left_motor_force = 0;
403454
bigben->work_led = true;
404455
bigben->work_ff = true;
405-
schedule_work(&bigben->worker);
456+
bigben_schedule_work(bigben);
406457

407458
hid_info(hid, "LED and force feedback support for BigBen gamepad\n");
408459

0 commit comments

Comments
 (0)