Skip to content

Commit 533aae7

Browse files
author
Bartosz Golaszewski
committed
gpiolib: cdev: fix NULL-pointer dereferences
There are several places where we can crash the kernel by requesting lines, unbinding the GPIO device, then calling any of the system calls relevant to the GPIO character device's annonymous file descriptors: ioctl(), read(), poll(). While I observed it with the GPIO simulator, it will also happen for any of the GPIO devices that can be hot-unplugged - for instance any HID GPIO expander (e.g. CP2112). This affects both v1 and v2 uAPI. This fixes it partially by checking if gdev->chip is not NULL but it doesn't entirely remedy the situation as we still have a race condition in which another thread can remove the device after the check. Fixes: d7c51b4 ("gpio: userspace ABI for reading/writing GPIO lines") Fixes: 3c0d9c6 ("gpiolib: cdev: support GPIO_V2_GET_LINE_IOCTL and GPIO_V2_LINE_GET_VALUES_IOCTL") Fixes: aad9558 ("gpiolib: cdev: support GPIO_V2_GET_LINEINFO_IOCTL and GPIO_V2_GET_LINEINFO_WATCH_IOCTL") Fixes: a54756c ("gpiolib: cdev: support GPIO_V2_LINE_SET_CONFIG_IOCTL") Fixes: 7b8e00d ("gpiolib: cdev: support GPIO_V2_LINE_SET_VALUES_IOCTL") Signed-off-by: Bartosz Golaszewski <[email protected]> Reviewed-by: Andy Shevchenko <[email protected]> Reviewed-by: Linus Walleij <[email protected]>
1 parent 3b7c747 commit 533aae7

File tree

1 file changed

+27
-0
lines changed

1 file changed

+27
-0
lines changed

drivers/gpio/gpiolib-cdev.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ static long linehandle_ioctl(struct file *file, unsigned int cmd,
201201
unsigned int i;
202202
int ret;
203203

204+
if (!lh->gdev->chip)
205+
return -ENODEV;
206+
204207
switch (cmd) {
205208
case GPIOHANDLE_GET_LINE_VALUES_IOCTL:
206209
/* NOTE: It's okay to read values of output lines */
@@ -1384,6 +1387,9 @@ static long linereq_ioctl(struct file *file, unsigned int cmd,
13841387
struct linereq *lr = file->private_data;
13851388
void __user *ip = (void __user *)arg;
13861389

1390+
if (!lr->gdev->chip)
1391+
return -ENODEV;
1392+
13871393
switch (cmd) {
13881394
case GPIO_V2_LINE_GET_VALUES_IOCTL:
13891395
return linereq_get_values(lr, ip);
@@ -1410,6 +1416,9 @@ static __poll_t linereq_poll(struct file *file,
14101416
struct linereq *lr = file->private_data;
14111417
__poll_t events = 0;
14121418

1419+
if (!lr->gdev->chip)
1420+
return EPOLLHUP | EPOLLERR;
1421+
14131422
poll_wait(file, &lr->wait, wait);
14141423

14151424
if (!kfifo_is_empty_spinlocked_noirqsave(&lr->events,
@@ -1429,6 +1438,9 @@ static ssize_t linereq_read(struct file *file,
14291438
ssize_t bytes_read = 0;
14301439
int ret;
14311440

1441+
if (!lr->gdev->chip)
1442+
return -ENODEV;
1443+
14321444
if (count < sizeof(le))
14331445
return -EINVAL;
14341446

@@ -1716,6 +1728,9 @@ static __poll_t lineevent_poll(struct file *file,
17161728
struct lineevent_state *le = file->private_data;
17171729
__poll_t events = 0;
17181730

1731+
if (!le->gdev->chip)
1732+
return EPOLLHUP | EPOLLERR;
1733+
17191734
poll_wait(file, &le->wait, wait);
17201735

17211736
if (!kfifo_is_empty_spinlocked_noirqsave(&le->events, &le->wait.lock))
@@ -1740,6 +1755,9 @@ static ssize_t lineevent_read(struct file *file,
17401755
ssize_t ge_size;
17411756
int ret;
17421757

1758+
if (!le->gdev->chip)
1759+
return -ENODEV;
1760+
17431761
/*
17441762
* When compatible system call is being used the struct gpioevent_data,
17451763
* in case of at least ia32, has different size due to the alignment
@@ -1821,6 +1839,9 @@ static long lineevent_ioctl(struct file *file, unsigned int cmd,
18211839
void __user *ip = (void __user *)arg;
18221840
struct gpiohandle_data ghd;
18231841

1842+
if (!le->gdev->chip)
1843+
return -ENODEV;
1844+
18241845
/*
18251846
* We can get the value for an event line but not set it,
18261847
* because it is input by definition.
@@ -2407,6 +2428,9 @@ static __poll_t lineinfo_watch_poll(struct file *file,
24072428
struct gpio_chardev_data *cdev = file->private_data;
24082429
__poll_t events = 0;
24092430

2431+
if (!cdev->gdev->chip)
2432+
return EPOLLHUP | EPOLLERR;
2433+
24102434
poll_wait(file, &cdev->wait, pollt);
24112435

24122436
if (!kfifo_is_empty_spinlocked_noirqsave(&cdev->events,
@@ -2425,6 +2449,9 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
24252449
int ret;
24262450
size_t event_size;
24272451

2452+
if (!cdev->gdev->chip)
2453+
return -ENODEV;
2454+
24282455
#ifndef CONFIG_GPIO_CDEV_V1
24292456
event_size = sizeof(struct gpio_v2_line_info_changed);
24302457
if (count < event_size)

0 commit comments

Comments
 (0)