diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2017-09-06 19:22:59 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2017-09-21 19:31:23 -0400 |
commit | 6b4877c7bdc6ae39ce03716df7caeecf204697eb (patch) | |
tree | 07b43428be1a41e3c7114af9256303ca4f17bb7c /drivers/input/misc | |
parent | e8b95728f724797f958912fd9b765a695595d3a6 (diff) |
Input: uinput - avoid crash when sending FF request to device going away
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: 00ce756ce53a ("Input: uinput - mark failed submission requests as free")
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/input/misc')
-rw-r--r-- | drivers/input/misc/uinput.c | 39 |
1 files changed, 21 insertions, 18 deletions
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 2cff40be8860..443151de90c6 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c | |||
@@ -98,14 +98,15 @@ static int uinput_request_reserve_slot(struct uinput_device *udev, | |||
98 | uinput_request_alloc_id(udev, request)); | 98 | uinput_request_alloc_id(udev, request)); |
99 | } | 99 | } |
100 | 100 | ||
101 | static void uinput_request_done(struct uinput_device *udev, | 101 | static void uinput_request_release_slot(struct uinput_device *udev, |
102 | struct uinput_request *request) | 102 | unsigned int id) |
103 | { | 103 | { |
104 | /* Mark slot as available */ | 104 | /* Mark slot as available */ |
105 | udev->requests[request->id] = NULL; | 105 | spin_lock(&udev->requests_lock); |
106 | wake_up(&udev->requests_waitq); | 106 | udev->requests[id] = NULL; |
107 | spin_unlock(&udev->requests_lock); | ||
107 | 108 | ||
108 | complete(&request->done); | 109 | wake_up(&udev->requests_waitq); |
109 | } | 110 | } |
110 | 111 | ||
111 | static int uinput_request_send(struct uinput_device *udev, | 112 | static int uinput_request_send(struct uinput_device *udev, |
@@ -138,20 +139,22 @@ static int uinput_request_send(struct uinput_device *udev, | |||
138 | static int uinput_request_submit(struct uinput_device *udev, | 139 | static int uinput_request_submit(struct uinput_device *udev, |
139 | struct uinput_request *request) | 140 | struct uinput_request *request) |
140 | { | 141 | { |
141 | int error; | 142 | int retval; |
142 | 143 | ||
143 | error = uinput_request_reserve_slot(udev, request); | 144 | retval = uinput_request_reserve_slot(udev, request); |
144 | if (error) | 145 | if (retval) |
145 | return error; | 146 | return retval; |
146 | 147 | ||
147 | error = uinput_request_send(udev, request); | 148 | retval = uinput_request_send(udev, request); |
148 | if (error) { | 149 | if (retval) |
149 | uinput_request_done(udev, request); | 150 | goto out; |
150 | return error; | ||
151 | } | ||
152 | 151 | ||
153 | wait_for_completion(&request->done); | 152 | wait_for_completion(&request->done); |
154 | return request->retval; | 153 | retval = request->retval; |
154 | |||
155 | out: | ||
156 | uinput_request_release_slot(udev, request->id); | ||
157 | return retval; | ||
155 | } | 158 | } |
156 | 159 | ||
157 | /* | 160 | /* |
@@ -169,7 +172,7 @@ static void uinput_flush_requests(struct uinput_device *udev) | |||
169 | request = udev->requests[i]; | 172 | request = udev->requests[i]; |
170 | if (request) { | 173 | if (request) { |
171 | request->retval = -ENODEV; | 174 | request->retval = -ENODEV; |
172 | uinput_request_done(udev, request); | 175 | complete(&request->done); |
173 | } | 176 | } |
174 | } | 177 | } |
175 | 178 | ||
@@ -957,7 +960,7 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, | |||
957 | } | 960 | } |
958 | 961 | ||
959 | req->retval = ff_up.retval; | 962 | req->retval = ff_up.retval; |
960 | uinput_request_done(udev, req); | 963 | complete(&req->done); |
961 | goto out; | 964 | goto out; |
962 | 965 | ||
963 | case UI_END_FF_ERASE: | 966 | case UI_END_FF_ERASE: |
@@ -973,7 +976,7 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, | |||
973 | } | 976 | } |
974 | 977 | ||
975 | req->retval = ff_erase.retval; | 978 | req->retval = ff_erase.retval; |
976 | uinput_request_done(udev, req); | 979 | complete(&req->done); |
977 | goto out; | 980 | goto out; |
978 | } | 981 | } |
979 | 982 | ||