Information disclosure (kernel memory leakage via GPIO short-read in ADM1266 GPIO accessors)
Description
The commit fixes an information disclosure vulnerability in ADM1266 GPIO accessors under hwmon/pmbus. Previously adm1266_gpio_get() and adm1266_gpio_get_multiple() performed a 2-byte block read and combined read_buf[0] and read_buf[1] without ensuring two bytes were actually returned. If the I2C device returned 0 or 1 bytes (short block-read), read_buf[1] could be uninitialized stack memory, and the resulting pins_status/status value would leak kernel stack bits to userspace via gpiolib (sysfs or ioctls). The patch adds a check for ret < 2 and returns -EIO, preventing use of potentially garbage data and eliminating the leak.
Proof of Concept
PoC (proof-of-concept) is provided conceptually, showing how an attacker with a vulnerable kernel could trigger a short I2C block read and observe leaked kernel data via GPIO status reporting. The PoC relies on an I2C device that returns 0 or 1 byte for a GPIO_STATUS/PDIO_STATUS block read on the ADM1266 interface. On a vulnerable kernel, the code path would compose pins_status from read_buf[0] and read_buf[1], where read_buf[1] contains uninitialized stack data, leaking bits to userspace. The following simple demonstration illustrates the exploit using a user-space I2C interface (requires a device/emulator that returns only 1 byte for the GPIO_STATUS read or a stub device that reports a short read).
Python PoC (conceptual, requires a vulnerable kernel and a short-read-capable I2C device):
from smbus2 import SMBus
DEVICE_ADDR = 0x5A # ADM1266 GPIO/Status device address (example)
GPIO_STATUS = 0x10 # ADM1266 GPIO_STATUS command (example)
with SMBus(1) as bus:
# Attempt to read 2 bytes from the GPIO_STATUS; a vulnerable kernel may return only 0 or 1 byte
data = bus.read_i2c_block_data(DEVICE_ADDR, GPIO_STATUS, 2)
pins_status = data[0] + (data[1] << 8)
print("pins_status:", pins_status)
Notes:
- On a vulnerable kernel (pre-fix), data[1] could be uninitialized and contain stack garbage, causing information disclosure via the reported pins_status value.
- On the fixed kernel, the driver returns -EIO when ret < 2, so this PoC would fail with an error rather than leaking memory.
- The exact I2C device address and GPIO_STATUS command are hardware-dependent and are used here as illustrative placeholders. A real PoC would use an I2C stub or emulator that returns only 0 or 1 byte for the block read.
Commit Details
Author: Abdurrahman Hussain
Date: 2026-05-19 00:52 UTC
Message:
hwmon: (pmbus/adm1266) reject short block-read responses in the GPIO accessors
adm1266_gpio_get() and adm1266_gpio_get_multiple() both compose the
pin-status word as
pins_status = read_buf[0] + (read_buf[1] << 8);
right after i2c_smbus_read_block_data(), guarding only against an
error return. A well-behaved device returns 2 bytes for
GPIO_STATUS/PDIO_STATUS, but the helper happily reports a 0- or
1-byte response too. If the device returns 0 bytes, both read_buf
slots are uninitialized stack memory; if it returns 1 byte, read_buf[1]
is.
The composed value then flows through set_bit() into the caller's
*bits in adm1266_gpio_get_multiple(), or into the return value of
adm1266_gpio_get(), and ends up in userspace via gpiolib (sysfs and
the char-dev ioctls). That leaks a few bits of kernel stack per
request on any device whose firmware glitch, bus error, or hostile
slave produces a short block-read response.
Add the missing length check to both call sites and surface a short
response as -EIO.
Fixes: d98dfad35c38 ("hwmon: (pmbus/adm1266) Add support for GPIOs")
Cc: stable@vger.kernel.org
Signed-off-by: Abdurrahman Hussain <abdurrahman@nexthop.ai>
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20260518-adm1266-gpio-fixes-v3-3-e425e4f88139@nexthop.ai
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Triage Assessment
Vulnerability Type: Information disclosure
Confidence: HIGH
Reasoning:
The patch adds a check for short I2C block reads (ret < 2) and returns -EIO, preventing the code from using potentially uninitialized read_buf data to form GPIO status. Without this, a faulty/short response could leak kernel stack bits to userspace via gpiolib, constituting an information disclosure vulnerability. The fix is a targeted security fix rather than a general refactor or feature change.
Verification Assessment
Vulnerability Type: Information disclosure (kernel memory leakage via GPIO short-read in ADM1266 GPIO accessors)
Confidence: HIGH
Affected Versions: v7.0-rc6 and earlier (up to this patch)
Code Diff
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 3bf512df30cdb4..6f904a5a8ea6b3 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -176,6 +176,8 @@ static int adm1266_gpio_get(struct gpio_chip *chip, unsigned int offset)
ret = i2c_smbus_read_block_data(data->client, pmbus_cmd, read_buf);
if (ret < 0)
return ret;
+ if (ret < 2)
+ return -EIO;
pins_status = read_buf[0] + (read_buf[1] << 8);
if (offset < ADM1266_GPIO_NR)
@@ -196,6 +198,8 @@ static int adm1266_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask
ret = i2c_smbus_read_block_data(data->client, ADM1266_GPIO_STATUS, read_buf);
if (ret < 0)
return ret;
+ if (ret < 2)
+ return -EIO;
status = read_buf[0] + (read_buf[1] << 8);
@@ -208,6 +212,8 @@ static int adm1266_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask
ret = i2c_smbus_read_block_data(data->client, ADM1266_PDIO_STATUS, read_buf);
if (ret < 0)
return ret;
+ if (ret < 2)
+ return -EIO;
status = read_buf[0] + (read_buf[1] << 8);