Skip to content

Commit

Permalink
Input: uinput - avoid crash when sending FF request to device going away
Browse files Browse the repository at this point in the history
If FF request comes in while uinput device is going away,
uinput_request_send() will fail with -ENODEV, and uinput_request_submit()
will attempt to mark the slot as unused by calling uinput_request_done().
Unfortunately in this case we haven't initialized request->done completion
yet, and we get a crash:

[   39.402036] BUG: spinlock bad magic on CPU#1, fftest/3108
[   39.402046]  lock: 0xffff88006a93bb00, .magic: 00000000, .owner: /39, .owner_cpu: 1217155072
[   39.402055] CPU: 1 PID: 3108 Comm: fftest Tainted: G        W 4.13.0+ #15
[   39.402059] Hardware name: LENOVO 20HQS0EG02/20HQS0EG02, BIOS N1MET37W (1.22 ) 07/04/2017
[   39.402064]  0000000000000086 f0fad82f3ceaa120 ffff88006a93b9a0 ffffffff9de941bb
[   39.402077]  ffff88026df8ae00 ffff88006a93bb00 ffff88006a93b9c0 ffffffff9dca62b7
[   39.402088]  ffff88006a93bb00 ffff88006a93baf8 ffff88006a93b9e0 ffffffff9dca62e7
[   39.402099] Call Trace:
[   39.402112]  [<ffffffff9de941bb>] dump_stack+0x4d/0x63
[   39.402123]  [<ffffffff9dca62b7>] spin_dump+0x97/0x9c
[   39.402130]  [<ffffffff9dca62e7>] spin_bug+0x2b/0x2d
[   39.402138]  [<ffffffff9dca6373>] do_raw_spin_lock+0x28/0xfd
[   39.402147]  [<ffffffff9e3055cd>] _raw_spin_lock_irqsave+0x19/0x1f
[   39.402154]  [<ffffffff9dca05b7>] complete+0x1d/0x48
[   39.402162]  [<ffffffffc04f30af>] 0xffffffffc04f30af
[   39.402167]  [<ffffffffc04f468c>] 0xffffffffc04f468c
[   39.402177]  [<ffffffff9dd59c16>] ? __slab_free+0x22f/0x359
[   39.402184]  [<ffffffff9dcc13e9>] ? tk_clock_read+0xc/0xe
[   39.402189]  [<ffffffffc04f471f>] 0xffffffffc04f471f
[   39.402195]  [<ffffffff9dc9ffe5>] ? __wake_up+0x44/0x4b
[   39.402200]  [<ffffffffc04f3240>] ? 0xffffffffc04f3240
[   39.402207]  [<ffffffff9e0f57f3>] erase_effect+0xa1/0xd2
[   39.402214]  [<ffffffff9e0f58c6>] input_ff_flush+0x43/0x5c
[   39.402219]  [<ffffffffc04f32ad>] 0xffffffffc04f32ad
[   39.402227]  [<ffffffff9e0f174f>] input_flush_device+0x3d/0x51
[   39.402234]  [<ffffffff9e0f69ae>] evdev_flush+0x49/0x5c
[   39.402243]  [<ffffffff9dd62d6e>] filp_close+0x3f/0x65
[   39.402253]  [<ffffffff9dd7dcf7>] put_files_struct+0x66/0xc1
[   39.402261]  [<ffffffff9dd7ddeb>] exit_files+0x47/0x4e
[   39.402270]  [<ffffffff9dc6b329>] do_exit+0x483/0x969
[   39.402278]  [<ffffffff9dc73211>] ? recalc_sigpending_tsk+0x3d/0x44
[   39.402285]  [<ffffffff9dc6c7a2>] do_group_exit+0x42/0xb0
[   39.402293]  [<ffffffff9dc767e1>] get_signal+0x58d/0x5bf
[   39.402300]  [<ffffffff9dc03701>] do_signal+0x37/0x53e
[   39.402307]  [<ffffffff9e0f8401>] ? evdev_ioctl_handler+0xac8/0xb04
[   39.402314]  [<ffffffff9e0f8464>] ? evdev_ioctl+0x10/0x12
[   39.402321]  [<ffffffff9dd74cfa>] ? do_vfs_ioctl+0x42e/0x501
[   39.402328]  [<ffffffff9dc0170e>] prepare_exit_to_usermode+0x66/0x90
[   39.402333]  [<ffffffff9dc0181b>] syscall_return_slowpath+0xe3/0xec
[   39.402339]  [<ffffffff9e305b7b>] int_ret_from_sys_call+0x25/0x8f

While we could solve this by simply initializing the completion earlier, we
are better off rearranging the code a bit so we avoid calling complete() on
requests that we did not send out. This patch consolidates marking request
slots as free in one place (in uinput_request_submit(), the same place
where we acquire them) and having everyone else simply signal completion
of the requests.

Fixes: 00ce756 ("Input: uinput - mark failed submission requests as free")
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
  • Loading branch information
dtor committed Sep 21, 2017
1 parent e8b9572 commit 6b4877c
Showing 1 changed file with 21 additions and 18 deletions.
39 changes: 21 additions & 18 deletions drivers/input/misc/uinput.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,15 @@ static int uinput_request_reserve_slot(struct uinput_device *udev,
uinput_request_alloc_id(udev, request));
}

static void uinput_request_done(struct uinput_device *udev,
struct uinput_request *request)
static void uinput_request_release_slot(struct uinput_device *udev,
unsigned int id)
{
/* Mark slot as available */
udev->requests[request->id] = NULL;
wake_up(&udev->requests_waitq);
spin_lock(&udev->requests_lock);
udev->requests[id] = NULL;
spin_unlock(&udev->requests_lock);

complete(&request->done);
wake_up(&udev->requests_waitq);
}

static int uinput_request_send(struct uinput_device *udev,
Expand Down Expand Up @@ -138,20 +139,22 @@ static int uinput_request_send(struct uinput_device *udev,
static int uinput_request_submit(struct uinput_device *udev,
struct uinput_request *request)
{
int error;
int retval;

error = uinput_request_reserve_slot(udev, request);
if (error)
return error;
retval = uinput_request_reserve_slot(udev, request);
if (retval)
return retval;

error = uinput_request_send(udev, request);
if (error) {
uinput_request_done(udev, request);
return error;
}
retval = uinput_request_send(udev, request);
if (retval)
goto out;

wait_for_completion(&request->done);
return request->retval;
retval = request->retval;

out:
uinput_request_release_slot(udev, request->id);
return retval;
}

/*
Expand All @@ -169,7 +172,7 @@ static void uinput_flush_requests(struct uinput_device *udev)
request = udev->requests[i];
if (request) {
request->retval = -ENODEV;
uinput_request_done(udev, request);
complete(&request->done);
}
}

Expand Down Expand Up @@ -957,7 +960,7 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
}

req->retval = ff_up.retval;
uinput_request_done(udev, req);
complete(&req->done);
goto out;

case UI_END_FF_ERASE:
Expand All @@ -973,7 +976,7 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
}

req->retval = ff_erase.retval;
uinput_request_done(udev, req);
complete(&req->done);
goto out;
}

Expand Down

0 comments on commit 6b4877c

Please sign in to comment.