* [PATCH v4 0/4] gpiolib: acpi: Consolidated fixes and bounds checking
@ 2026-05-31 12:03 Marco Scardovi
2026-05-31 12:03 ` [PATCH v4 1/4] gpiolib: acpi: Add robust bounds-checking for GPIO pin resources Marco Scardovi
` (3 more replies)
0 siblings, 4 replies; 10+ messages in thread
From: Marco Scardovi @ 2026-05-31 12:03 UTC (permalink / raw)
To: Mika Westerberg, Andy Shevchenko, Linus Walleij,
Bartosz Golaszewski
Cc: linux-gpio, linux-acpi, linux-kernel
Hi all,
while reviewing my patches I ended up noticing these two were concurrent of
the same problem so I ended up merging them and opening a new thread to
avoid any confusion.
lore.kernel.org/linux-gpio/20260518075357.112584-1-mscardovi95@gmail.com/
lore.kernel.org/linux-gpio/20260524162708.62949-1-scardracs@disroot.org/
Please mind to check the 1st one before reviewing it as it contains some
of @Andy's comments on it.
This series consolidates various fixes and bounds-checking improvements
for gpiolib-acpi.
- Patch 1: Adds robust bounds checking for GPIO pin resource arrays.
- Patch 2: Fixes a resource leak and concurrent access race in the
GPIO OperationRegion address space handler.
- Patch 3: Prevents physical address truncation from 64-bit to 16-bit
in the OperationRegion handler.
- Patch 4: Prevents out-of-bounds pointer arithmetic in
acpi_gpio_package_count when counting GPIOs.
Changes in v4:
- Merged the address truncation and package count pointer arithmetic
bounds-checking fixes into this unified patch series.
- Cleaned up the OperationRegion handler modifications to avoid
conflicts between the leak fix and truncation check.
- Added code documentation comments explaining locking requirements in
acpi_gpiochip_find_conn().
- Reworded the commit subject line of Patch 2 to follow standard
kernel conventions.
- Optimized the concurrent double-request rollback path in Patch 2 to
free the descriptor outside the mutex (`conn_lock`) to prevent
potential lockdep issues.
- Refined the `-EBUSY` recovery comment in Patch 2 to accurately
characterize it as a best-effort recovery path.
Marco Scardovi (2):
gpiolib: acpi: prevent address truncation in OperationRegion handler
gpiolib: acpi: fix out-of-bounds pointer arithmetic in
acpi_gpio_package_count
Marco Scardovi (scardracs) (2):
gpiolib: acpi: Add robust bounds-checking for GPIO pin resources
gpiolib: acpi: fix resource leak in OpRegion
drivers/gpio/gpiolib-acpi-core.c | 186 ++++++++++++++++++++++++-------
1 file changed, 147 insertions(+), 39 deletions(-)
--
2.54.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v4 1/4] gpiolib: acpi: Add robust bounds-checking for GPIO pin resources
2026-05-31 12:03 [PATCH v4 0/4] gpiolib: acpi: Consolidated fixes and bounds checking Marco Scardovi
@ 2026-05-31 12:03 ` Marco Scardovi
2026-05-31 12:03 ` [PATCH v4 2/4] gpiolib: acpi: fix resource leak in OpRegion Marco Scardovi
` (2 subsequent siblings)
3 siblings, 0 replies; 10+ messages in thread
From: Marco Scardovi @ 2026-05-31 12:03 UTC (permalink / raw)
To: Mika Westerberg, Andy Shevchenko, Linus Walleij,
Bartosz Golaszewski
Cc: linux-gpio, linux-acpi, linux-kernel, Marco Scardovi (scardracs)
From: "Marco Scardovi (scardracs)" <mscardovi95@gmail.com>
Ensure that the GPIO pin resource arrays are safely bounded before
accessing indices. Add bounds checking in acpi_request_own_gpiod(),
acpi_gpio_irq_is_wake(), and acpi_gpiochip_alloc_event() to prevent
out-of-bounds array reads if the ACPI namespace provides malformed or
empty pin tables.
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Marco Scardovi <mscardovi95@gmail.com>
---
drivers/gpio/gpiolib-acpi-core.c | 24 ++++++++++++++++++++----
1 file changed, 20 insertions(+), 4 deletions(-)
diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c
index eb8a40cfb7a9..a6d78dad299e 100644
--- a/drivers/gpio/gpiolib-acpi-core.c
+++ b/drivers/gpio/gpiolib-acpi-core.c
@@ -320,10 +320,18 @@ static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip,
unsigned int index,
const char *label)
{
- int polarity = GPIO_ACTIVE_HIGH;
- enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio, polarity);
- unsigned int pin = agpio->pin_table[index];
struct gpio_desc *desc;
+ enum gpiod_flags flags;
+ unsigned int pin;
+ int polarity;
+
+ polarity = GPIO_ACTIVE_HIGH;
+ flags = acpi_gpio_to_gpiod_flags(agpio, polarity);
+
+ if (index >= agpio->pin_table_length)
+ return ERR_PTR(-EINVAL);
+
+ pin = agpio->pin_table[index];
desc = gpiochip_request_own_desc(chip, pin, label, polarity, flags);
if (IS_ERR(desc))
@@ -337,11 +345,16 @@ static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip,
static bool acpi_gpio_irq_is_wake(struct device *parent,
const struct acpi_resource_gpio *agpio)
{
- unsigned int pin = agpio->pin_table[0];
+ unsigned int pin;
if (agpio->wake_capable != ACPI_WAKE_CAPABLE)
return false;
+ if (agpio->pin_table_length == 0)
+ return false;
+
+ pin = agpio->pin_table[0];
+
if (acpi_gpio_in_ignore_list(ACPI_GPIO_IGNORE_WAKE, dev_name(parent), pin)) {
dev_info(parent, "Ignoring wakeup on pin %u\n", pin);
return false;
@@ -368,6 +381,9 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares,
return AE_OK;
handle = ACPI_HANDLE(chip->parent);
+ if (agpio->pin_table_length == 0)
+ return AE_OK;
+
pin = agpio->pin_table[0];
if (pin <= 255) {
--
2.54.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v4 2/4] gpiolib: acpi: fix resource leak in OpRegion
2026-05-31 12:03 [PATCH v4 0/4] gpiolib: acpi: Consolidated fixes and bounds checking Marco Scardovi
2026-05-31 12:03 ` [PATCH v4 1/4] gpiolib: acpi: Add robust bounds-checking for GPIO pin resources Marco Scardovi
@ 2026-05-31 12:03 ` Marco Scardovi
2026-06-01 12:18 ` Mika Westerberg
2026-05-31 12:03 ` [PATCH v4 3/4] gpiolib: acpi: prevent address truncation in OperationRegion handler Marco Scardovi
2026-05-31 12:03 ` [PATCH v4 4/4] gpiolib: acpi: fix out-of-bounds pointer arithmetic in acpi_gpio_package_count Marco Scardovi
3 siblings, 1 reply; 10+ messages in thread
From: Marco Scardovi @ 2026-05-31 12:03 UTC (permalink / raw)
To: Mika Westerberg, Andy Shevchenko, Linus Walleij,
Bartosz Golaszewski
Cc: linux-gpio, linux-acpi, linux-kernel, Marco Scardovi (scardracs)
From: "Marco Scardovi (scardracs)" <mscardovi95@gmail.com>
If acpi_remove_address_space_handler() fails, the cleanup function
acpi_gpiochip_free_regions() previously returned early. This leaks
the connections list and all requested GPIO descriptors.
Similarly, if acpi_gpio_adr_space_handler() fails to allocate a connection
or request a GPIO descriptor during a multi-pin transaction, it exits
without freeing the descriptors and connections that were already allocated
in the same call.
To avoid leaks, introduce a localized new_conns list inside the handler to
track the new connections requested during the current transaction. On
error, roll back only the connections in the new_conns list (avoiding
deadlock on the non-recursive conn_lock and preventing over-cleanup of
historic connections). On success, splice the new_conns list into the
global achip->conns list under the conn_lock.
Additionally, rename the global connection cleanup function to
acpi_gpiochip_free_all_connections() and call it in the GPIO chip teardown
path.
Fixes: 473ed7be0da0 ("gpio / ACPI: Add support for ACPI GPIO operation regions")
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Marco Scardovi <mscardovi95@gmail.com>
---
drivers/gpio/gpiolib-acpi-core.c | 142 ++++++++++++++++++++++++-------
1 file changed, 111 insertions(+), 31 deletions(-)
diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c
index a6d78dad299e..e3bc4677b51d 100644
--- a/drivers/gpio/gpiolib-acpi-core.c
+++ b/drivers/gpio/gpiolib-acpi-core.c
@@ -1094,6 +1094,51 @@ int acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, const char *con_id,
}
EXPORT_SYMBOL_GPL(acpi_dev_gpio_irq_wake_get_by);
+static void acpi_gpiochip_free_connection_list(struct list_head *list)
+{
+ struct acpi_gpio_connection *conn;
+ struct acpi_gpio_connection *tmp;
+
+ list_for_each_entry_safe_reverse(conn, tmp, list, node) {
+ gpiochip_free_own_desc(conn->desc);
+ list_del(&conn->node);
+ kfree(conn);
+ }
+}
+
+static void acpi_gpiochip_free_all_connections(struct acpi_gpio_chip *achip)
+{
+ guard(mutex)(&achip->conn_lock);
+
+ acpi_gpiochip_free_connection_list(&achip->conns);
+}
+
+/*
+ * Find a connection in the global or local list.
+ * The caller must hold achip->conn_lock to protect the global list.
+ * The local list new_conns is private to the calling thread.
+ */
+static struct acpi_gpio_connection *
+acpi_gpiochip_find_conn(struct acpi_gpio_chip *achip,
+ struct list_head *new_conns, unsigned int pin)
+{
+ struct acpi_gpio_connection *conn;
+
+ list_for_each_entry(conn, &achip->conns, node) {
+ if (conn->pin == pin)
+ return conn;
+ }
+
+ if (new_conns) {
+ list_for_each_entry(conn, new_conns, node) {
+ if (conn->pin == pin)
+ return conn;
+ }
+ }
+
+ return NULL;
+}
+
static acpi_status
acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
u32 bits, u64 *value, void *handler_context,
@@ -1101,11 +1146,16 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
{
struct acpi_gpio_chip *achip = region_context;
struct gpio_chip *chip = achip->chip;
+ struct acpi_gpio_connection *other;
+ struct acpi_gpio_connection *conn;
struct acpi_resource_gpio *agpio;
struct acpi_resource *ares;
u16 pin_index = address;
+ LIST_HEAD(new_conns);
acpi_status status;
int length;
+ u16 shift;
+ u16 word;
int i;
status = acpi_buffer_to_resource(achip->conn_info.connection,
@@ -1129,20 +1179,15 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
length = min(agpio->pin_table_length, pin_index + bits);
for (i = pin_index; i < length; ++i) {
unsigned int pin = agpio->pin_table[i];
- struct acpi_gpio_connection *conn;
struct gpio_desc *desc;
- u16 word, shift;
- bool found;
+ bool found = false;
mutex_lock(&achip->conn_lock);
- found = false;
- list_for_each_entry(conn, &achip->conns, node) {
- if (conn->pin == pin) {
- found = true;
- desc = conn->desc;
- break;
- }
+ conn = acpi_gpiochip_find_conn(achip, &new_conns, pin);
+ if (conn) {
+ desc = conn->desc;
+ found = true;
}
/*
@@ -1163,34 +1208,66 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
}
}
+ mutex_unlock(&achip->conn_lock);
+
if (!found) {
desc = acpi_request_own_gpiod(chip, agpio, i, "ACPI:OpRegion");
if (IS_ERR(desc)) {
- mutex_unlock(&achip->conn_lock);
+ /*
+ * In case of concurrent handler execution, another
+ * thread may have already requested the same GPIO,
+ * causing acpi_request_own_gpiod() to fail with -EBUSY.
+ * Re-check the visible connection lists and reuse an
+ * existing connection if one is found.
+ */
+ if (PTR_ERR(desc) == -EBUSY) {
+ mutex_lock(&achip->conn_lock);
+ conn = acpi_gpiochip_find_conn(achip, &new_conns, pin);
+ if (conn) {
+ desc = conn->desc;
+ found = true;
+ }
+ mutex_unlock(&achip->conn_lock);
+ if (found)
+ goto use_existing;
+ }
status = AE_ERROR;
- goto out;
+ goto err_free_new_conns;
}
conn = kzalloc_obj(*conn);
if (!conn) {
gpiochip_free_own_desc(desc);
- mutex_unlock(&achip->conn_lock);
status = AE_NO_MEMORY;
- goto out;
+ goto err_free_new_conns;
}
conn->pin = pin;
conn->desc = desc;
- list_add_tail(&conn->node, &achip->conns);
- }
- mutex_unlock(&achip->conn_lock);
+ mutex_lock(&achip->conn_lock);
+ /*
+ * Re-check both lists again before adding to local
+ * new_conns, as another thread could have completed
+ * and spliced its list while conn_lock was released.
+ */
+ other = acpi_gpiochip_find_conn(achip, &new_conns, pin);
+ if (other) {
+ struct gpio_desc *desc_to_free = conn->desc;
+ struct acpi_gpio_connection *c_to_free = conn;
- /*
- * For the cases when OperationRegion() consists of more than
- * 64 bits calculate the word and bit shift to use that one to
- * access the value.
- */
+ desc = other->desc;
+ mutex_unlock(&achip->conn_lock);
+
+ gpiochip_free_own_desc(desc_to_free);
+ kfree(c_to_free);
+ } else {
+ list_add_tail(&conn->node, &new_conns);
+ mutex_unlock(&achip->conn_lock);
+ }
+ }
+
+use_existing:
word = i / 64;
shift = i % 64;
@@ -1204,9 +1281,19 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
}
}
+ mutex_lock(&achip->conn_lock);
+ list_splice_tail(&new_conns, &achip->conns);
+ mutex_unlock(&achip->conn_lock);
+
+ status = AE_OK;
+
out:
ACPI_FREE(ares);
return status;
+
+err_free_new_conns:
+ acpi_gpiochip_free_connection_list(&new_conns);
+ goto out;
}
static void acpi_gpiochip_request_regions(struct acpi_gpio_chip *achip)
@@ -1229,22 +1316,15 @@ static void acpi_gpiochip_free_regions(struct acpi_gpio_chip *achip)
{
struct gpio_chip *chip = achip->chip;
acpi_handle handle = ACPI_HANDLE(chip->parent);
- struct acpi_gpio_connection *conn, *tmp;
acpi_status status;
status = acpi_remove_address_space_handler(handle, ACPI_ADR_SPACE_GPIO,
acpi_gpio_adr_space_handler);
- if (ACPI_FAILURE(status)) {
+ if (ACPI_FAILURE(status))
dev_err(chip->parent,
"Failed to remove GPIO OpRegion handler\n");
- return;
- }
- list_for_each_entry_safe_reverse(conn, tmp, &achip->conns, node) {
- gpiochip_free_own_desc(conn->desc);
- list_del(&conn->node);
- kfree(conn);
- }
+ acpi_gpiochip_free_all_connections(achip);
}
void acpi_gpiochip_add(struct gpio_chip *chip)
--
2.54.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v4 3/4] gpiolib: acpi: prevent address truncation in OperationRegion handler
2026-05-31 12:03 [PATCH v4 0/4] gpiolib: acpi: Consolidated fixes and bounds checking Marco Scardovi
2026-05-31 12:03 ` [PATCH v4 1/4] gpiolib: acpi: Add robust bounds-checking for GPIO pin resources Marco Scardovi
2026-05-31 12:03 ` [PATCH v4 2/4] gpiolib: acpi: fix resource leak in OpRegion Marco Scardovi
@ 2026-05-31 12:03 ` Marco Scardovi
2026-05-31 12:03 ` [PATCH v4 4/4] gpiolib: acpi: fix out-of-bounds pointer arithmetic in acpi_gpio_package_count Marco Scardovi
3 siblings, 0 replies; 10+ messages in thread
From: Marco Scardovi @ 2026-05-31 12:03 UTC (permalink / raw)
To: Mika Westerberg, Andy Shevchenko, Linus Walleij,
Bartosz Golaszewski
Cc: linux-gpio, linux-acpi, linux-kernel
The ACPI address space handler for GPIO OperationRegions receives the
pin offset as a 64-bit acpi_physical_address. However, the handler
truncates this address to a u16 pin_index before validating it.
If an ACPI table attempts to access a pin offset greater than 65535,
the truncation wraps the index around. This may result in accesses to
unintended GPIO pins.
Fix this by adding an explicit check to verify that the 64-bit address
is less than agpio->pin_table_length before assigning it to the u16
pin_index, returning AE_BAD_PARAMETER if it is out of bounds.
Additionally, make the length calculation overflow-safe and change the types
of length and loop counter to unsigned.
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Marco Scardovi <scardracs@disroot.org>
---
drivers/gpio/gpiolib-acpi-core.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c
index e3bc4677b51d..d12dab42a096 100644
--- a/drivers/gpio/gpiolib-acpi-core.c
+++ b/drivers/gpio/gpiolib-acpi-core.c
@@ -1150,13 +1150,13 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
struct acpi_gpio_connection *conn;
struct acpi_resource_gpio *agpio;
struct acpi_resource *ares;
- u16 pin_index = address;
+ unsigned int length;
LIST_HEAD(new_conns);
acpi_status status;
- int length;
+ unsigned int i;
+ u16 pin_index;
u16 shift;
u16 word;
- int i;
status = acpi_buffer_to_resource(achip->conn_info.connection,
achip->conn_info.length, &ares);
@@ -1176,7 +1176,17 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
return AE_BAD_PARAMETER;
}
- length = min(agpio->pin_table_length, pin_index + bits);
+ if (address >= agpio->pin_table_length) {
+ ACPI_FREE(ares);
+ return AE_BAD_PARAMETER;
+ }
+
+ pin_index = address;
+ if (bits > agpio->pin_table_length - pin_index)
+ length = agpio->pin_table_length;
+ else
+ length = pin_index + bits;
+
for (i = pin_index; i < length; ++i) {
unsigned int pin = agpio->pin_table[i];
struct gpio_desc *desc;
--
2.54.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v4 4/4] gpiolib: acpi: fix out-of-bounds pointer arithmetic in acpi_gpio_package_count
2026-05-31 12:03 [PATCH v4 0/4] gpiolib: acpi: Consolidated fixes and bounds checking Marco Scardovi
` (2 preceding siblings ...)
2026-05-31 12:03 ` [PATCH v4 3/4] gpiolib: acpi: prevent address truncation in OperationRegion handler Marco Scardovi
@ 2026-05-31 12:03 ` Marco Scardovi
3 siblings, 0 replies; 10+ messages in thread
From: Marco Scardovi @ 2026-05-31 12:03 UTC (permalink / raw)
To: Mika Westerberg, Andy Shevchenko, Linus Walleij,
Bartosz Golaszewski
Cc: linux-gpio, linux-acpi, linux-kernel
When counting GPIOs in an ACPI package, encountering a reference or
string causes the element pointer to be advanced by 3 (element += 3)
and then by 1 (element++).
If a malformed ACPI package contains fewer than 4 remaining elements
when a reference or string is processed, this pointer arithmetic
advances the element pointer past the end of the package elements
array. This results in undefined behavior and can cause out-of-bounds
reads.
Fix this by ensuring at least 4 elements remain in the package before
advancing the element pointer, returning -EPROTO if the package
structure is invalid.
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Marco Scardovi <scardracs@disroot.org>
---
drivers/gpio/gpiolib-acpi-core.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c
index d12dab42a096..b19fd02b64d0 100644
--- a/drivers/gpio/gpiolib-acpi-core.c
+++ b/drivers/gpio/gpiolib-acpi-core.c
@@ -1407,6 +1407,8 @@ static int acpi_gpio_package_count(const union acpi_object *obj)
switch (element->type) {
case ACPI_TYPE_LOCAL_REFERENCE:
case ACPI_TYPE_STRING:
+ if (end - element < 4)
+ return -EPROTO;
element += 3;
fallthrough;
case ACPI_TYPE_INTEGER:
--
2.54.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v4 2/4] gpiolib: acpi: fix resource leak in OpRegion
2026-05-31 12:03 ` [PATCH v4 2/4] gpiolib: acpi: fix resource leak in OpRegion Marco Scardovi
@ 2026-06-01 12:18 ` Mika Westerberg
2026-06-01 13:03 ` Marco Scardovi
2026-06-02 7:57 ` Andy Shevchenko
0 siblings, 2 replies; 10+ messages in thread
From: Mika Westerberg @ 2026-06-01 12:18 UTC (permalink / raw)
To: Marco Scardovi
Cc: Mika Westerberg, Andy Shevchenko, Linus Walleij,
Bartosz Golaszewski, linux-gpio, linux-acpi, linux-kernel,
Marco Scardovi (scardracs)
Hi,
On Sun, May 31, 2026 at 02:03:09PM +0200, Marco Scardovi wrote:
> From: "Marco Scardovi (scardracs)" <mscardovi95@gmail.com>
>
> If acpi_remove_address_space_handler() fails, the cleanup function
> acpi_gpiochip_free_regions() previously returned early. This leaks
> the connections list and all requested GPIO descriptors.
>
> Similarly, if acpi_gpio_adr_space_handler() fails to allocate a connection
> or request a GPIO descriptor during a multi-pin transaction, it exits
> without freeing the descriptors and connections that were already allocated
> in the same call.
>
> To avoid leaks, introduce a localized new_conns list inside the handler to
> track the new connections requested during the current transaction. On
> error, roll back only the connections in the new_conns list (avoiding
> deadlock on the non-recursive conn_lock and preventing over-cleanup of
> historic connections). On success, splice the new_conns list into the
> global achip->conns list under the conn_lock.
>
> Additionally, rename the global connection cleanup function to
> acpi_gpiochip_free_all_connections() and call it in the GPIO chip teardown
> path.
>
> Fixes: 473ed7be0da0 ("gpio / ACPI: Add support for ACPI GPIO operation regions")
> Assisted-by: Antigravity:gemini-3.5-flash
> Signed-off-by: Marco Scardovi <mscardovi95@gmail.com>
> ---
> drivers/gpio/gpiolib-acpi-core.c | 142 ++++++++++++++++++++++++-------
> 1 file changed, 111 insertions(+), 31 deletions(-)
This is really complex solution to a problem I'm not sure that even exists.
> diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c
> index a6d78dad299e..e3bc4677b51d 100644
> --- a/drivers/gpio/gpiolib-acpi-core.c
> +++ b/drivers/gpio/gpiolib-acpi-core.c
> @@ -1094,6 +1094,51 @@ int acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, const char *con_id,
> }
> EXPORT_SYMBOL_GPL(acpi_dev_gpio_irq_wake_get_by);
>
> +static void acpi_gpiochip_free_connection_list(struct list_head *list)
> +{
> + struct acpi_gpio_connection *conn;
> + struct acpi_gpio_connection *tmp;
> +
> + list_for_each_entry_safe_reverse(conn, tmp, list, node) {
> + gpiochip_free_own_desc(conn->desc);
> + list_del(&conn->node);
> + kfree(conn);
> + }
> +}
> +
> +static void acpi_gpiochip_free_all_connections(struct acpi_gpio_chip *achip)
> +{
> + guard(mutex)(&achip->conn_lock);
> +
> + acpi_gpiochip_free_connection_list(&achip->conns);
> +}
> +
> +/*
> + * Find a connection in the global or local list.
> + * The caller must hold achip->conn_lock to protect the global list.
> + * The local list new_conns is private to the calling thread.
> + */
> +static struct acpi_gpio_connection *
> +acpi_gpiochip_find_conn(struct acpi_gpio_chip *achip,
> + struct list_head *new_conns, unsigned int pin)
> +{
> + struct acpi_gpio_connection *conn;
> +
> + list_for_each_entry(conn, &achip->conns, node) {
> + if (conn->pin == pin)
> + return conn;
> + }
> +
> + if (new_conns) {
> + list_for_each_entry(conn, new_conns, node) {
> + if (conn->pin == pin)
> + return conn;
> + }
> + }
> +
> + return NULL;
> +}
> +
> static acpi_status
> acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
> u32 bits, u64 *value, void *handler_context,
> @@ -1101,11 +1146,16 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
> {
> struct acpi_gpio_chip *achip = region_context;
> struct gpio_chip *chip = achip->chip;
> + struct acpi_gpio_connection *other;
> + struct acpi_gpio_connection *conn;
> struct acpi_resource_gpio *agpio;
> struct acpi_resource *ares;
> u16 pin_index = address;
> + LIST_HEAD(new_conns);
> acpi_status status;
> int length;
> + u16 shift;
> + u16 word;
> int i;
>
> status = acpi_buffer_to_resource(achip->conn_info.connection,
> @@ -1129,20 +1179,15 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
> length = min(agpio->pin_table_length, pin_index + bits);
> for (i = pin_index; i < length; ++i) {
> unsigned int pin = agpio->pin_table[i];
> - struct acpi_gpio_connection *conn;
> struct gpio_desc *desc;
> - u16 word, shift;
> - bool found;
> + bool found = false;
>
> mutex_lock(&achip->conn_lock);
>
> - found = false;
> - list_for_each_entry(conn, &achip->conns, node) {
> - if (conn->pin == pin) {
> - found = true;
> - desc = conn->desc;
> - break;
> - }
> + conn = acpi_gpiochip_find_conn(achip, &new_conns, pin);
> + if (conn) {
> + desc = conn->desc;
> + found = true;
> }
>
> /*
> @@ -1163,34 +1208,66 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
> }
> }
>
> + mutex_unlock(&achip->conn_lock);
> +
> if (!found) {
> desc = acpi_request_own_gpiod(chip, agpio, i, "ACPI:OpRegion");
If any of the below fails we clean up the descriptor while the prior are
already in the list. Once the GPIO chip is released the list is released as
well.
> - mutex_unlock(&achip->conn_lock);
> + /*
> + * In case of concurrent handler execution, another
> + * thread may have already requested the same GPIO,
> + * causing acpi_request_own_gpiod() to fail with -EBUSY.
> + * Re-check the visible connection lists and reuse an
> + * existing connection if one is found.
> + */
> + if (PTR_ERR(desc) == -EBUSY) {
> + mutex_lock(&achip->conn_lock);
> + conn = acpi_gpiochip_find_conn(achip, &new_conns, pin);
> + if (conn) {
> + desc = conn->desc;
> + found = true;
> + }
> + mutex_unlock(&achip->conn_lock);
> + if (found)
> + goto use_existing;
> + }
> status = AE_ERROR;
> - goto out;
> + goto err_free_new_conns;
> }
>
> conn = kzalloc_obj(*conn);
> if (!conn) {
> gpiochip_free_own_desc(desc);
> - mutex_unlock(&achip->conn_lock);
> status = AE_NO_MEMORY;
> - goto out;
> + goto err_free_new_conns;
> }
>
> conn->pin = pin;
> conn->desc = desc;
> - list_add_tail(&conn->node, &achip->conns);
> - }
>
> - mutex_unlock(&achip->conn_lock);
> + mutex_lock(&achip->conn_lock);
> + /*
> + * Re-check both lists again before adding to local
> + * new_conns, as another thread could have completed
> + * and spliced its list while conn_lock was released.
> + */
> + other = acpi_gpiochip_find_conn(achip, &new_conns, pin);
> + if (other) {
> + struct gpio_desc *desc_to_free = conn->desc;
> + struct acpi_gpio_connection *c_to_free = conn;
>
> - /*
> - * For the cases when OperationRegion() consists of more than
> - * 64 bits calculate the word and bit shift to use that one to
> - * access the value.
> - */
> + desc = other->desc;
> + mutex_unlock(&achip->conn_lock);
> +
> + gpiochip_free_own_desc(desc_to_free);
> + kfree(c_to_free);
> + } else {
> + list_add_tail(&conn->node, &new_conns);
> + mutex_unlock(&achip->conn_lock);
> + }
> + }
> +
> +use_existing:
> word = i / 64;
> shift = i % 64;
>
> @@ -1204,9 +1281,19 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
> }
> }
>
> + mutex_lock(&achip->conn_lock);
> + list_splice_tail(&new_conns, &achip->conns);
> + mutex_unlock(&achip->conn_lock);
> +
> + status = AE_OK;
> +
> out:
> ACPI_FREE(ares);
> return status;
> +
> +err_free_new_conns:
> + acpi_gpiochip_free_connection_list(&new_conns);
> + goto out;
> }
>
> static void acpi_gpiochip_request_regions(struct acpi_gpio_chip *achip)
> @@ -1229,22 +1316,15 @@ static void acpi_gpiochip_free_regions(struct acpi_gpio_chip *achip)
> {
> struct gpio_chip *chip = achip->chip;
> acpi_handle handle = ACPI_HANDLE(chip->parent);
> - struct acpi_gpio_connection *conn, *tmp;
> acpi_status status;
>
> status = acpi_remove_address_space_handler(handle, ACPI_ADR_SPACE_GPIO,
> acpi_gpio_adr_space_handler);
> - if (ACPI_FAILURE(status)) {
> + if (ACPI_FAILURE(status))
> dev_err(chip->parent,
> "Failed to remove GPIO OpRegion handler\n");
> - return;
> - }
If we fail to remove the OpRegion handler and continue to release the
structures does that mean any AML code that accesses the GPIOs now are
accessing already removed (below) objects? Here _REG is not called (we
failed to remove the OpRegion handler) so the AML does not know that it is
gone so it can happily still access the region. So IMHO it is safer to keep
the connections instead of removing them.
Of course it is possible that I'm missing something. It has been a while
since I looked at any of this stuff.
>
> - list_for_each_entry_safe_reverse(conn, tmp, &achip->conns, node) {
> - gpiochip_free_own_desc(conn->desc);
> - list_del(&conn->node);
> - kfree(conn);
> - }
> + acpi_gpiochip_free_all_connections(achip);
> }
>
> void acpi_gpiochip_add(struct gpio_chip *chip)
> --
> 2.54.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v4 2/4] gpiolib: acpi: fix resource leak in OpRegion
2026-06-01 12:18 ` Mika Westerberg
@ 2026-06-01 13:03 ` Marco Scardovi
2026-06-02 7:57 ` Andy Shevchenko
1 sibling, 0 replies; 10+ messages in thread
From: Marco Scardovi @ 2026-06-01 13:03 UTC (permalink / raw)
To: Mika Westerberg
Cc: Mika Westerberg, Andy Shevchenko, Linus Walleij,
Bartosz Golaszewski, linux-gpio, linux-acpi, linux-kernel
In data lunedì 1 giugno 2026 14:18:15 Ora legale dell’Europa centrale, hai
scritto:
> Hi,
>
> On Sun, May 31, 2026 at 02:03:09PM +0200, Marco Scardovi wrote:
> > ...
>
> If we fail to remove the OpRegion handler and continue to release the
> structures does that mean any AML code that accesses the GPIOs now are
> accessing already removed (below) objects? Here _REG is not called (we
> failed to remove the OpRegion handler) so the AML does not know that it is
> gone so it can happily still access the region. So IMHO it is safer to keep
> the connections instead of removing them.
>
> Of course it is possible that I'm missing something. It has been a while
> since I looked at any of this stuff.
>
Hi Mika,
no, I get your point: I was being overly cautious about something that may
or may not happen. Consider these patches as void and sorry for the noise.
Marco
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v4 2/4] gpiolib: acpi: fix resource leak in OpRegion
2026-06-01 12:18 ` Mika Westerberg
2026-06-01 13:03 ` Marco Scardovi
@ 2026-06-02 7:57 ` Andy Shevchenko
2026-06-02 9:53 ` Mika Westerberg
1 sibling, 1 reply; 10+ messages in thread
From: Andy Shevchenko @ 2026-06-02 7:57 UTC (permalink / raw)
To: Mika Westerberg
Cc: Marco Scardovi, Mika Westerberg, Linus Walleij,
Bartosz Golaszewski, linux-gpio, linux-acpi, linux-kernel,
Marco Scardovi (scardracs)
On Mon, Jun 01, 2026 at 02:18:15PM +0200, Mika Westerberg wrote:
> On Sun, May 31, 2026 at 02:03:09PM +0200, Marco Scardovi wrote:
...
> This is really complex solution to a problem I'm not sure that even exists.
The problem that potentially may have an attack vector is the malicious ACPI tables
(via SSDT overlay, for example). So some of the validations (like pin index in the
pin list) would be somewhat useful.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v4 2/4] gpiolib: acpi: fix resource leak in OpRegion
2026-06-02 7:57 ` Andy Shevchenko
@ 2026-06-02 9:53 ` Mika Westerberg
2026-06-02 10:04 ` Andy Shevchenko
0 siblings, 1 reply; 10+ messages in thread
From: Mika Westerberg @ 2026-06-02 9:53 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Marco Scardovi, Mika Westerberg, Linus Walleij,
Bartosz Golaszewski, linux-gpio, linux-acpi, linux-kernel,
Marco Scardovi (scardracs)
On Tue, Jun 02, 2026 at 10:57:50AM +0300, Andy Shevchenko wrote:
> On Mon, Jun 01, 2026 at 02:18:15PM +0200, Mika Westerberg wrote:
> > On Sun, May 31, 2026 at 02:03:09PM +0200, Marco Scardovi wrote:
>
> ...
>
> > This is really complex solution to a problem I'm not sure that even exists.
>
> The problem that potentially may have an attack vector is the malicious ACPI tables
> (via SSDT overlay, for example). So some of the validations (like pin index in the
> pin list) would be somewhat useful.
Yes but with SSDT overlay you can do much worse things than passing an
invalid pin index.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v4 2/4] gpiolib: acpi: fix resource leak in OpRegion
2026-06-02 9:53 ` Mika Westerberg
@ 2026-06-02 10:04 ` Andy Shevchenko
0 siblings, 0 replies; 10+ messages in thread
From: Andy Shevchenko @ 2026-06-02 10:04 UTC (permalink / raw)
To: Mika Westerberg
Cc: Marco Scardovi, Mika Westerberg, Linus Walleij,
Bartosz Golaszewski, linux-gpio, linux-acpi, linux-kernel,
Marco Scardovi (scardracs)
On Tue, Jun 02, 2026 at 11:53:46AM +0200, Mika Westerberg wrote:
> On Tue, Jun 02, 2026 at 10:57:50AM +0300, Andy Shevchenko wrote:
> > On Mon, Jun 01, 2026 at 02:18:15PM +0200, Mika Westerberg wrote:
> > > On Sun, May 31, 2026 at 02:03:09PM +0200, Marco Scardovi wrote:
...
> > > This is really complex solution to a problem I'm not sure that even exists.
> >
> > The problem that potentially may have an attack vector is the malicious ACPI tables
> > (via SSDT overlay, for example). So some of the validations (like pin index in the
> > pin list) would be somewhat useful.
>
> Yes but with SSDT overlay you can do much worse things than passing an
> invalid pin index.
Still my point is valid :-)
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-06-02 10:04 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-31 12:03 [PATCH v4 0/4] gpiolib: acpi: Consolidated fixes and bounds checking Marco Scardovi
2026-05-31 12:03 ` [PATCH v4 1/4] gpiolib: acpi: Add robust bounds-checking for GPIO pin resources Marco Scardovi
2026-05-31 12:03 ` [PATCH v4 2/4] gpiolib: acpi: fix resource leak in OpRegion Marco Scardovi
2026-06-01 12:18 ` Mika Westerberg
2026-06-01 13:03 ` Marco Scardovi
2026-06-02 7:57 ` Andy Shevchenko
2026-06-02 9:53 ` Mika Westerberg
2026-06-02 10:04 ` Andy Shevchenko
2026-05-31 12:03 ` [PATCH v4 3/4] gpiolib: acpi: prevent address truncation in OperationRegion handler Marco Scardovi
2026-05-31 12:03 ` [PATCH v4 4/4] gpiolib: acpi: fix out-of-bounds pointer arithmetic in acpi_gpio_package_count Marco Scardovi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox