summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>2026-11-05 14:23:45 +0100
committerBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>2026-11-10 10:18:48 +0100
commit82028344caeed2ea3536708906adee02e472633d (patch)
treea12e20b2c2ace524a17a24b3d8541fa99b3db6b3
parente20e4b8cda2b4d7dd32dbd2c0875a15d92545f04 (diff)
downloadlinux-82028344caeed2ea3536708906adee02e472633d.tar.gz
linux-82028344caeed2ea3536708906adee02e472633d.zip
gpiolib: synchronize updates of descriptor labels
desc_set_label() swaps desc->label with rcu_replace_pointer() but without any synchronization of concurrent updates. The swap is a plain read + write: two threads can observe the same old label and then call call_scru() on the same SRCU head corrupting the state of the chain. Add a spinlock to struct gpio_device - we're using a single spinlock for all descriptors as using a per-descriptor lock would unnecessarily bloat the size of the desc array with very little chance and no consequences of contention - and use it to serialize the pointer update. Cc: stable@vger.kernel.org Fixes: 1f2bcb8c8ccd ("gpio: protect the descriptor label with SRCU") Closes: https://sashiko.dev/#/bug/linux-2f7ac8b6-5034-4ff1-9afa-5700ff44e3e9 Link: https://patch.msgid.link/20261105-gpiolib-set-desc-label-corruption-v1-1-fa84726ceac0@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
-rw-r--r--drivers/gpio/gpiolib.c11
-rw-r--r--drivers/gpio/gpiolib.h2
2 files changed, 10 insertions, 3 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 6a866ac76505..4f0259a0d2f8 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -154,9 +154,12 @@ static int desc_set_label(struct gpio_desc *desc, const char *label)
memcpy(new->str, label, len);
}
- old = rcu_replace_pointer(desc->label, new, 1);
- if (old)
- call_srcu(&desc->gdev->desc_srcu, &old->rh, desc_free_label);
+ scoped_guard(spinlock, &desc->gdev->desc_lock) {
+ old = rcu_replace_pointer(desc->label, new,
+ lockdep_is_held(&desc->gdev->desc_lock));
+ if (old)
+ call_srcu(&desc->gdev->desc_srcu, &old->rh, desc_free_label);
+ }
return 0;
}
@@ -1207,6 +1210,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
goto err_free_ida;
rcu_assign_pointer(gdev->chip, gc);
+ spin_lock_init(&gdev->desc_lock);
+
ret = init_srcu_struct(&gdev->desc_srcu);
if (ret)
goto err_cleanup_gdev_srcu;
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 650a702741df..8dd8ea747f8a 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -37,6 +37,7 @@ struct fwnode_handle;
* @valid_mask: If not %NULL, holds bitmask of GPIOs which are valid to be
* used from the chip.
* @desc_srcu: ensures consistent state of GPIO descriptors exposed to users
+ * @desc_lock: synchronizes the updates to descriptor labels
* @ngpio: the number of GPIO lines on this GPIO device, equal to the size
* of the @descs array.
* @can_sleep: indicate whether the GPIO chip driver's callbacks can sleep
@@ -71,6 +72,7 @@ struct gpio_device {
struct gpio_desc *descs;
unsigned long *valid_mask;
struct srcu_struct desc_srcu;
+ spinlock_t desc_lock;
unsigned int base;
u16 ngpio;
bool can_sleep;