On Tue, May 23, 2023 at 05:51:01PM +0200, Bartosz Golaszewski wrote: > When a GPIO device is forcefully unregistered, we are left with an > inactive object. If user-space kept an open file descriptor to a line > request associated with such a structure, upon closing it, we'll see the > kernel crash due to freeing unexistent GPIO descriptors. > nonexistent But I'm not sure that works - gpiod_free() is null aware, so strictly speaking "freeing nonexistent GPIO descriptors" isn't the problem. You mean orphaned GPIO descriptors? > Fix it by checking if chip is still alive before calling gpiod_free() in > release callbacks for both v2 and v1 ABI. > > Fixes: 3c0d9c635ae2 ("gpiolib: cdev: support GPIO_V2_GET_LINE_IOCTL and GPIO_V2_LINE_GET_VALUES_IOCTL") The problem is also in v1, so do we want to consider backporting a fix for that too? > Reported-by: Kent Gibson <warthog618@xxxxxxxxx> > Signed-off-by: Bartosz Golaszewski <brgl@xxxxxxxx> > --- > drivers/gpio/gpiolib-cdev.c | 22 ++++++++++++++++------ > 1 file changed, 16 insertions(+), 6 deletions(-) > > diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c > index 0a33971c964c..6830f668a1b0 100644 > --- a/drivers/gpio/gpiolib-cdev.c > +++ b/drivers/gpio/gpiolib-cdev.c > @@ -315,13 +315,19 @@ static long linehandle_ioctl_compat(struct file *file, unsigned int cmd, > > static void linehandle_free(struct linehandle_state *lh) > { > + struct gpio_device *gdev = lh->gdev; It isn't clear to me what this is for. The flow below now calls gpiod_free() less often, so not that. It is there for the normal case?? > int i; > > - for (i = 0; i < lh->num_descs; i++) > - if (lh->descs[i]) > - gpiod_free(lh->descs[i]); > + for (i = 0; i < lh->num_descs; i++) { > + if (lh->descs[i]) { > + down_write(&gdev->sem); > + if (gdev->chip) > + gpiod_free(lh->descs[i]); > + up_write(&gdev->sem); > + } > + } > kfree(lh->label); > - gpio_device_put(lh->gdev); > + gpio_device_put(gdev); > kfree(lh); > } > lineevent_free() needs the fix too? > @@ -1565,17 +1571,21 @@ static ssize_t linereq_read(struct file *file, char __user *buf, > > static void linereq_free(struct linereq *lr) > { > + struct gpio_device *gdev = lr->gdev; > unsigned int i; > > for (i = 0; i < lr->num_lines; i++) { > if (lr->lines[i].desc) { > edge_detector_stop(&lr->lines[i]); > - gpiod_free(lr->lines[i].desc); > + down_write(&gdev->sem); > + if (gdev->chip) > + gpiod_free(lr->lines[i].desc); > + up_write(&gdev->sem); > } > } > kfifo_free(&lr->events); > kfree(lr->label); > - gpio_device_put(lr->gdev); > + gpio_device_put(gdev); > kfree(lr); > } > TBH the fact you have to mess with sems here indicates to me the problem lies in gpiolib itself. As a gpiolib client, cdev should just be able to release the desc back to gpiolib and have it cleanup the mess. Not that I ever got my head around the whole gpiolib object lifecycle here - for v2 I just followed what v1 did. Also, gpiolib still reports an error when forceably removing chips that have open requests: dev_crit(&gdev->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n"); Any other gpiolib clients out there that this might impact? Else why report that crit error if you expect it is dealt with? So while this may fix the crash, I can't say I'm happy with it. Cheers, Kent.