[PATCH AUTOSEL 6.18] gpiolib: acpi: Add robust bounds-checking for GPIO pin resources

Sasha Levin <[email protected]>
Newsgroups org.kernel.vger.linux-acpi,dev.linux.lists.patches,org.kernel.vger.linux-gpio,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <[email protected]>
From: Marco Scardovi <[email protected]>

[ Upstream commit dece79032f529d2c9fdbf63a9f2fc32244722775 ]

Ensure that GPIO pin resource arrays are safely bounded before accessing
indices. Add explicit 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.

This change addresses potential safety issues arising from
inconsistent or invalid ACPI pin tables. It does not alter functional
behavior in well-formed tables.

Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Marco Scardovi <[email protected]>
Acked-by: Mika Westerberg <[email protected]>
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Bartosz Golaszewski <[email protected]>
Signed-off-by: Sasha Levin <[email protected]>
---

LLM Generated explanations, may be completely bogus:

## Phase 1: Commit Message Forensics

### Step 1.1: Subject Line
**Record:** `[gpiolib: acpi] [add] Add robust bounds-checking for GPIO
pin resources` — defensive hardening in the ACPI GPIO core to validate
`pin_table` indices before array access.

### Step 1.2: Commit Message Tags
**Record:**
- **Acked-by:** Mika Westerberg `<[email protected]>`
  (GPIO/ACPI maintainer)
- **Link:**
  https://patch.msgid.link/[email protected]
- **Signed-off-by:** Marco Scardovi, Bartosz Golaszewski (gpiolib
  maintainer)
- **Assisted-by:** Antigravity:gemini-3.5-flash
- **No** Fixes:, Reported-by:, Tested-by:, Cc: [email protected],
  syzbot links

Notable: maintainer ack, but no fuzzer report or explicit stable
nomination.

### Step 1.3: Commit Body Analysis
**Record:**
- **Bug:** Three functions access `agpio->pin_table[]` without verifying
  `pin_table_length` first.
- **Symptom:** Out-of-bounds array reads when ACPI firmware provides
  empty or malformed GPIO pin tables.
- **Failure mode:** OOB read of `pin_table`; no change for well-formed
  tables.
- **Root cause:** Missing bounds checks in helper functions
  extracted/consolidated over years; one lookup path (line 719) already
  checks, but event/wakeup helpers do not.

### Step 1.4: Hidden Bug Fix Detection
**Record:** Yes — labeled as “robust bounds-checking” but is a real
memory-safety fix (out-of-bounds read prevention), not cosmetic cleanup.

---

## Phase 2: Diff Analysis

### Step 2.1: Change Inventory
**Record:**
- **File:** `drivers/gpio/gpiolib-acpi-core.c` (+19 / -4, ~23 lines
  touched)
- **Functions:** `acpi_request_own_gpiod()`, `acpi_gpio_irq_is_wake()`,
  `acpi_gpiochip_alloc_event()`
- **Scope:** Single-file, surgical fix

### Step 2.2: Code Flow Changes
**Record:**
1. **`acpi_request_own_gpiod()`:** Before → directly indexed
   `agpio->pin_table[index]`. After → returns `ERR_PTR(-EINVAL)` if
   `index >= pin_table_length`, then accesses table.
2. **`acpi_gpio_irq_is_wake()`:** Before → read `pin_table[0]`
   unconditionally. After → returns `false` if `pin_table_length == 0`.
3. **`acpi_gpiochip_alloc_event()`:** Before → read `pin_table[0]` after
   IRQ-resource check. After → returns `AE_OK` early if
   `pin_table_length == 0`.

### Step 2.3: Bug Mechanism
**Record:** **Category:** Buffer overflow / out-of-bounds read.
**Mechanism:** `pin_table_length` can be 0 (or `index` can be out of
range) while code still indexes `pin_table[]`, reading memory past the
allocated ACPI resource buffer.

### Step 2.4: Fix Quality
**Record:** Obviously correct, minimal, matches existing pattern at line
719 in the same file. Low regression risk — only affects malformed/empty
tables; well-formed tables unchanged. `acpi_gpiochip_alloc_event()`
already treats most failures as non-fatal (`AE_OK`), consistent with new
early return.

---

## Phase 3: Git History Investigation

### Step 3.1: Blame
**Record:**
- `acpi_request_own_gpiod()` unbounded access since `2e2b496cebefb` (Nov
  2020)
- `acpi_gpio_irq_is_wake()` unbounded `[0]` access since
  `0c2cae09a765b1` (Mar 2022)
- `acpi_gpiochip_alloc_event()` unbounded `[0]` access since
  `6072b9dcf97870` (Mar 2014)
- All present in this 6.18.y tree

### Step 3.2: Fixes: Tag
**Record:** N/A — no Fixes: tag in commit message.

### Step 3.3: Related File History
**Record:** Recent related fix in same file: `f749b366b8e79` “Fix
potential out-of-boundary left shift” (backported to stable with `Cc:
stable`). This commit is patch 1/2 of a v6 series; patch 2/2 hardens the
OperationRegion handler separately and is **not** required for this
patch to apply or function.

### Step 3.4: Author Context
**Record:** Marco Scardovi is a contributor (Rockchip GPIO fixes); not
the subsystem maintainer. Patch was acked by Mika Westerberg.

### Step 3.5: Dependencies
**Record:** Standalone. No prerequisite commits. Patch 2/2 is
complementary but independent. Applies cleanly to current `gpiolib-acpi-
core.c` in this tree.

---

## Phase 4: Mailing List and External Research

### Step 4.1: Original Discussion
**Record:** Part of `[PATCH v6 0/2]` series submitted June 10, 2026.
Patch 1/2 is this commit. `b4 shazam` could not find it on lore (likely
too new for index). Web search found lkml/spinics archives confirming
content and v6 cover letter. lore.kernel.org direct fetch blocked by bot
protection.

### Step 4.2: Reviewers
**Record:** v6 cover letter CCs Mika Westerberg, Andy Shevchenko, Linus
Walleij, Bartosz Golaszewski, linux-gpio@, linux-acpi@. Acked-by from
Mika Westerberg in committed version.

### Step 4.3: Bug Reports
**Record:** No syzbot, bugzilla, or user crash reports. Issue identified
by code review / defensive analysis of ACPI edge cases.

### Step 4.4: Series Context
**Record:** 2-patch series. This patch covers
event/wakeup/`acpi_request_own_gpiod` paths. Patch 2/2 covers
OperationRegion handler bounds (not in this tree yet). This patch is
self-contained.

### Step 4.5: Stable List History
**Record:** No stable-list discussion found. Absence of `Cc: stable` is
expected per review instructions.

---

## Phase 5: Code Semantic Analysis

### Step 5.1: Key Functions
**Record:** `acpi_request_own_gpiod`, `acpi_gpio_irq_is_wake`,
`acpi_gpiochip_alloc_event`

### Step 5.2: Callers
**Record:**
- `acpi_gpiochip_alloc_event` → called from
  `acpi_gpiochip_request_interrupts()` via `acpi_walk_resources()` on
  `_AEI`
- `acpi_gpiochip_request_interrupts()` → called from
  `gpiochip_irqchip_add()` in `gpiolib.c` during every GPIO chip IRQ
  setup
- `acpi_request_own_gpiod` → called from `acpi_gpiochip_alloc_event()`
  (index 0) and OpRegion handler (index `i` in bounded loop at line
  1113)
- `acpi_gpio_irq_is_wake` → called from `acpi_gpiochip_alloc_event()`
  (line 445) and ACPI GPIO lookup callback (line 731, after existing
  bounds check at 719)

### Step 5.3: Callees
**Record:** `gpiochip_request_own_desc`, `acpi_gpio_in_ignore_list`,
`acpi_get_handle`, `gpiochip_lock_as_irq`, etc. — standard GPIO/ACPI
operations during probe and event registration.

### Step 5.4: Reachability
**Record:** Triggered during GPIO controller registration on **every
ACPI platform** at boot (`CONFIG_ACPI` + GPIO chip with IRQ support).
Not directly userspace-triggerable, but firmware ACPI tables are the
input. Malformed `_AEI` GPIO resources with `pin_table_length == 0` hit
`acpi_gpiochip_alloc_event` on every affected chip probe.

### Step 5.5: Similar Patterns
**Record:** Line 719 already has `if (pin_index >=
agpio->pin_table_length) return 1;` in the lookup path — this patch
closes the same gap in the event/wakeup helpers. OpRegion loop uses
`min_t(u16, agpio->pin_table_length, pin_index + bits)` but still calls
`acpi_request_own_gpiod` without its own index guard.

---

## Phase 6: Cross-Reference Against Local Tree

### Step 6.1: Buggy Code Present?
**Record:** **Yes.** Local tree is **v6.18.44** (`VERSION=6,
PATCHLEVEL=18, SUBLEVEL=44`). All three functions lack the proposed
bounds checks (verified by reading current file). Bug dates to 2014–2020
code still present.

### Step 6.2: Backport Complications
**Record:** **Clean apply expected.** Single hunk in one file, no
structural divergence. No conflicting recent changes in these functions.

### Step 6.3: Related Fixes Already Present?
**Record:** Partial protection exists in ACPI GPIO lookup (line 719) and
OpRegion loop (line 1113), but **not** in the three functions this
commit fixes. The proposed fix is **not** already present.

---

## Phase 7: Subsystem Context

### Step 7.1: Subsystem Criticality
**Record:** `drivers/gpio/gpiolib-acpi-core.c` — **IMPORTANT**
subsystem. ACPI GPIO core used on x86 laptops/servers and ACPI-enabled
ARM platforms during device enumeration and interrupt setup.

### Step 7.2: Activity
**Record:** Actively maintained; recent stable-relevant fixes in same
file (e.g., `f749b366` OOB/UB fix backported to stable).

---

## Phase 8: Impact and Risk Assessment

### Step 8.1: Who Is Affected
**Record:** ACPI systems with GPIO controllers (`CONFIG_ACPI` +
`CONFIG_GPIOLIB`). All such platforms traverse this code at GPIO chip
registration.

### Step 8.2: Trigger Conditions
**Record:** Malformed or empty ACPI GPIO pin tables in `_AEI` resources
or other GPIO resource descriptors. Uncommon but plausible with buggy
firmware. Not unprivileged-userspace-triggerable; firmware-dependent.

### Step 8.3: Failure Mode Severity
**Record:** Out-of-bounds kernel read → **MEDIUM-HIGH**. On KASAN
builds: detectable memory safety bug. On production: may read adjacent
memory (garbage pin number, possible mis-driven GPIO or further errors).
Unlikely to panic in all cases, but real safety defect in a core boot
path.

### Step 8.4: Risk-Benefit
**Record:**
- **Benefit:** Closes longstanding OOB-read holes in ACPI GPIO
  event/wakeup path; aligns with existing bounds check at line 719;
  precedent from `f749b366` in same file.
- **Risk:** Very low — ~15 lines of early-return guards, no API/behavior
  change for valid tables.
- **Ratio:** Favorable for stable.

---

## Phase 9: Final Synthesis

### Step 9.1: Evidence Summary

**FOR backport:**
- Real out-of-bounds read bug in core ACPI GPIO code present since
  2014–2020
- Affects boot-time GPIO/ACPI event setup on all ACPI platforms
- Small, surgical, maintainer-acked fix
- Buggy code confirmed in v6.18.44 tree; fix applies cleanly
- Consistent with prior stable backport of OOB fix in same file
  (`f749b366`)
- Defense-in-depth where partial checks already exist but are incomplete

**AGAINST backport:**
- No syzbot/user crash reports
- Requires malformed ACPI firmware (uncommon trigger)
- Patch 1/2 of a series (though standalone)
- Impact on production kernels without KASAN may be limited to reading
  garbage rather than immediate crash

**Unresolved:** No direct lore thread review comments retrieved (lore
blocked); no runtime crash evidence.

### Step 9.2: Stable Rules Checklist
1. Obviously correct and tested? **PASS** — straightforward bounds
   checks; maintainer acked
2. Fixes a real bug? **PASS** — OOB array access on empty/malformed pin
   tables
3. Important issue? **PASS** — memory safety / OOB read in core boot
   path (MEDIUM-HIGH severity)
4. Small and contained? **PASS** — one file, ~19 lines added
5. No new features/APIs? **PASS** — pure defensive validation
6. Can apply to local tree? **PASS** — buggy code present, clean apply
   expected

### Step 9.3: Exception Categories
**Record:** None (not device ID, quirk, DT, build, or docs). Standard
memory-safety bug fix.

### Step 9.4: Decision Rationale

For **this v6.18.44 tree**, the unbounded `pin_table[]` accesses have
been present for years in code that runs during GPIO chip registration
on every ACPI machine. The fix is minimal, obviously correct,
maintainer-reviewed, and closes a real out-of-bounds read class that
partial existing checks do not cover (especially
`acpi_gpiochip_alloc_event` reading `pin_table[0]` before any length
validation). While no crash report was found, stable trees routinely
accept such defensive fixes in core subsystems — particularly when a
nearly identical class of fix was already backported in the same file.
Risk of regression is negligible.

---

## Verification

- **[Phase 1]** Parsed commit message and tags from user-provided diff
- **[Phase 1]** Confirmed no Fixes:/Reported-by:/Cc: stable tags
- **[Phase 2]** Analyzed diff: 3 functions, +19/-4 lines in `gpiolib-
  acpi-core.c`
- **[Phase 3]** `git describe HEAD` → `v6.18.44-1-g2736c32da98b9`;
  Makefile → 6.18.44
- **[Phase 3]** `git blame` on lines 318–372: buggy code from 2014–2022
  commits, all in tree
- **[Phase 3]** `git show f749b366b8e79`: related OOB fix in same file,
  Cc: stable, already in tree
- **[Phase 3]** `git log --oneline -20 -- drivers/gpio/gpiolib-acpi-
  core.c`: no duplicate fix present
- **[Phase 4]** `b4 shazam` / `b4 dig`: patch not indexed on lore yet
- **[Phase 4]** Web search: confirmed v6 0/2 series, patch 1/2 content,
  maintainer CC list
- **[Phase 4]** WebFetch lore/patch.msgid.link: blocked/timeout — review
  comments unverified
- **[Phase 5]** `grep acpi_gpio_irq_is_wake`: callers at lines 445, 731
- **[Phase 5]** `grep acpi_gpiochip_request_interrupts`: called from
  `gpiolib.c:2131` during IRQ chip add
- **[Phase 5]** Read `gpiolib-acpi-core.c` lines 280–460, 700–750,
  1095–1200: confirmed missing bounds checks and existing partial checks
  at 719/1113
- **[Phase 6]** Read current tree code: all three functions lack
  proposed guards
- **[Phase 6]** Confirmed patch 2/2 (OpRegion) not in tree; patch 1/2 is
  independent
- **[Phase 7]** Subsystem: ACPI GPIO core, IMPORTANT criticality
- **[Phase 8]** Failure mode: OOB read, MEDIUM-HIGH; trigger: malformed
  ACPI firmware at boot

**YES**

 drivers/gpio/gpiolib-acpi-core.c | 23 +++++++++++++++++++----
 1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c
index 8851e48669756..eb14351afef90 100644
--- a/drivers/gpio/gpiolib-acpi-core.c
+++ b/drivers/gpio/gpiolib-acpi-core.c
@@ -320,10 +320,17 @@ 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];
+	enum gpiod_flags flags;
 	struct gpio_desc *desc;
+	unsigned int pin;
+	int polarity;
+
+	if (index >= agpio->pin_table_length)
+		return ERR_PTR(-EINVAL);
+
+	pin = agpio->pin_table[index];
+	polarity = GPIO_ACTIVE_HIGH;
+	flags = acpi_gpio_to_gpiod_flags(agpio, polarity);
 
 	desc = gpiochip_request_own_desc(chip, pin, label, polarity, flags);
 	if (IS_ERR(desc))
@@ -337,7 +344,12 @@ 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->pin_table_length == 0)
+		return false;
+
+	pin = agpio->pin_table[0];
 
 	if (agpio->wake_capable != ACPI_WAKE_CAPABLE)
 		return false;
@@ -367,6 +379,9 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares,
 	if (!acpi_gpio_get_irq_resource(ares, &agpio))
 		return AE_OK;
 
+	if (agpio->pin_table_length == 0)
+		return AE_OK;
+
 	handle = ACPI_HANDLE(chip->parent);
 	pin = agpio->pin_table[0];
 
-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.