[PATCH] iio: adc: pac1934: validate the ACPI channel configuration
From: Kang Chen
Date: Tue Nov 03 2026 - 07:10:42 EST
The four _DSM calls that describe the channel configuration trust the
returned package. The first one is walked in pairs and its count is
used to index the four-element labels[], shunts[] and
active_channels[] arrays; a longer package writes past them, and an odd
count reads elements[i + 1] past the end. Non-string or non-integer
elements make the driver use union members that hold unrelated data,
including a firmware-chosen pointer for devm_kmemdup().
Validate the object type, the package count, and the type of every
element before using it. Allocate the labels with devm_kzalloc() and
reject an impossible allocation length, so the terminating NUL cannot
be written out of bounds on 32-bit systems. Reject milliohm values
that would overflow the u32 micro-ohm fields, and micro-ohm values that
do not fit in u32. Also validate the single-element bipolar and
sample-rate packages before reading them.
Fixes: 0fb528c8255b ("iio: adc: adding support for PAC193x")
Signed-off-by: Kang Chen <k.chen@xxxxxxxxxxxxxxxx>
---
drivers/iio/adc/pac1934.c | 72 ++++++++++++++++++++++++++++++++++-----
1 file changed, 63 insertions(+), 9 deletions(-)
diff --git a/drivers/iio/adc/pac1934.c b/drivers/iio/adc/pac1934.c
index 5e901ab25c8e..b0447fc813fc 100644
--- a/drivers/iio/adc/pac1934.c
+++ b/drivers/iio/adc/pac1934.c
@@ -1094,7 +1094,7 @@ static int pac1934_acpi_parse_channel_config(struct i2c_client *client,
union acpi_object *rez;
struct device *dev = &client->dev;
unsigned short bi_dir_mask;
- int idx, i;
+ int idx, i, ret = 0;
guid_t guid;
handle = ACPI_HANDLE(dev);
@@ -1105,18 +1105,44 @@ static int pac1934_acpi_parse_channel_config(struct i2c_client *client,
if (!rez)
return -EINVAL;
+ if (rez->type != ACPI_TYPE_PACKAGE ||
+ rez->package.count % 2 ||
+ rez->package.count > 2 * info->phys_channels) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
for (i = 0; i < rez->package.count; i += 2) {
+ size_t len;
+ u64 mohm;
+
+ if (rez->package.elements[i].type != ACPI_TYPE_STRING ||
+ rez->package.elements[i + 1].type != ACPI_TYPE_INTEGER) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ mohm = rez->package.elements[i + 1].integer.value;
+ if (mohm > U32_MAX / 1000) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ len = rez->package.elements[i].string.length;
+ if (len == U32_MAX) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
idx = i / 2;
- info->labels[idx] =
- devm_kmemdup(dev, rez->package.elements[i].string.pointer,
- (size_t)rez->package.elements[i].string.length + 1,
- GFP_KERNEL);
+ info->labels[idx] = devm_kzalloc(dev, len + 1, GFP_KERNEL);
if (!info->labels[idx]) {
- ACPI_FREE(rez);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto out_free;
}
- info->labels[idx][rez->package.elements[i].string.length] = '\0';
- info->shunts[idx] = rez->package.elements[i + 1].integer.value * 1000;
+ memcpy(info->labels[idx],
+ rez->package.elements[i].string.pointer, len);
+ info->shunts[idx] = mohm * 1000;
info->active_channels[idx] = (info->shunts[idx] != 0);
}
@@ -1133,7 +1159,19 @@ static int pac1934_acpi_parse_channel_config(struct i2c_client *client,
return 0;
}
+ if (rez->type != ACPI_TYPE_PACKAGE ||
+ rez->package.count > info->phys_channels) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
for (i = 0; i < rez->package.count; i++) {
+ if (rez->package.elements[i].type != ACPI_TYPE_INTEGER ||
+ rez->package.elements[i].integer.value > U32_MAX) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
idx = i;
info->shunts[idx] = rez->package.elements[i].integer.value;
info->active_channels[idx] = (info->shunts[idx] != 0);
@@ -1145,6 +1183,12 @@ static int pac1934_acpi_parse_channel_config(struct i2c_client *client,
if (!rez)
return -EINVAL;
+ if (rez->type != ACPI_TYPE_PACKAGE || rez->package.count < 1 ||
+ rez->package.elements[0].type != ACPI_TYPE_INTEGER) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
bi_dir_mask = rez->package.elements[0].integer.value;
info->bi_dir[0] = ((bi_dir_mask & (1 << 3)) | (bi_dir_mask & (1 << 7))) != 0;
info->bi_dir[1] = ((bi_dir_mask & (1 << 2)) | (bi_dir_mask & (1 << 6))) != 0;
@@ -1157,11 +1201,21 @@ static int pac1934_acpi_parse_channel_config(struct i2c_client *client,
if (!rez)
return -EINVAL;
+ if (rez->type != ACPI_TYPE_PACKAGE || rez->package.count < 1 ||
+ rez->package.elements[0].type != ACPI_TYPE_INTEGER) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
info->sample_rate_value = rez->package.elements[0].integer.value;
ACPI_FREE(rez);
return 0;
+
+out_free:
+ ACPI_FREE(rez);
+ return ret;
}
static int pac1934_fw_parse_channel_config(struct i2c_client *client,
--
2.34.1