diff options
| author | Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com> | 2026-11-05 14:23:45 +0100 |
|---|---|---|
| committer | Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com> | 2026-11-10 10:18:48 +0100 |
| commit | 82028344caeed2ea3536708906adee02e472633d (patch) | |
| tree | a12e20b2c2ace524a17a24b3d8541fa99b3db6b3 | |
| parent | e20e4b8cda2b4d7dd32dbd2c0875a15d92545f04 (diff) | |
| download | linux-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.c | 11 | ||||
| -rw-r--r-- | drivers/gpio/gpiolib.h | 2 |
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; |
