[PATCH 1/5] gpiolib: fix GPIO descriptor leak in gpiod_get_array()
From: Dmitry Torokhov
Date: Wed Oct 21 2026 - 15:02:23 EST
In gpiod_get_array(), descs->ndescs is used as the loop induction
variable and is only incremented at the end of each iteration. When
krealloc() fails on the first iteration (when index is 0), descs->desc[0]
has already been acquired via gpiod_get_index(), but descs->ndescs is
still 0. Consequently, gpiod_put_array() frees descs without releasing
descs->desc[0], leaking the descriptor.
Fix this by using a dedicated loop induction variable and incrementing
descs->ndescs immediately upon storing the retrieved descriptor.
Additionally, since the fast-path array_info setup only executes for
the first element (index 0), simplify the bitmap_set() calls to start at
bit 0 for count bits instead of calculating offsets from the loop index.
Fixes: bf9346f5d47b ("gpiolib: Identify arrays matching GPIO hardware")
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@xxxxxxxxx>
---
drivers/gpio/gpiolib.c | 36 +++++++++++++++---------------------
1 file changed, 15 insertions(+), 21 deletions(-)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index c0394c7d6279..21c9cea71c68 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -5193,7 +5193,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
struct gpio_descs *descs;
struct gpio_device *gdev;
struct gpio_array *array_info = NULL;
- int count, bitmap_size;
+ int i, count, bitmap_size;
unsigned long dflags;
size_t descs_size;
@@ -5206,21 +5206,21 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
if (!descs)
return ERR_PTR(-ENOMEM);
- for (descs->ndescs = 0; descs->ndescs < count; descs->ndescs++) {
- desc = gpiod_get_index(dev, con_id, descs->ndescs, flags);
+ for (i = 0; i < count; i++) {
+ desc = gpiod_get_index(dev, con_id, i, flags);
if (IS_ERR(desc)) {
gpiod_put_array(descs);
return ERR_CAST(desc);
}
- descs->desc[descs->ndescs] = desc;
+ descs->desc[descs->ndescs++] = desc;
gdev = gpiod_to_gpio_device(desc);
/*
* If pin hardware number of array member 0 is also 0, select
* its chip as a candidate for fast bitmap processing path.
*/
- if (descs->ndescs == 0 && gpiod_hwgpio(desc) == 0) {
+ if (i == 0 && gpiod_hwgpio(desc) == 0) {
struct gpio_descs *array;
bitmap_size = BITS_TO_LONGS(gdev->ngpio > count ?
@@ -5245,10 +5245,8 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
array_info->desc = descs->desc;
array_info->size = count;
array_info->gdev = gdev;
- bitmap_set(array_info->get_mask, descs->ndescs,
- count - descs->ndescs);
- bitmap_set(array_info->set_mask, descs->ndescs,
- count - descs->ndescs);
+ bitmap_set(array_info->get_mask, 0, count);
+ bitmap_set(array_info->set_mask, 0, count);
descs->info = array_info;
}
@@ -5258,38 +5256,34 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
/* Unmark array members which don't belong to the 'fast' chip */
if (array_info->gdev != gdev) {
- __clear_bit(descs->ndescs, array_info->get_mask);
- __clear_bit(descs->ndescs, array_info->set_mask);
+ __clear_bit(i, array_info->get_mask);
+ __clear_bit(i, array_info->set_mask);
}
/*
* Detect array members which belong to the 'fast' chip
* but their pins are not in hardware order.
*/
- else if (gpiod_hwgpio(desc) != descs->ndescs) {
+ else if (gpiod_hwgpio(desc) != i) {
/*
* Don't use fast path if all array members processed so
* far belong to the same chip as this one but its pin
* hardware number is different from its array index.
*/
- if (bitmap_full(array_info->get_mask, descs->ndescs)) {
+ if (bitmap_full(array_info->get_mask, i)) {
array_info = NULL;
} else {
- __clear_bit(descs->ndescs,
- array_info->get_mask);
- __clear_bit(descs->ndescs,
- array_info->set_mask);
+ __clear_bit(i, array_info->get_mask);
+ __clear_bit(i, array_info->set_mask);
}
} else {
dflags = READ_ONCE(desc->flags);
/* Exclude open drain or open source from fast output */
if (test_bit(GPIOD_FLAG_OPEN_DRAIN, &dflags) ||
test_bit(GPIOD_FLAG_OPEN_SOURCE, &dflags))
- __clear_bit(descs->ndescs,
- array_info->set_mask);
+ __clear_bit(i, array_info->set_mask);
/* Identify 'fast' pins which require invertion */
if (gpiod_is_active_low(desc))
- __set_bit(descs->ndescs,
- array_info->invert_mask);
+ __set_bit(i, array_info->invert_mask);
}
}
if (array_info)
--
2.56.0.307.g5fe7bd680c-goog