From: Aditya Chari S <adi25charis(a)gmail.com>
gb_audio_gb_get_topology() combined three separate responsibilities
into a single call: querying the topology size, allocating a buffer
for it, and fetching the topology data into that buffer. This left
callers with no way to perform any of these steps independently, and
forced the kzalloc() allocation to live inside the protocol-layer
driver rather than the caller, as already flagged by a FIXME comment
at the call site in audio_module.c.
Split the function into two:
gb_audio_gb_get_topology_size() - queries only the topology size
gb_audio_gb_get_topology() - fetches topology data into a
caller-supplied buffer of a
given size
Update the only caller, gb_audio_probe() in audio_module.c, to query
the size first, allocate the topology buffer itself, then fetch the
data into it, freeing the buffer via the existing free_topology error
path on failure.
This resolves both the "TODO: Split into separate calls" comment
above the original function in audio_gb.c and the FIXME comment at
the call site in audio_module.c, both of which are removed as part
of this change.
No functional change in behavior for the existing probe path.
Compile-tested with W=1, sparse (C=2), and checkpatch.pl; all clean
on the three changed files (audio_gb.c, audio_module.c, audio_codec.h).
Signed-off-by: Aditya Chari S <adi25charis(a)gmail.com>
---
drivers/staging/greybus/audio_codec.h | 4 +++-
drivers/staging/greybus/audio_gb.c | 33 ++++++++++----------------
drivers/staging/greybus/audio_module.c | 21 +++++++++++-----
3 files changed, 31 insertions(+), 27 deletions(-)
diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h
index f3f7a7ec6..be5a2a86b 100644
--- a/drivers/staging/greybus/audio_codec.h
+++ b/drivers/staging/greybus/audio_codec.h
@@ -178,8 +178,10 @@ int gbaudio_register_module(struct gbaudio_module_info *module);
void gbaudio_unregister_module(struct gbaudio_module_info *module);
/* protocol related */
+int gb_audio_gb_get_topology_size(struct gb_connection *connection,
+ u16 *size);
int gb_audio_gb_get_topology(struct gb_connection *connection,
- struct gb_audio_topology **topology);
+ struct gb_audio_topology *topology, u16 size);
int gb_audio_gb_get_control(struct gb_connection *connection,
u8 control_id, u8 index,
struct gb_audio_ctl_elem_value *value);
diff --git a/drivers/staging/greybus/audio_gb.c b/drivers/staging/greybus/audio_gb.c
index 9d8994fdb..e6356643d 100644
--- a/drivers/staging/greybus/audio_gb.c
+++ b/drivers/staging/greybus/audio_gb.c
@@ -8,13 +8,10 @@
#include <linux/greybus.h>
#include "audio_codec.h"
-/* TODO: Split into separate calls */
-int gb_audio_gb_get_topology(struct gb_connection *connection,
- struct gb_audio_topology **topology)
+int gb_audio_gb_get_topology_size(struct gb_connection *connection,
+ u16 *size)
{
struct gb_audio_get_topology_size_response size_resp;
- struct gb_audio_topology *topo;
- u16 size;
int ret;
ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_TOPOLOGY_SIZE,
@@ -22,24 +19,20 @@ int gb_audio_gb_get_topology(struct gb_connection *connection,
if (ret)
return ret;
- size = le16_to_cpu(size_resp.size);
- if (size < sizeof(*topo))
- return -ENODATA;
-
- topo = kzalloc(size, GFP_KERNEL);
- if (!topo)
- return -ENOMEM;
+ *size = le16_to_cpu(size_resp.size);
- ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_TOPOLOGY, NULL, 0,
- topo, size);
- if (ret) {
- kfree(topo);
- return ret;
- }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gb_audio_gb_get_topology_size);
- *topology = topo;
+int gb_audio_gb_get_topology(struct gb_connection *connection,
+ struct gb_audio_topology *topology, u16 size)
+{
+ if (size < sizeof(*topology))
+ return -ENODATA;
- return 0;
+ return gb_operation_sync(connection, GB_AUDIO_TYPE_GET_TOPOLOGY, NULL, 0,
+ topology, size);
}
EXPORT_SYMBOL_GPL(gb_audio_gb_get_topology);
diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c
index 12c376c47..1163cf093 100644
--- a/drivers/staging/greybus/audio_module.c
+++ b/drivers/staging/greybus/audio_module.c
@@ -239,6 +239,7 @@ static int gb_audio_probe(struct gb_bundle *bundle,
struct gb_audio_manager_module_descriptor desc;
struct gbaudio_data_connection *dai, *_dai;
int ret, i;
+ u16 size;
struct gb_audio_topology *topology;
/* There should be at least one Management and one Data cport */
@@ -304,16 +305,24 @@ static int gb_audio_probe(struct gb_bundle *bundle,
}
gbmodule->dev_id = gbmodule->mgmt_connection->intf->interface_id;
- /*
- * FIXME: malloc for topology happens via audio_gb driver
- * should be done within codec driver itself
- */
- ret = gb_audio_gb_get_topology(gbmodule->mgmt_connection, &topology);
+ ret = gb_audio_gb_get_topology_size(gbmodule->mgmt_connection, &size);
if (ret) {
- dev_err(dev, "%d:Error while fetching topology\n", ret);
+ dev_err(dev, "%d:Error while fetching topology size\n", ret);
+ goto disable_connection;
+ }
+
+ topology = kzalloc(size, GFP_KERNEL);
+ if (!topology) {
+ ret = -ENOMEM;
goto disable_connection;
}
+ ret = gb_audio_gb_get_topology(gbmodule->mgmt_connection, topology, size);
+ if (ret) {
+ dev_err(dev, "%d:Error while fetching topology\n", ret);
+ goto free_topology;
+ }
+
/* process topology data */
ret = gbaudio_tplg_parse_data(gbmodule, topology);
if (ret) {
--
2.53.0
Hi,
while auditing conditional provider/header contracts, I noticed that Greybus
Arche still appears to describe a USB3613 provider world that is absent from
current mainline.
drivers/staging/greybus/Kconfig still has:
depends on USB_HSIC_USB3613 || COMPILE_TEST
and drivers/staging/greybus/arche-platform.c still conditionally includes
the USB3613 header and calls usb3613_hub_mode_ctrl() when
CONFIG_USB_HSIC_USB3613 is enabled. However, the current tree does not appear
to provide include/linux/usb/usb3613.h or a Kconfig provider for
USB_HSIC_USB3613.
I am not sending a patch yet because this is staging/hardware policy sensitive.
The possible directions seem to be:
1. restore or move the USB3613 provider/header if the hardware path is still
intended;
2. remove the stale USB3613 integration path and rely on the local stub;
3. change the Kconfig dependency to describe only current supported worlds; or
4. keep the contract if an out-of-tree provider is intentionally expected.
Could you advise which direction is expected for Arche?
This is static source/Kconfig/header analysis only. I have not tested Arche
hardware.
Thanks,
Pengpeng
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