Hi all,
I think the Greybus audio topology parser in
drivers/staging/greybus/audio_topology.c can read past the topology blob
when a module reports inconsistent counts. I would appreciate it if you
could take a look.
gb_audio_gb_get_topology() allocates the blob to a device-reported u16 size
and only checks that it is at least sizeof(struct gb_audio_topology).
size = le16_to_cpu(size_resp.size);
if (size < sizeof(*topo))
return -ENODATA;
topo = kzalloc(size, GFP_KERNEL);
After that the parser trusts the interior fields. gb_generate_enum_strings()
walks one C string per item with no end pointer.
items = le32_to_cpu(gbenum->items);
for (i = 0; i < items; i++) {
strings[i] = data;
while (*data != '\0')
data++;
data++;
}
items is a device le32 and there is no guarantee the blob holds that many
NUL terminated strings, so the while loop runs off the end of the
allocation. The same pattern is in gbaudio_tplg_process_header(), which
derives the control, widget and route offsets by adding device le32 block
sizes with no check against the end of the blob, and in the csize cursor
advance in gbaudio_tplg_process_kcontrols() and gbaudio_tplg_create_widget().
The whole blob is device data parsed on the probe path with no privilege
needed. The attacker here is a malicious or faulty Greybus audio module on
the interface.
I reproduced the enum string walk under KASAN on 7.1-rc7. A blob with one
NUL per item stays inside the allocation. A blob with missing terminators
makes KASAN report a slab out of bounds read past the buffer.
I have a partial fix that passes the buffer end into the enum walk and stops
on overrun. A complete fix also needs to bound the header offsets and the
csize advances against the allocation size, so I wanted to ask before
sending a series.
Does this look like a real bug to you? If it does I am happy to send a
proper patch. The parser was added in c8e6336bb3f8 ("greybus: audio: Add
topology parser for GB codec"), which I think is the right Fixes tag.
Thanks,
Maoyi
https://maoyixie.com/
gb_vibrator_probe() maps any device_create() failure to -EINVAL. This
loses the real errno returned by the driver core, such as -ENOMEM, and
makes probe failures harder to diagnose correctly.
Return PTR_ERR(dev) instead so callers receive the actual failure reason
while preserving the existing cleanup path.
Signed-off-by: Alfie Varghese <alfievarghese22(a)gmail.com>
---
v2: No code changes, resending as a single properly versioned patch.
drivers/staging/greybus/vibrator.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c
index 0ec4d317c9db..763c234fbc03 100644
--- a/drivers/staging/greybus/vibrator.c
+++ b/drivers/staging/greybus/vibrator.c
@@ -161,7 +161,7 @@ static int gb_vibrator_probe(struct gb_bundle *bundle,
dev = device_create(&vibrator_class, &bundle->dev,
MKDEV(0, 0), vib, "vibrator%d", vib->minor);
if (IS_ERR(dev)) {
- retval = -EINVAL;
+ retval = PTR_ERR(dev);
goto err_ida_remove;
}
vib->dev = dev;
--
2.53.0
gb_vibrator_probe() maps any device_create() failure to -EINVAL. This
loses the real errno returned by the driver core, such as -ENOMEM, and
makes probe failures harder to diagnose correctly.
Return PTR_ERR(dev) instead so callers receive the actual failure reason
while preserving the existing cleanup path.
Signed-off-by: Alfie Varghese <alfievarghese22(a)gmail.com>
---
drivers/staging/greybus/vibrator.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/staging/greybus/vibrator.c b/drivers/staging/greybus/vibrator.c
index 0ec4d317c..763c234fb 100644
--- a/drivers/staging/greybus/vibrator.c
+++ b/drivers/staging/greybus/vibrator.c
@@ -161,7 +161,7 @@ static int gb_vibrator_probe(struct gb_bundle *bundle,
dev = device_create(&vibrator_class, &bundle->dev,
MKDEV(0, 0), vib, "vibrator%d", vib->minor);
if (IS_ERR(dev)) {
- retval = -EINVAL;
+ retval = PTR_ERR(dev);
goto err_ida_remove;
}
vib->dev = dev;
--
2.54.0.windows.1
From: Bryam Vargas <hexlabsecurity(a)proton.me>
gb_audio_gb_get_topology() fetches a topology blob of a module-supplied
size, and gbaudio_tplg_parse_data() then walks it by adding the
module-supplied size_dais, size_controls and size_widgets fields to
form the control, widget and route section offsets. Those le32 sizes
are never checked against the fetched blob, so a module reporting a
small topology size but large section sizes makes the offsets point
past the allocation, and parsing reads out of bounds.
Reject a topology whose section sizes do not fit within the fetched
size before it is parsed.
Fixes: 184992e305f1 ("greybus: audio: Add Greybus Audio Device Class Protocol helper routines")
Cc: stable(a)vger.kernel.org
Signed-off-by: Bryam Vargas <hexlabsecurity(a)proton.me>
---
I reproduced the out-of-bounds read both in-kernel under KASAN and with
a userspace AddressSanitizer model of the gbaudio_tplg_process_header()
offset walk. The topology blob is kzalloc(size) where size is
module-supplied (a u16), and process_header() forms control_offset =
&data + size_dais, widget_offset = control_offset + size_controls, etc.;
the consumers then read structs at those offsets.
- In-kernel (7.1.0-rc5 + KASAN): a 64-byte blob (header 24, so 40 bytes
available) with size_dais = 44 makes control_offset point 4 bytes
past the allocation, and reading the first control byte there trips:
BUG: KASAN: slab-out-of-bounds in ...parse_topology...
Read of size 1 at addr ...
... which belongs to the cache kmalloc-64 of size 64
The buggy address is located 4 bytes to the right of
allocated 64-byte region
The patched arm (sections rejected, -EINVAL) and an in-bounds control
arm (size_dais = 8) read cleanly with no KASAN report.
- ASan model (-m32 and -m64): size_dais = 4096 makes control_offset
point ~4 KB past the 64-byte blob - heap-buffer-overflow READ located
4056 bytes after the region, both ABIs; patched and in-bounds clean.
The source is a greybus audio module trust boundary (an attacker-supplied
or compromised module reporting a malformed topology); the access is a
read, and a large size_dais sends the offset far enough to fault. The
reproducer (kernel module + ASan model) is available on request.
---
drivers/staging/greybus/audio_gb.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/drivers/staging/greybus/audio_gb.c b/drivers/staging/greybus/audio_gb.c
index 9d8994fdb41a..144591f1a512 100644
--- a/drivers/staging/greybus/audio_gb.c
+++ b/drivers/staging/greybus/audio_gb.c
@@ -37,6 +37,19 @@ int gb_audio_gb_get_topology(struct gb_connection *connection,
return ret;
}
+ /*
+ * The size_* fields are supplied by the module and are used by
+ * gbaudio_tplg_parse_data() to compute offsets into the blob; make
+ * sure the sections fit within the fetched topology, so walking it
+ * cannot read out of bounds.
+ */
+ if ((u64)le32_to_cpu(topo->size_dais) + le32_to_cpu(topo->size_controls) +
+ le32_to_cpu(topo->size_widgets) + le32_to_cpu(topo->size_routes) >
+ size - sizeof(*topo)) {
+ kfree(topo);
+ return -EINVAL;
+ }
+
*topology = topo;
return 0;
---
base-commit: 8e65320d91cdc3b241d4b94855c88459b91abf66
change-id: 20260616-b4-disp-4352e8b0-45e86659956e
Best regards,
--
Bryam Vargas <hexlabsecurity(a)proton.me>
Smatch warns:
drivers/staging/greybus/audio_codec.c:335 gbaudio_module_update()
warn: sscanf doesn't return error codes
sscanf() returns the number of successfully matched input items, not a
negative error code. Compare the return value directly with the expected
number of conversions instead of storing it in ret as an error code.
Also remove the redundant else-if check for snd_soc_dapm_aif_out. The
widget id is validated earlier in the function, so the remaining branch
can only handle snd_soc_dapm_aif_out. This avoids a compiler warning
about a potentially uninitialized variable.
Reported-by: kernel test robot <lkp(a)intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202606140347.gGVWDnbi-lkp@intel.com/
Signed-off-by: abdelnasser hussein <abdelnasserhussein11(a)gmail.com>
---
drivers/staging/greybus/audio_codec.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c
index 720aa752e17e..6daa4e706792 100644
--- a/drivers/staging/greybus/audio_codec.c
+++ b/drivers/staging/greybus/audio_codec.c
@@ -311,8 +311,7 @@ int gbaudio_module_update(struct gbaudio_codec_info *codec,
}
/* parse dai_id from AIF widget's stream_name */
- ret = sscanf(w->sname, "%s %d %s", intf_name, &dai_id, dir);
- if (ret < 3) {
+ if (sscanf(w->sname, "%s %d %s", intf_name, &dai_id, dir) != 3) {
dev_err(codec->dev, "Error while parsing dai_id for %s\n", w->name);
return -EINVAL;
}
@@ -323,7 +322,7 @@ int gbaudio_module_update(struct gbaudio_codec_info *codec,
ret = gbaudio_module_enable_tx(codec, module, dai_id);
else
ret = gbaudio_module_disable_tx(module, dai_id);
- } else if (w->id == snd_soc_dapm_aif_out) {
+ } else {
if (enable)
ret = gbaudio_module_enable_rx(codec, module, dai_id);
else
--
2.54.0
This patch series addresses two separate issues in gbaudio_module_update()
that were previously combined in v2:
1. Fixes an improper check of the sscanf() return value (smatch warning).
2. Removes a redundant else-if check that could lead to an uninitialized
variable warning for 'ret' (reported by kernel test robot).
Changes in v3:
- Split the changes into a 2-patch series based on feedback from
Greg Kroah-Hartman.
- Assigned correct Reported-by and Closes tags to both patches.
abdelnasser hussein (2):
staging: greybus: audio_codec: fix sscanf return value check
staging: greybus: audio_codec: remove redundant else-if check
drivers/staging/greybus/audio_codec.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
--
2.54.0