All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/5] gpiolib: cdev: guard tidying
@ 2023-12-21  1:20 Kent Gibson
  2023-12-21  1:20 ` [PATCH v2 1/5] gpiolib: cdev: add gpio_device locking wrapper around gpio_ioctl() Kent Gibson
                   ` (5 more replies)
  0 siblings, 6 replies; 12+ messages in thread
From: Kent Gibson @ 2023-12-21  1:20 UTC (permalink / raw)
  To: linux-kernel, linux-gpio, brgl, linus.walleij, andy; +Cc: Kent Gibson

This series contains some tidying up of gpiolib-cdev following the
recent adoption of guard().

The first patch is a fix to protect gpio_ioctl() from having the
gpio chip removed while the ioctl is in progress.

The next couple of patches are minor fixes inspired by recent
submissions and reviews for gpiolib.c.

Patch 2 adds a missing include.

Patch 3 switches allocation of struct linereq from kzalloc() to
kvzalloc() as it can be larger than one page - even more so after the
recent relocation of debounce_period_us.

The final two patches replace wrapper functions with guards.

Patch 4 tidies up the functions that use a guard on the linereq
config_mutex.

Patch 5 tidies up the functions that use a guard on the gpio_device.

Changes v1 -> v2:
 - add patch 1 to protect gpio_ioctl() from chip removal
 - improve commit comment (patch 3)
 - use guard(rwsem_read) rather than rolling our own (patch 5)

Cheers,
Kent.

Kent Gibson (5):
  gpiolib: cdev: add gpio_device locking wrapper around gpio_ioctl()
  gpiolib: cdev: include overflow.h
  gpiolib: cdev: allocate linereq using kvzalloc()
  gpiolib: cdev: replace locking wrappers for config_mutex with guards
  gpiolib: cdev: replace locking wrappers for gpio_device with guards

 drivers/gpio/gpiolib-cdev.c | 257 ++++++++++--------------------------
 1 file changed, 70 insertions(+), 187 deletions(-)

--
2.39.2


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v2 1/5] gpiolib: cdev: add gpio_device locking wrapper around gpio_ioctl()
  2023-12-21  1:20 [PATCH v2 0/5] gpiolib: cdev: guard tidying Kent Gibson
@ 2023-12-21  1:20 ` Kent Gibson
  2023-12-21  9:32   ` Bartosz Golaszewski
  2023-12-21  1:20 ` [PATCH v2 2/5] gpiolib: cdev: include overflow.h Kent Gibson
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 12+ messages in thread
From: Kent Gibson @ 2023-12-21  1:20 UTC (permalink / raw)
  To: linux-kernel, linux-gpio, brgl, linus.walleij, andy; +Cc: Kent Gibson

While the GPIO cdev gpio_ioctl() call is in progress, the kernel can
call gpiochip_remove() which will set gdev->chip to NULL, after which
any subsequent access will cause a crash.

gpio_ioctl() was overlooked by the previous fix to protect syscalls
(bdbbae241a04), so add protection for that.

Fixes: bdbbae241a04 ("gpiolib: protect the GPIO device against being dropped while in use by user-space")
Fixes: d7c51b47ac11 ("gpio: userspace ABI for reading/writing GPIO lines")
Fixes: 3c0d9c635ae2 ("gpiolib: cdev: support GPIO_V2_GET_LINE_IOCTL and GPIO_V2_LINE_GET_VALUES_IOCTL")
Fixes: aad955842d1c ("gpiolib: cdev: support GPIO_V2_GET_LINEINFO_IOCTL and GPIO_V2_GET_LINEINFO_WATCH_IOCTL")
Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 drivers/gpio/gpiolib-cdev.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 744734405912..9155c54acc1e 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -2598,10 +2598,7 @@ static int lineinfo_unwatch(struct gpio_chardev_data *cdev, void __user *ip)
 	return 0;
 }
 
-/*
- * gpio_ioctl() - ioctl handler for the GPIO chardev
- */
-static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+static long gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg)
 {
 	struct gpio_chardev_data *cdev = file->private_data;
 	struct gpio_device *gdev = cdev->gdev;
@@ -2638,6 +2635,17 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	}
 }
 
+/*
+ * gpio_ioctl() - ioctl handler for the GPIO chardev
+ */
+static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct gpio_chardev_data *cdev = file->private_data;
+
+	return call_ioctl_locked(file, cmd, arg, cdev->gdev,
+				 gpio_ioctl_unlocked);
+}
+
 #ifdef CONFIG_COMPAT
 static long gpio_ioctl_compat(struct file *file, unsigned int cmd,
 			      unsigned long arg)
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v2 2/5] gpiolib: cdev: include overflow.h
  2023-12-21  1:20 [PATCH v2 0/5] gpiolib: cdev: guard tidying Kent Gibson
  2023-12-21  1:20 ` [PATCH v2 1/5] gpiolib: cdev: add gpio_device locking wrapper around gpio_ioctl() Kent Gibson
@ 2023-12-21  1:20 ` Kent Gibson
  2023-12-21  1:20 ` [PATCH v2 3/5] gpiolib: cdev: allocate linereq using kvzalloc() Kent Gibson
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 12+ messages in thread
From: Kent Gibson @ 2023-12-21  1:20 UTC (permalink / raw)
  To: linux-kernel, linux-gpio, brgl, linus.walleij, andy; +Cc: Kent Gibson

struct_size() is used to calculate struct linereq size, so explicitly
include overflow.h.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 drivers/gpio/gpiolib-cdev.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 9155c54acc1e..942fe115b726 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -20,6 +20,7 @@
 #include <linux/kfifo.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/overflow.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/poll.h>
 #include <linux/rbtree.h>
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v2 3/5] gpiolib: cdev: allocate linereq using kvzalloc()
  2023-12-21  1:20 [PATCH v2 0/5] gpiolib: cdev: guard tidying Kent Gibson
  2023-12-21  1:20 ` [PATCH v2 1/5] gpiolib: cdev: add gpio_device locking wrapper around gpio_ioctl() Kent Gibson
  2023-12-21  1:20 ` [PATCH v2 2/5] gpiolib: cdev: include overflow.h Kent Gibson
@ 2023-12-21  1:20 ` Kent Gibson
  2023-12-21  1:20 ` [PATCH v2 4/5] gpiolib: cdev: replace locking wrappers for config_mutex with guards Kent Gibson
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 12+ messages in thread
From: Kent Gibson @ 2023-12-21  1:20 UTC (permalink / raw)
  To: linux-kernel, linux-gpio, brgl, linus.walleij, andy; +Cc: Kent Gibson

The size of struct linereq may exceed a page, so allocate space for
it using kvzalloc() instead of kzalloc() to handle the case where
memory is heavily fragmented and kzalloc() cannot find a sufficient
contiguous region.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 drivers/gpio/gpiolib-cdev.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 942fe115b726..5424c878627e 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -1723,7 +1723,7 @@ static void linereq_free(struct linereq *lr)
 	kfifo_free(&lr->events);
 	kfree(lr->label);
 	gpio_device_put(lr->gdev);
-	kfree(lr);
+	kvfree(lr);
 }
 
 static int linereq_release(struct inode *inode, struct file *file)
@@ -1788,7 +1788,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
 	if (ret)
 		return ret;
 
-	lr = kzalloc(struct_size(lr, lines, ulr.num_lines), GFP_KERNEL);
+	lr = kvzalloc(struct_size(lr, lines, ulr.num_lines), GFP_KERNEL);
 	if (!lr)
 		return -ENOMEM;
 	lr->num_lines = ulr.num_lines;
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v2 4/5] gpiolib: cdev: replace locking wrappers for config_mutex with guards
  2023-12-21  1:20 [PATCH v2 0/5] gpiolib: cdev: guard tidying Kent Gibson
                   ` (2 preceding siblings ...)
  2023-12-21  1:20 ` [PATCH v2 3/5] gpiolib: cdev: allocate linereq using kvzalloc() Kent Gibson
@ 2023-12-21  1:20 ` Kent Gibson
  2023-12-21  1:20 ` [PATCH v2 5/5] gpiolib: cdev: replace locking wrappers for gpio_device " Kent Gibson
  2023-12-27 14:46 ` [PATCH v2 0/5] gpiolib: cdev: guard tidying Bartosz Golaszewski
  5 siblings, 0 replies; 12+ messages in thread
From: Kent Gibson @ 2023-12-21  1:20 UTC (permalink / raw)
  To: linux-kernel, linux-gpio, brgl, linus.walleij, andy; +Cc: Kent Gibson

After the adoption of guard(), the locking wrappers that hold the
config_mutex for linereq_set_values() and linereq_set_config() no
longer add value, so combine them into the functions they wrap.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 drivers/gpio/gpiolib-cdev.c | 63 ++++++++++++++-----------------------
 1 file changed, 24 insertions(+), 39 deletions(-)

diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 5424c878627e..9ff2b447cc20 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -1454,14 +1454,19 @@ static long linereq_get_values(struct linereq *lr, void __user *ip)
 	return 0;
 }
 
-static long linereq_set_values_unlocked(struct linereq *lr,
-					struct gpio_v2_line_values *lv)
+static long linereq_set_values(struct linereq *lr, void __user *ip)
 {
 	DECLARE_BITMAP(vals, GPIO_V2_LINES_MAX);
+	struct gpio_v2_line_values lv;
 	struct gpio_desc **descs;
 	unsigned int i, didx, num_set;
 	int ret;
 
+	if (copy_from_user(&lv, ip, sizeof(lv)))
+		return -EFAULT;
+
+	guard(mutex)(&lr->config_mutex);
+
 	/*
 	 * gpiod_set_array_value_complex() requires compacted desc and val
 	 * arrays, rather than the sparse ones in lv.
@@ -1472,12 +1477,12 @@ static long linereq_set_values_unlocked(struct linereq *lr,
 	bitmap_zero(vals, GPIO_V2_LINES_MAX);
 	/* scan requested lines to determine the subset to be set */
 	for (num_set = 0, i = 0; i < lr->num_lines; i++) {
-		if (lv->mask & BIT_ULL(i)) {
+		if (lv.mask & BIT_ULL(i)) {
 			/* setting inputs is not allowed */
 			if (!test_bit(FLAG_IS_OUT, &lr->lines[i].desc->flags))
 				return -EPERM;
 			/* add to compacted values */
-			if (lv->bits & BIT_ULL(i))
+			if (lv.bits & BIT_ULL(i))
 				__set_bit(num_set, vals);
 			num_set++;
 			/* capture desc for the num_set == 1 case */
@@ -1493,7 +1498,7 @@ static long linereq_set_values_unlocked(struct linereq *lr,
 		if (!descs)
 			return -ENOMEM;
 		for (didx = 0, i = 0; i < lr->num_lines; i++) {
-			if (lv->mask & BIT_ULL(i)) {
+			if (lv.mask & BIT_ULL(i)) {
 				descs[didx] = lr->lines[i].desc;
 				didx++;
 			}
@@ -1507,31 +1512,28 @@ static long linereq_set_values_unlocked(struct linereq *lr,
 	return ret;
 }
 
-static long linereq_set_values(struct linereq *lr, void __user *ip)
-{
-	struct gpio_v2_line_values lv;
-
-	if (copy_from_user(&lv, ip, sizeof(lv)))
-		return -EFAULT;
-
-	guard(mutex)(&lr->config_mutex);
-
-	return linereq_set_values_unlocked(lr, &lv);
-}
-
-static long linereq_set_config_unlocked(struct linereq *lr,
-					struct gpio_v2_line_config *lc)
+static long linereq_set_config(struct linereq *lr, void __user *ip)
 {
+	struct gpio_v2_line_config lc;
 	struct gpio_desc *desc;
 	struct line *line;
 	unsigned int i;
 	u64 flags, edflags;
 	int ret;
 
+	if (copy_from_user(&lc, ip, sizeof(lc)))
+		return -EFAULT;
+
+	ret = gpio_v2_line_config_validate(&lc, lr->num_lines);
+	if (ret)
+		return ret;
+
+	guard(mutex)(&lr->config_mutex);
+
 	for (i = 0; i < lr->num_lines; i++) {
 		line = &lr->lines[i];
 		desc = lr->lines[i].desc;
-		flags = gpio_v2_line_config_flags(lc, i);
+		flags = gpio_v2_line_config_flags(&lc, i);
 		gpio_v2_line_config_flags_to_desc_flags(flags, &desc->flags);
 		edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS;
 		/*
@@ -1539,7 +1541,7 @@ static long linereq_set_config_unlocked(struct linereq *lr,
 		 * or output, else the line will be treated "as is".
 		 */
 		if (flags & GPIO_V2_LINE_FLAG_OUTPUT) {
-			int val = gpio_v2_line_config_output_value(lc, i);
+			int val = gpio_v2_line_config_output_value(&lc, i);
 
 			edge_detector_stop(line);
 			ret = gpiod_direction_output(desc, val);
@@ -1550,7 +1552,7 @@ static long linereq_set_config_unlocked(struct linereq *lr,
 			if (ret)
 				return ret;
 
-			ret = edge_detector_update(line, lc, i, edflags);
+			ret = edge_detector_update(line, &lc, i, edflags);
 			if (ret)
 				return ret;
 		}
@@ -1562,23 +1564,6 @@ static long linereq_set_config_unlocked(struct linereq *lr,
 	return 0;
 }
 
-static long linereq_set_config(struct linereq *lr, void __user *ip)
-{
-	struct gpio_v2_line_config lc;
-	int ret;
-
-	if (copy_from_user(&lc, ip, sizeof(lc)))
-		return -EFAULT;
-
-	ret = gpio_v2_line_config_validate(&lc, lr->num_lines);
-	if (ret)
-		return ret;
-
-	guard(mutex)(&lr->config_mutex);
-
-	return linereq_set_config_unlocked(lr, &lc);
-}
-
 static long linereq_ioctl_unlocked(struct file *file, unsigned int cmd,
 				   unsigned long arg)
 {
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v2 5/5] gpiolib: cdev: replace locking wrappers for gpio_device with guards
  2023-12-21  1:20 [PATCH v2 0/5] gpiolib: cdev: guard tidying Kent Gibson
                   ` (3 preceding siblings ...)
  2023-12-21  1:20 ` [PATCH v2 4/5] gpiolib: cdev: replace locking wrappers for config_mutex with guards Kent Gibson
@ 2023-12-21  1:20 ` Kent Gibson
  2023-12-21 15:48   ` Andy Shevchenko
  2023-12-27 14:46 ` [PATCH v2 0/5] gpiolib: cdev: guard tidying Bartosz Golaszewski
  5 siblings, 1 reply; 12+ messages in thread
From: Kent Gibson @ 2023-12-21  1:20 UTC (permalink / raw)
  To: linux-kernel, linux-gpio, brgl, linus.walleij, andy; +Cc: Kent Gibson

Replace the wrapping functions that inhibit removal of the gpio chip
with equivalent guards.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 drivers/gpio/gpiolib-cdev.c | 205 +++++++++---------------------------
 1 file changed, 47 insertions(+), 158 deletions(-)

diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 9ff2b447cc20..2a88736629ef 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -24,6 +24,7 @@
 #include <linux/pinctrl/consumer.h>
 #include <linux/poll.h>
 #include <linux/rbtree.h>
+#include <linux/rwsem.h>
 #include <linux/seq_file.h>
 #include <linux/spinlock.h>
 #include <linux/timekeeping.h>
@@ -65,45 +66,6 @@ typedef long (*ioctl_fn)(struct file *, unsigned int, unsigned long);
 typedef ssize_t (*read_fn)(struct file *, char __user *,
 			   size_t count, loff_t *);
 
-static __poll_t call_poll_locked(struct file *file,
-				 struct poll_table_struct *wait,
-				 struct gpio_device *gdev, poll_fn func)
-{
-	__poll_t ret;
-
-	down_read(&gdev->sem);
-	ret = func(file, wait);
-	up_read(&gdev->sem);
-
-	return ret;
-}
-
-static long call_ioctl_locked(struct file *file, unsigned int cmd,
-			      unsigned long arg, struct gpio_device *gdev,
-			      ioctl_fn func)
-{
-	long ret;
-
-	down_read(&gdev->sem);
-	ret = func(file, cmd, arg);
-	up_read(&gdev->sem);
-
-	return ret;
-}
-
-static ssize_t call_read_locked(struct file *file, char __user *buf,
-				size_t count, loff_t *f_ps,
-				struct gpio_device *gdev, read_fn func)
-{
-	ssize_t ret;
-
-	down_read(&gdev->sem);
-	ret = func(file, buf, count, f_ps);
-	up_read(&gdev->sem);
-
-	return ret;
-}
-
 /*
  * GPIO line handle management
  */
@@ -238,8 +200,8 @@ static long linehandle_set_config(struct linehandle_state *lh,
 	return 0;
 }
 
-static long linehandle_ioctl_unlocked(struct file *file, unsigned int cmd,
-				      unsigned long arg)
+static long linehandle_ioctl(struct file *file, unsigned int cmd,
+			     unsigned long arg)
 {
 	struct linehandle_state *lh = file->private_data;
 	void __user *ip = (void __user *)arg;
@@ -248,6 +210,8 @@ static long linehandle_ioctl_unlocked(struct file *file, unsigned int cmd,
 	unsigned int i;
 	int ret;
 
+	guard(rwsem_read)(&lh->gdev->sem);
+
 	if (!lh->gdev->chip)
 		return -ENODEV;
 
@@ -297,15 +261,6 @@ static long linehandle_ioctl_unlocked(struct file *file, unsigned int cmd,
 	}
 }
 
-static long linehandle_ioctl(struct file *file, unsigned int cmd,
-			     unsigned long arg)
-{
-	struct linehandle_state *lh = file->private_data;
-
-	return call_ioctl_locked(file, cmd, arg, lh->gdev,
-				 linehandle_ioctl_unlocked);
-}
-
 #ifdef CONFIG_COMPAT
 static long linehandle_ioctl_compat(struct file *file, unsigned int cmd,
 				    unsigned long arg)
@@ -1564,12 +1519,14 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
 	return 0;
 }
 
-static long linereq_ioctl_unlocked(struct file *file, unsigned int cmd,
-				   unsigned long arg)
+static long linereq_ioctl(struct file *file, unsigned int cmd,
+			  unsigned long arg)
 {
 	struct linereq *lr = file->private_data;
 	void __user *ip = (void __user *)arg;
 
+	guard(rwsem_read)(&lr->gdev->sem);
+
 	if (!lr->gdev->chip)
 		return -ENODEV;
 
@@ -1585,15 +1542,6 @@ static long linereq_ioctl_unlocked(struct file *file, unsigned int cmd,
 	}
 }
 
-static long linereq_ioctl(struct file *file, unsigned int cmd,
-			  unsigned long arg)
-{
-	struct linereq *lr = file->private_data;
-
-	return call_ioctl_locked(file, cmd, arg, lr->gdev,
-				 linereq_ioctl_unlocked);
-}
-
 #ifdef CONFIG_COMPAT
 static long linereq_ioctl_compat(struct file *file, unsigned int cmd,
 				 unsigned long arg)
@@ -1602,12 +1550,14 @@ static long linereq_ioctl_compat(struct file *file, unsigned int cmd,
 }
 #endif
 
-static __poll_t linereq_poll_unlocked(struct file *file,
-				      struct poll_table_struct *wait)
+static __poll_t linereq_poll(struct file *file,
+			     struct poll_table_struct *wait)
 {
 	struct linereq *lr = file->private_data;
 	__poll_t events = 0;
 
+	guard(rwsem_read)(&lr->gdev->sem);
+
 	if (!lr->gdev->chip)
 		return EPOLLHUP | EPOLLERR;
 
@@ -1620,22 +1570,16 @@ static __poll_t linereq_poll_unlocked(struct file *file,
 	return events;
 }
 
-static __poll_t linereq_poll(struct file *file,
-			     struct poll_table_struct *wait)
-{
-	struct linereq *lr = file->private_data;
-
-	return call_poll_locked(file, wait, lr->gdev, linereq_poll_unlocked);
-}
-
-static ssize_t linereq_read_unlocked(struct file *file, char __user *buf,
-				     size_t count, loff_t *f_ps)
+static ssize_t linereq_read(struct file *file, char __user *buf,
+			    size_t count, loff_t *f_ps)
 {
 	struct linereq *lr = file->private_data;
 	struct gpio_v2_line_event le;
 	ssize_t bytes_read = 0;
 	int ret;
 
+	guard(rwsem_read)(&lr->gdev->sem);
+
 	if (!lr->gdev->chip)
 		return -ENODEV;
 
@@ -1677,15 +1621,6 @@ static ssize_t linereq_read_unlocked(struct file *file, char __user *buf,
 	return bytes_read;
 }
 
-static ssize_t linereq_read(struct file *file, char __user *buf,
-			    size_t count, loff_t *f_ps)
-{
-	struct linereq *lr = file->private_data;
-
-	return call_read_locked(file, buf, count, f_ps, lr->gdev,
-				linereq_read_unlocked);
-}
-
 static void linereq_free(struct linereq *lr)
 {
 	struct line *line;
@@ -1938,12 +1873,14 @@ struct lineevent_state {
 	(GPIOEVENT_REQUEST_RISING_EDGE | \
 	GPIOEVENT_REQUEST_FALLING_EDGE)
 
-static __poll_t lineevent_poll_unlocked(struct file *file,
-					struct poll_table_struct *wait)
+static __poll_t lineevent_poll(struct file *file,
+			       struct poll_table_struct *wait)
 {
 	struct lineevent_state *le = file->private_data;
 	__poll_t events = 0;
 
+	guard(rwsem_read)(&le->gdev->sem);
+
 	if (!le->gdev->chip)
 		return EPOLLHUP | EPOLLERR;
 
@@ -1955,14 +1892,6 @@ static __poll_t lineevent_poll_unlocked(struct file *file,
 	return events;
 }
 
-static __poll_t lineevent_poll(struct file *file,
-			       struct poll_table_struct *wait)
-{
-	struct lineevent_state *le = file->private_data;
-
-	return call_poll_locked(file, wait, le->gdev, lineevent_poll_unlocked);
-}
-
 static int lineevent_unregistered_notify(struct notifier_block *nb,
 					 unsigned long action, void *data)
 {
@@ -1979,8 +1908,8 @@ struct compat_gpioeevent_data {
 	u32		id;
 };
 
-static ssize_t lineevent_read_unlocked(struct file *file, char __user *buf,
-				       size_t count, loff_t *f_ps)
+static ssize_t lineevent_read(struct file *file, char __user *buf,
+			      size_t count, loff_t *f_ps)
 {
 	struct lineevent_state *le = file->private_data;
 	struct gpioevent_data ge;
@@ -1988,6 +1917,8 @@ static ssize_t lineevent_read_unlocked(struct file *file, char __user *buf,
 	ssize_t ge_size;
 	int ret;
 
+	guard(rwsem_read)(&le->gdev->sem);
+
 	if (!le->gdev->chip)
 		return -ENODEV;
 
@@ -2042,15 +1973,6 @@ static ssize_t lineevent_read_unlocked(struct file *file, char __user *buf,
 	return bytes_read;
 }
 
-static ssize_t lineevent_read(struct file *file, char __user *buf,
-			      size_t count, loff_t *f_ps)
-{
-	struct lineevent_state *le = file->private_data;
-
-	return call_read_locked(file, buf, count, f_ps, le->gdev,
-				lineevent_read_unlocked);
-}
-
 static void lineevent_free(struct lineevent_state *le)
 {
 	if (le->device_unregistered_nb.notifier_call)
@@ -2071,13 +1993,15 @@ static int lineevent_release(struct inode *inode, struct file *file)
 	return 0;
 }
 
-static long lineevent_ioctl_unlocked(struct file *file, unsigned int cmd,
-				     unsigned long arg)
+static long lineevent_ioctl(struct file *file, unsigned int cmd,
+			    unsigned long arg)
 {
 	struct lineevent_state *le = file->private_data;
 	void __user *ip = (void __user *)arg;
 	struct gpiohandle_data ghd;
 
+	guard(rwsem_read)(&le->gdev->sem);
+
 	if (!le->gdev->chip)
 		return -ENODEV;
 
@@ -2103,15 +2027,6 @@ static long lineevent_ioctl_unlocked(struct file *file, unsigned int cmd,
 	return -EINVAL;
 }
 
-static long lineevent_ioctl(struct file *file, unsigned int cmd,
-			    unsigned long arg)
-{
-	struct lineevent_state *le = file->private_data;
-
-	return call_ioctl_locked(file, cmd, arg, le->gdev,
-				 lineevent_ioctl_unlocked);
-}
-
 #ifdef CONFIG_COMPAT
 static long lineevent_ioctl_compat(struct file *file, unsigned int cmd,
 				   unsigned long arg)
@@ -2584,12 +2499,17 @@ static int lineinfo_unwatch(struct gpio_chardev_data *cdev, void __user *ip)
 	return 0;
 }
 
-static long gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg)
+/*
+ * gpio_ioctl() - ioctl handler for the GPIO chardev
+ */
+static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
 	struct gpio_chardev_data *cdev = file->private_data;
 	struct gpio_device *gdev = cdev->gdev;
 	void __user *ip = (void __user *)arg;
 
+	guard(rwsem_read)(&gdev->sem);
+
 	/* We fail any subsequent ioctl():s when the chip is gone */
 	if (!gdev->chip)
 		return -ENODEV;
@@ -2621,17 +2541,6 @@ static long gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned lo
 	}
 }
 
-/*
- * gpio_ioctl() - ioctl handler for the GPIO chardev
- */
-static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-	struct gpio_chardev_data *cdev = file->private_data;
-
-	return call_ioctl_locked(file, cmd, arg, cdev->gdev,
-				 gpio_ioctl_unlocked);
-}
-
 #ifdef CONFIG_COMPAT
 static long gpio_ioctl_compat(struct file *file, unsigned int cmd,
 			      unsigned long arg)
@@ -2679,12 +2588,14 @@ static int gpio_device_unregistered_notify(struct notifier_block *nb,
 	return NOTIFY_OK;
 }
 
-static __poll_t lineinfo_watch_poll_unlocked(struct file *file,
-					     struct poll_table_struct *pollt)
+static __poll_t lineinfo_watch_poll(struct file *file,
+				    struct poll_table_struct *pollt)
 {
 	struct gpio_chardev_data *cdev = file->private_data;
 	__poll_t events = 0;
 
+	guard(rwsem_read)(&cdev->gdev->sem);
+
 	if (!cdev->gdev->chip)
 		return EPOLLHUP | EPOLLERR;
 
@@ -2697,17 +2608,8 @@ static __poll_t lineinfo_watch_poll_unlocked(struct file *file,
 	return events;
 }
 
-static __poll_t lineinfo_watch_poll(struct file *file,
-				    struct poll_table_struct *pollt)
-{
-	struct gpio_chardev_data *cdev = file->private_data;
-
-	return call_poll_locked(file, pollt, cdev->gdev,
-				lineinfo_watch_poll_unlocked);
-}
-
-static ssize_t lineinfo_watch_read_unlocked(struct file *file, char __user *buf,
-					    size_t count, loff_t *off)
+static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
+				   size_t count, loff_t *off)
 {
 	struct gpio_chardev_data *cdev = file->private_data;
 	struct gpio_v2_line_info_changed event;
@@ -2715,6 +2617,8 @@ static ssize_t lineinfo_watch_read_unlocked(struct file *file, char __user *buf,
 	int ret;
 	size_t event_size;
 
+	guard(rwsem_read)(&cdev->gdev->sem);
+
 	if (!cdev->gdev->chip)
 		return -ENODEV;
 
@@ -2777,15 +2681,6 @@ static ssize_t lineinfo_watch_read_unlocked(struct file *file, char __user *buf,
 	return bytes_read;
 }
 
-static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
-				   size_t count, loff_t *off)
-{
-	struct gpio_chardev_data *cdev = file->private_data;
-
-	return call_read_locked(file, buf, count, off, cdev->gdev,
-				lineinfo_watch_read_unlocked);
-}
-
 /**
  * gpio_chrdev_open() - open the chardev for ioctl operations
  * @inode: inode for this chardev
@@ -2799,17 +2694,15 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
 	struct gpio_chardev_data *cdev;
 	int ret = -ENOMEM;
 
-	down_read(&gdev->sem);
+	guard(rwsem_read)(&gdev->sem);
 
 	/* Fail on open if the backing gpiochip is gone */
-	if (!gdev->chip) {
-		ret = -ENODEV;
-		goto out_unlock;
-	}
+	if (!gdev->chip)
+		return -ENODEV;
 
 	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
 	if (!cdev)
-		goto out_unlock;
+		return -ENODEV;
 
 	cdev->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL);
 	if (!cdev->watched_lines)
@@ -2838,8 +2731,6 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
 	if (ret)
 		goto out_unregister_device_notifier;
 
-	up_read(&gdev->sem);
-
 	return ret;
 
 out_unregister_device_notifier:
@@ -2853,8 +2744,6 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
 	bitmap_free(cdev->watched_lines);
 out_free_cdev:
 	kfree(cdev);
-out_unlock:
-	up_read(&gdev->sem);
 	return ret;
 }
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 1/5] gpiolib: cdev: add gpio_device locking wrapper around gpio_ioctl()
  2023-12-21  1:20 ` [PATCH v2 1/5] gpiolib: cdev: add gpio_device locking wrapper around gpio_ioctl() Kent Gibson
@ 2023-12-21  9:32   ` Bartosz Golaszewski
  0 siblings, 0 replies; 12+ messages in thread
From: Bartosz Golaszewski @ 2023-12-21  9:32 UTC (permalink / raw)
  To: Kent Gibson; +Cc: linux-kernel, linux-gpio, linus.walleij, andy

On Thu, Dec 21, 2023 at 2:21 AM Kent Gibson <warthog618@gmail.com> wrote:
>
> While the GPIO cdev gpio_ioctl() call is in progress, the kernel can
> call gpiochip_remove() which will set gdev->chip to NULL, after which
> any subsequent access will cause a crash.
>
> gpio_ioctl() was overlooked by the previous fix to protect syscalls
> (bdbbae241a04), so add protection for that.
>
> Fixes: bdbbae241a04 ("gpiolib: protect the GPIO device against being dropped while in use by user-space")
> Fixes: d7c51b47ac11 ("gpio: userspace ABI for reading/writing GPIO lines")
> Fixes: 3c0d9c635ae2 ("gpiolib: cdev: support GPIO_V2_GET_LINE_IOCTL and GPIO_V2_LINE_GET_VALUES_IOCTL")
> Fixes: aad955842d1c ("gpiolib: cdev: support GPIO_V2_GET_LINEINFO_IOCTL and GPIO_V2_GET_LINEINFO_WATCH_IOCTL")
> Signed-off-by: Kent Gibson <warthog618@gmail.com>
> ---
>  drivers/gpio/gpiolib-cdev.c | 16 ++++++++++++----
>  1 file changed, 12 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
> index 744734405912..9155c54acc1e 100644
> --- a/drivers/gpio/gpiolib-cdev.c
> +++ b/drivers/gpio/gpiolib-cdev.c
> @@ -2598,10 +2598,7 @@ static int lineinfo_unwatch(struct gpio_chardev_data *cdev, void __user *ip)
>         return 0;
>  }
>
> -/*
> - * gpio_ioctl() - ioctl handler for the GPIO chardev
> - */
> -static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> +static long gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg)
>  {
>         struct gpio_chardev_data *cdev = file->private_data;
>         struct gpio_device *gdev = cdev->gdev;
> @@ -2638,6 +2635,17 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>         }
>  }
>
> +/*
> + * gpio_ioctl() - ioctl handler for the GPIO chardev
> + */
> +static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> +{
> +       struct gpio_chardev_data *cdev = file->private_data;
> +
> +       return call_ioctl_locked(file, cmd, arg, cdev->gdev,
> +                                gpio_ioctl_unlocked);
> +}
> +
>  #ifdef CONFIG_COMPAT
>  static long gpio_ioctl_compat(struct file *file, unsigned int cmd,
>                               unsigned long arg)
> --
> 2.39.2
>

I applied this. I'll send it upstream tomorrow and once it's in
master, I'll pick up the rest on Monday.

Bart

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 5/5] gpiolib: cdev: replace locking wrappers for gpio_device with guards
  2023-12-21  1:20 ` [PATCH v2 5/5] gpiolib: cdev: replace locking wrappers for gpio_device " Kent Gibson
@ 2023-12-21 15:48   ` Andy Shevchenko
  2023-12-22  0:47     ` Kent Gibson
  0 siblings, 1 reply; 12+ messages in thread
From: Andy Shevchenko @ 2023-12-21 15:48 UTC (permalink / raw)
  To: Kent Gibson; +Cc: linux-kernel, linux-gpio, brgl, linus.walleij

On Thu, Dec 21, 2023 at 09:20:40AM +0800, Kent Gibson wrote:
> Replace the wrapping functions that inhibit removal of the gpio chip

GPIO

> with equivalent guards.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 5/5] gpiolib: cdev: replace locking wrappers for gpio_device with guards
  2023-12-21 15:48   ` Andy Shevchenko
@ 2023-12-22  0:47     ` Kent Gibson
  2023-12-22 14:05       ` Bartosz Golaszewski
  0 siblings, 1 reply; 12+ messages in thread
From: Kent Gibson @ 2023-12-22  0:47 UTC (permalink / raw)
  To: brgl; +Cc: andy, linux-kernel, linux-gpio, linus.walleij

On Thu, Dec 21, 2023 at 05:48:52PM +0200, Andy Shevchenko wrote:
> On Thu, Dec 21, 2023 at 09:20:40AM +0800, Kent Gibson wrote:
> > Replace the wrapping functions that inhibit removal of the gpio chip
>
> GPIO
>

Bart, I don't care either way and not enough to respin a v3.
If it bothers you could you fix it on the way in?

That is if you aren't too busy reversing xmas trees ;-).

Thanks,
Kent.


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 5/5] gpiolib: cdev: replace locking wrappers for gpio_device with guards
  2023-12-22  0:47     ` Kent Gibson
@ 2023-12-22 14:05       ` Bartosz Golaszewski
  2023-12-22 14:12         ` Kent Gibson
  0 siblings, 1 reply; 12+ messages in thread
From: Bartosz Golaszewski @ 2023-12-22 14:05 UTC (permalink / raw)
  To: Kent Gibson; +Cc: andy, linux-kernel, linux-gpio, linus.walleij

On Fri, Dec 22, 2023 at 1:47 AM Kent Gibson <warthog618@gmail.com> wrote:
>
> On Thu, Dec 21, 2023 at 05:48:52PM +0200, Andy Shevchenko wrote:
> > On Thu, Dec 21, 2023 at 09:20:40AM +0800, Kent Gibson wrote:
> > > Replace the wrapping functions that inhibit removal of the gpio chip
> >
> > GPIO
> >
>
> Bart, I don't care either way and not enough to respin a v3.
> If it bothers you could you fix it on the way in?
>

Sure!

> That is if you aren't too busy reversing xmas trees ;-).
>

Joke's on you, I actually do find them easier to read and try to use
them everywhere in new code I write. :)

Bart

> Thanks,
> Kent.
>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 5/5] gpiolib: cdev: replace locking wrappers for gpio_device with guards
  2023-12-22 14:05       ` Bartosz Golaszewski
@ 2023-12-22 14:12         ` Kent Gibson
  0 siblings, 0 replies; 12+ messages in thread
From: Kent Gibson @ 2023-12-22 14:12 UTC (permalink / raw)
  To: Bartosz Golaszewski; +Cc: andy, linux-kernel, linux-gpio, linus.walleij

On Fri, Dec 22, 2023 at 03:05:41PM +0100, Bartosz Golaszewski wrote:
> On Fri, Dec 22, 2023 at 1:47 AM Kent Gibson <warthog618@gmail.com> wrote:
> >
> > On Thu, Dec 21, 2023 at 05:48:52PM +0200, Andy Shevchenko wrote:
> > > On Thu, Dec 21, 2023 at 09:20:40AM +0800, Kent Gibson wrote:
> > > > Replace the wrapping functions that inhibit removal of the gpio chip
> > >
> > > GPIO
> > >
> >
> > Bart, I don't care either way and not enough to respin a v3.
> > If it bothers you could you fix it on the way in?
> >
>
> Sure!
>
> > That is if you aren't too busy reversing xmas trees ;-).
> >
>
> Joke's on you, I actually do find them easier to read and try to use
> them everywhere in new code I write. :)
>

Oh, I agree - it is more readable.  It just seems very timely that Andy
keeps griping about them not being inverted.

Cheers,
Kent.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 0/5] gpiolib: cdev: guard tidying
  2023-12-21  1:20 [PATCH v2 0/5] gpiolib: cdev: guard tidying Kent Gibson
                   ` (4 preceding siblings ...)
  2023-12-21  1:20 ` [PATCH v2 5/5] gpiolib: cdev: replace locking wrappers for gpio_device " Kent Gibson
@ 2023-12-27 14:46 ` Bartosz Golaszewski
  5 siblings, 0 replies; 12+ messages in thread
From: Bartosz Golaszewski @ 2023-12-27 14:46 UTC (permalink / raw)
  To: Kent Gibson; +Cc: linux-kernel, linux-gpio, linus.walleij, andy

On Thu, Dec 21, 2023 at 2:20 AM Kent Gibson <warthog618@gmail.com> wrote:
>
> This series contains some tidying up of gpiolib-cdev following the
> recent adoption of guard().
>
> The first patch is a fix to protect gpio_ioctl() from having the
> gpio chip removed while the ioctl is in progress.
>
> The next couple of patches are minor fixes inspired by recent
> submissions and reviews for gpiolib.c.
>
> Patch 2 adds a missing include.
>
> Patch 3 switches allocation of struct linereq from kzalloc() to
> kvzalloc() as it can be larger than one page - even more so after the
> recent relocation of debounce_period_us.
>
> The final two patches replace wrapper functions with guards.
>
> Patch 4 tidies up the functions that use a guard on the linereq
> config_mutex.
>
> Patch 5 tidies up the functions that use a guard on the gpio_device.
>
> Changes v1 -> v2:
>  - add patch 1 to protect gpio_ioctl() from chip removal
>  - improve commit comment (patch 3)
>  - use guard(rwsem_read) rather than rolling our own (patch 5)
>
> Cheers,
> Kent.
>
> Kent Gibson (5):
>   gpiolib: cdev: add gpio_device locking wrapper around gpio_ioctl()
>   gpiolib: cdev: include overflow.h
>   gpiolib: cdev: allocate linereq using kvzalloc()
>   gpiolib: cdev: replace locking wrappers for config_mutex with guards
>   gpiolib: cdev: replace locking wrappers for gpio_device with guards
>
>  drivers/gpio/gpiolib-cdev.c | 257 ++++++++++--------------------------
>  1 file changed, 70 insertions(+), 187 deletions(-)
>
> --
> 2.39.2
>

I applied the remaining patches, thanks.

Bart

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2023-12-27 14:46 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-12-21  1:20 [PATCH v2 0/5] gpiolib: cdev: guard tidying Kent Gibson
2023-12-21  1:20 ` [PATCH v2 1/5] gpiolib: cdev: add gpio_device locking wrapper around gpio_ioctl() Kent Gibson
2023-12-21  9:32   ` Bartosz Golaszewski
2023-12-21  1:20 ` [PATCH v2 2/5] gpiolib: cdev: include overflow.h Kent Gibson
2023-12-21  1:20 ` [PATCH v2 3/5] gpiolib: cdev: allocate linereq using kvzalloc() Kent Gibson
2023-12-21  1:20 ` [PATCH v2 4/5] gpiolib: cdev: replace locking wrappers for config_mutex with guards Kent Gibson
2023-12-21  1:20 ` [PATCH v2 5/5] gpiolib: cdev: replace locking wrappers for gpio_device " Kent Gibson
2023-12-21 15:48   ` Andy Shevchenko
2023-12-22  0:47     ` Kent Gibson
2023-12-22 14:05       ` Bartosz Golaszewski
2023-12-22 14:12         ` Kent Gibson
2023-12-27 14:46 ` [PATCH v2 0/5] gpiolib: cdev: guard tidying Bartosz Golaszewski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.