From: Geraldo Netto <geraldonetto@gmail.com>
To: linux-bluetooth@vger.kernel.org
Cc: Geraldo Netto <geraldonetto@gmail.com>
Subject: [PATCH BlueZ 1/2] audio: harden a2dp parsers
Date: Sat, 20 Jun 2026 21:17:34 +0200 [thread overview]
Message-ID: <20260620191735.2675946-2-geraldonetto@gmail.com> (raw)
In-Reply-To: <20260620191735.2675946-1-geraldonetto@gmail.com>
---
Makefile.am | 7 +
Makefile.plugins | 1 +
profiles/audio/a2dp-helpers.c | 136 ++++++++++++++++
profiles/audio/a2dp-helpers.h | 20 +++
profiles/audio/a2dp.c | 86 +++++-----
unit/test-a2dp.c | 288 ++++++++++++++++++++++++++++++++++
6 files changed, 489 insertions(+), 49 deletions(-)
create mode 100644 profiles/audio/a2dp-helpers.c
create mode 100644 profiles/audio/a2dp-helpers.h
create mode 100644 unit/test-a2dp.c
diff --git a/Makefile.am b/Makefile.am
index 76c4ab5d4..35871cc57 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -642,6 +642,13 @@ unit_test_avdtp_SOURCES = unit/test-avdtp.c \
unit/avdtp.c unit/avdtp.h
unit_test_avdtp_LDADD = src/libshared-glib.la $(GLIB_LIBS)
+unit_tests += unit/test-a2dp
+
+unit_test_a2dp_SOURCES = unit/test-a2dp.c \
+ profiles/audio/a2dp-helpers.c \
+ profiles/audio/a2dp-helpers.h
+unit_test_a2dp_LDADD = src/libshared-glib.la $(GLIB_LIBS) $(DBUS_LIBS)
+
unit_tests += unit/test-avctp
unit_test_avctp_SOURCES = unit/test-avctp.c \
diff --git a/Makefile.plugins b/Makefile.plugins
index ac667beda..57400d877 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -27,6 +27,7 @@ builtin_modules += a2dp
builtin_sources += profiles/audio/source.h profiles/audio/source.c \
profiles/audio/sink.h profiles/audio/sink.c \
profiles/audio/a2dp.h profiles/audio/a2dp.c \
+ profiles/audio/a2dp-helpers.h profiles/audio/a2dp-helpers.c \
profiles/audio/avdtp.h profiles/audio/avdtp.c \
profiles/audio/a2dp-codecs.h
endif
diff --git a/profiles/audio/a2dp-helpers.c b/profiles/audio/a2dp-helpers.c
new file mode 100644
index 000000000..035236df6
--- /dev/null
+++ b/profiles/audio/a2dp-helpers.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#include "a2dp-helpers.h"
+
+static bool parse_hex_byte(const char **value, uint8_t *byte)
+{
+ int high;
+ int low;
+
+ if ((*value)[0] == '\0' || (*value)[1] == '\0')
+ return false;
+
+ high = g_ascii_xdigit_value((*value)[0]);
+ low = g_ascii_xdigit_value((*value)[1]);
+ if (high < 0 || low < 0)
+ return false;
+
+ *byte = high << 4 | low;
+ *value += 2;
+
+ return true;
+}
+
+static bool parse_colon(const char **value)
+{
+ if (**value != ':')
+ return false;
+
+ (*value)++;
+
+ return true;
+}
+
+static bool parse_caps(const char *value, uint8_t *caps, size_t caps_len,
+ size_t *size)
+{
+ size_t len;
+ size_t i;
+
+ if (!value || !caps || !size)
+ return false;
+
+ *size = 0;
+
+ len = strlen(value);
+ if (!len || len % 2 || len / 2 > caps_len)
+ return false;
+
+ for (i = 0; i < len; i++) {
+ if (!g_ascii_isxdigit(value[i]))
+ return false;
+ }
+
+ for (i = 0; i < len; i += 2) {
+ const char *pos = value + i;
+
+ parse_hex_byte(&pos, &caps[i / 2]);
+ }
+
+ *size = len / 2;
+
+ return true;
+}
+
+bool a2dp_parse_capabilities_array(DBusMessageIter *value,
+ uint8_t **caps, int *size)
+{
+ DBusMessageIter array;
+
+ if (!value || !caps || !size)
+ return false;
+
+ *caps = NULL;
+ *size = 0;
+
+ if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_ARRAY)
+ return false;
+
+ if (dbus_message_iter_get_element_type(value) != DBUS_TYPE_BYTE)
+ return false;
+
+ dbus_message_iter_recurse(value, &array);
+ dbus_message_iter_get_fixed_array(&array, caps, size);
+
+ return *caps && *size > 0;
+}
+
+bool a2dp_parse_persisted_endpoint(const char *value, uint8_t *type,
+ uint8_t *codec,
+ bool *delay_reporting,
+ uint8_t *caps, size_t caps_len,
+ size_t *size)
+{
+ const char *pos;
+ uint8_t delay = 0;
+
+ if (!value || !type || !codec || !delay_reporting || !size)
+ return false;
+
+ *size = 0;
+
+ pos = value;
+ if (!parse_hex_byte(&pos, type) || !parse_colon(&pos))
+ return false;
+
+ if (!parse_hex_byte(&pos, codec) || !parse_colon(&pos))
+ return false;
+
+ if (pos[0] != '\0' && pos[1] != '\0' &&
+ g_ascii_isxdigit(pos[0]) && g_ascii_isxdigit(pos[1]) &&
+ pos[2] == ':') {
+ parse_hex_byte(&pos, &delay);
+ parse_colon(&pos);
+ if (delay > 1)
+ return false;
+ }
+
+ if (!parse_caps(pos, caps, caps_len, size))
+ return false;
+
+ *delay_reporting = delay;
+
+ return true;
+}
diff --git a/profiles/audio/a2dp-helpers.h b/profiles/audio/a2dp-helpers.h
new file mode 100644
index 000000000..a5c90c516
--- /dev/null
+++ b/profiles/audio/a2dp-helpers.h
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifndef BLUEZ_A2DP_HELPERS_H
+#define BLUEZ_A2DP_HELPERS_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <dbus/dbus.h>
+
+bool a2dp_parse_capabilities_array(DBusMessageIter *value,
+ uint8_t **caps, int *size);
+bool a2dp_parse_persisted_endpoint(const char *value, uint8_t *type,
+ uint8_t *codec,
+ bool *delay_reporting,
+ uint8_t *caps, size_t caps_len,
+ size_t *size);
+
+#endif /* BLUEZ_A2DP_HELPERS_H */
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index a5e002784..0999436c5 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -52,6 +52,7 @@
#include "sink.h"
#include "source.h"
#include "a2dp.h"
+#include "a2dp-helpers.h"
#include "a2dp-codecs.h"
#include "media.h"
@@ -689,7 +690,7 @@ static void stream_state_changed(struct avdtp_stream *stream,
if (err < 0 && err != -EINPROGRESS) {
error("avdtp_start: %s (%d)", strerror(-err), -err);
finalize_setup_errno(setup, err, finalize_resume,
- NULL);
+ (GSourceFunc) NULL);
return;
}
@@ -942,7 +943,8 @@ static void endpoint_open_cb(struct a2dp_setup *setup, uint8_t error_code)
if (error_code != 0) {
setup->stream = NULL;
- finalize_setup_errno(setup, -EPERM, finalize_config, NULL);
+ finalize_setup_errno(setup, -EPERM, finalize_config,
+ (GSourceFunc) NULL);
goto done;
}
@@ -954,7 +956,7 @@ static void endpoint_open_cb(struct a2dp_setup *setup, uint8_t error_code)
error("avdtp_open %s (%d)", strerror(-err), -err);
setup->stream = NULL;
- finalize_setup_errno(setup, err, finalize_config, NULL);
+ finalize_setup_errno(setup, err, finalize_config, (GSourceFunc) NULL);
done:
setup_unref(setup);
}
@@ -974,6 +976,7 @@ static void store_remote_sep(void *data, void *user_data)
char seid[4], value[256];
struct avdtp_service_capability *service = avdtp_get_codec(sep->sep);
struct avdtp_media_codec_capability *codec;
+ uint8_t delay_reporting;
unsigned int i;
ssize_t offset;
@@ -981,12 +984,13 @@ static void store_remote_sep(void *data, void *user_data)
return;
codec = (void *) service->data;
+ delay_reporting = avdtp_get_delay_reporting(sep->sep);
sprintf(seid, "%02hhx", avdtp_get_seid(sep->sep));
offset = sprintf(value, "%02hhx:%02hhx:%02hhx:",
avdtp_get_type(sep->sep), codec->media_codec_type,
- avdtp_get_delay_reporting(sep->sep));
+ delay_reporting);
for (i = 0; i < service->length - sizeof(*codec); i++)
offset += sprintf(value + offset, "%02hhx", codec->data[i]);
@@ -1139,7 +1143,8 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
return;
setup->stream = NULL;
- finalize_setup_errno(setup, -EPERM, finalize_config, NULL);
+ finalize_setup_errno(setup, -EPERM, finalize_config,
+ (GSourceFunc) NULL);
setup_unref(setup);
return;
}
@@ -1148,7 +1153,8 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
if (ret < 0) {
error("avdtp_open %s (%d)", strerror(-ret), -ret);
setup->stream = NULL;
- finalize_setup_errno(setup, ret, finalize_config, NULL);
+ finalize_setup_errno(setup, ret, finalize_config,
+ (GSourceFunc) NULL);
}
}
@@ -1431,7 +1437,8 @@ static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep,
if (start_err < 0 && start_err != -EINPROGRESS) {
error("avdtp_start: %s (%d)", strerror(-start_err),
-start_err);
- finalize_setup_errno(setup, start_err, finalize_resume, NULL);
+ finalize_setup_errno(setup, start_err, finalize_resume,
+ (GSourceFunc) NULL);
}
return TRUE;
@@ -1483,7 +1490,8 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
start_err = avdtp_start(session, a2dp_stream->stream);
if (start_err < 0 && start_err != -EINPROGRESS) {
error("avdtp_start: %s (%d)", strerror(-start_err), -start_err);
- finalize_setup_errno(setup, start_err, finalize_suspend, NULL);
+ finalize_setup_errno(setup, start_err, finalize_suspend,
+ (GSourceFunc) NULL);
}
}
@@ -1504,7 +1512,7 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
return TRUE;
finalize_setup_errno(setup, -ECONNRESET, finalize_suspend,
- finalize_resume, NULL);
+ finalize_resume, (GSourceFunc) NULL);
return TRUE;
}
@@ -1572,7 +1580,8 @@ static gboolean a2dp_reconfigure(gpointer data)
return FALSE;
failed:
- finalize_setup_errno(setup, posix_err, finalize_config, NULL);
+ finalize_setup_errno(setup, posix_err, finalize_config,
+ (GSourceFunc) NULL);
return FALSE;
}
@@ -1648,8 +1657,8 @@ static void abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,
return;
finalize_setup_errno(setup, -ECONNRESET, finalize_suspend,
- finalize_resume,
- finalize_config, NULL);
+ finalize_resume, finalize_config,
+ (GSourceFunc) NULL);
return;
}
@@ -1901,7 +1910,8 @@ static void channel_free(void *data)
setup->chan = NULL;
setup_ref(setup);
/* Finalize pending commands before we NULL setup->session */
- finalize_setup_errno(setup, -ENOTCONN, finalize_all, NULL);
+ finalize_setup_errno(setup, -ENOTCONN, finalize_all,
+ (GSourceFunc) NULL);
avdtp_unref(setup->session);
setup->session = NULL;
setup_unref(setup);
@@ -1991,10 +2001,12 @@ static struct a2dp_sep *find_sep(struct a2dp_server *server, uint8_t type,
static int parse_properties(DBusMessageIter *props, uint8_t **caps, int *size)
{
+ *caps = NULL;
+ *size = 0;
+
while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
const char *key;
DBusMessageIter value, entry;
- int var;
dbus_message_iter_recurse(props, &entry);
dbus_message_iter_get_basic(&entry, &key);
@@ -2002,15 +2014,10 @@ static int parse_properties(DBusMessageIter *props, uint8_t **caps, int *size)
dbus_message_iter_next(&entry);
dbus_message_iter_recurse(&entry, &value);
- var = dbus_message_iter_get_arg_type(&value);
if (strcasecmp(key, "Capabilities") == 0) {
- DBusMessageIter array;
-
- if (var != DBUS_TYPE_ARRAY)
+ if (!a2dp_parse_capabilities_array(&value, caps, size))
return -EINVAL;
- dbus_message_iter_recurse(&value, &array);
- dbus_message_iter_get_fixed_array(&array, caps, size);
return 0;
}
@@ -2130,7 +2137,7 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
struct avdtp_media_codec_capability *codec;
DBusMessageIter args, props;
const char *sender, *path;
- uint8_t *caps;
+ uint8_t *caps = NULL;
int err, size = 0;
sender = dbus_message_get_sender(msg);
@@ -2371,11 +2378,10 @@ static void load_remote_sep(struct a2dp_channel *chan, GKeyFile *key_file,
for (; *seids; seids++) {
uint8_t type;
uint8_t codec;
- uint8_t delay_reporting;
+ bool delay_reporting;
GSList *l = NULL;
- char caps[256];
uint8_t data[128];
- int i, size;
+ size_t size;
if (sscanf(*seids, "%02hhx", &rseid) != 1)
continue;
@@ -2385,34 +2391,16 @@ static void load_remote_sep(struct a2dp_channel *chan, GKeyFile *key_file,
if (!value)
continue;
- /* Try loading with delay_reporting first */
- if (sscanf(value, "%02hhx:%02hhx:%02hhx:%s", &type, &codec,
- &delay_reporting, caps) != 4) {
- /* Try old format */
- if (sscanf(value, "%02hhx:%02hhx:%s", &type, &codec,
- caps) != 3) {
- warn("Unable to load Endpoint: seid %u", rseid);
- g_free(value);
- continue;
- }
- delay_reporting = false;
- }
-
- for (i = 0, size = strlen(caps); i < size; i += 2) {
- uint8_t *tmp = data + i / 2;
-
- if (sscanf(caps + i, "%02hhx", tmp) != 1) {
- warn("Unable to load Endpoint: seid %u", rseid);
- break;
- }
+ if (!a2dp_parse_persisted_endpoint(value, &type, &codec,
+ &delay_reporting, data,
+ sizeof(data), &size)) {
+ warn("Unable to load Endpoint: seid %u", rseid);
+ g_free(value);
+ continue;
}
-
g_free(value);
- if (i != size)
- continue;
-
- caps_add_codec(&l, codec, data, size / 2);
+ caps_add_codec(&l, codec, data, size);
rsep = avdtp_register_remote_sep(chan->session, rseid, type, l,
delay_reporting);
diff --git a/unit/test-a2dp.c b/unit/test-a2dp.c
new file mode 100644
index 000000000..2f7bd7bbc
--- /dev/null
+++ b/unit/test-a2dp.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#include "profiles/audio/a2dp-helpers.h"
+
+static DBusMessage *new_method_call(void)
+{
+ return dbus_message_new_method_call("org.bluez.test",
+ "/org/bluez/test",
+ "org.bluez.test",
+ "Test");
+}
+
+static void append_byte_array(DBusMessage *msg, const uint8_t *data, int size)
+{
+ DBusMessageIter iter;
+ DBusMessageIter array;
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING,
+ &array);
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &data, size);
+ dbus_message_iter_close_container(&iter, &array);
+}
+
+static void append_string_array(DBusMessage *msg)
+{
+ DBusMessageIter iter;
+ DBusMessageIter array;
+ const char *value = "not-bytes";
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING,
+ &array);
+ dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &value);
+ dbus_message_iter_close_container(&iter, &array);
+}
+
+static void test_capabilities_array_accepts_byte_array(void)
+{
+ DBusMessage *msg = new_method_call();
+ DBusMessageIter iter;
+ const uint8_t bytes[] = { 0x11, 0x22, 0x33 };
+ uint8_t *caps = NULL;
+ int size = 0;
+
+ g_assert_nonnull(msg);
+
+ append_byte_array(msg, bytes, sizeof(bytes));
+ g_assert_true(dbus_message_iter_init(msg, &iter));
+ g_assert_true(a2dp_parse_capabilities_array(&iter, &caps, &size));
+ g_assert_cmpint(size, ==, 3);
+ g_assert_nonnull(caps);
+ g_assert_cmpint(memcmp(caps, bytes, sizeof(bytes)), ==, 0);
+
+ dbus_message_unref(msg);
+}
+
+static void test_capabilities_array_rejects_wrong_element_type(void)
+{
+ DBusMessage *msg = new_method_call();
+ DBusMessageIter iter;
+ uint8_t *caps = (void *) 0x01;
+ int size = 1;
+
+ g_assert_nonnull(msg);
+
+ append_string_array(msg);
+ g_assert_true(dbus_message_iter_init(msg, &iter));
+ g_assert_false(a2dp_parse_capabilities_array(&iter, &caps, &size));
+ g_assert_null(caps);
+ g_assert_cmpint(size, ==, 0);
+
+ dbus_message_unref(msg);
+}
+
+static void test_capabilities_array_rejects_empty_array(void)
+{
+ DBusMessage *msg = new_method_call();
+ DBusMessageIter iter;
+ uint8_t *caps = (void *) 0x01;
+ int size = 1;
+
+ g_assert_nonnull(msg);
+
+ append_byte_array(msg, NULL, 0);
+ g_assert_true(dbus_message_iter_init(msg, &iter));
+ g_assert_false(a2dp_parse_capabilities_array(&iter, &caps, &size));
+ g_assert_cmpint(size, ==, 0);
+
+ dbus_message_unref(msg);
+}
+
+static void test_capabilities_array_rejects_missing_iter(void)
+{
+ uint8_t *caps = (void *) 0x01;
+ int size = 1;
+
+ g_assert_false(a2dp_parse_capabilities_array(NULL, &caps, &size));
+}
+
+static void test_capabilities_array_rejects_non_array(void)
+{
+ DBusMessage *msg = new_method_call();
+ DBusMessageIter iter;
+ const char *value = "not-array";
+ uint8_t *caps = (void *) 0x01;
+ int size = 1;
+
+ g_assert_nonnull(msg);
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &value);
+ g_assert_true(dbus_message_iter_init(msg, &iter));
+ g_assert_false(a2dp_parse_capabilities_array(&iter, &caps, &size));
+ g_assert_null(caps);
+ g_assert_cmpint(size, ==, 0);
+
+ dbus_message_unref(msg);
+}
+
+static void assert_endpoint(const char *value, uint8_t expected_type,
+ uint8_t expected_codec,
+ bool expected_delay,
+ const uint8_t *expected_caps,
+ size_t expected_size)
+{
+ uint8_t type = 0xff;
+ uint8_t codec = 0xff;
+ bool delay_reporting = true;
+ uint8_t caps[128];
+ size_t size = 0;
+
+ memset(caps, 0xa5, sizeof(caps));
+
+ g_assert_true(a2dp_parse_persisted_endpoint(value, &type, &codec,
+ &delay_reporting,
+ caps, sizeof(caps),
+ &size));
+ g_assert_cmpint(type, ==, expected_type);
+ g_assert_cmpint(codec, ==, expected_codec);
+ g_assert_cmpint(delay_reporting, ==, expected_delay);
+ g_assert_cmpuint(size, ==, expected_size);
+ g_assert_cmpint(memcmp(caps, expected_caps, expected_size), ==, 0);
+}
+
+static void test_endpoint_parser_accepts_current_format(void)
+{
+ const uint8_t caps[] = { 0x11, 0x22, 0x33 };
+
+ assert_endpoint("00:40:01:112233", 0x00, 0x40, true, caps,
+ sizeof(caps));
+}
+
+static void test_endpoint_parser_accepts_old_format(void)
+{
+ const uint8_t caps[] = { 0xaa, 0xbb };
+
+ assert_endpoint("01:02:aabb", 0x01, 0x02, false, caps, sizeof(caps));
+}
+
+static void assert_endpoint_rejected(const char *value)
+{
+ uint8_t type = 0xff;
+ uint8_t codec = 0xff;
+ bool delay_reporting = true;
+ uint8_t caps[4] = { 0xa5, 0xa5, 0xa5, 0xa5 };
+ size_t size = 7;
+
+ g_assert_false(a2dp_parse_persisted_endpoint(value, &type, &codec,
+ &delay_reporting,
+ caps, sizeof(caps),
+ &size));
+ g_assert_cmpint(caps[0], ==, 0xa5);
+ g_assert_cmpint(caps[1], ==, 0xa5);
+ g_assert_cmpint(caps[2], ==, 0xa5);
+ g_assert_cmpint(caps[3], ==, 0xa5);
+}
+
+static void test_endpoint_parser_rejects_invalid_fields(void)
+{
+ assert_endpoint_rejected(NULL);
+ assert_endpoint_rejected("");
+ assert_endpoint_rejected("00:40");
+ assert_endpoint_rejected("00:40:");
+ assert_endpoint_rejected("00:40:01:");
+ assert_endpoint_rejected("00:40:02:aabb");
+ assert_endpoint_rejected("00:40:01:aab");
+ assert_endpoint_rejected("00:40:01:aazz");
+ assert_endpoint_rejected("00:40:01:aa:bb");
+ assert_endpoint_rejected("00:40:aabb:");
+ assert_endpoint_rejected("xx:40:01:aabb");
+}
+
+static void test_endpoint_parser_rejects_missing_output_buffer(void)
+{
+ uint8_t type;
+ uint8_t codec;
+ bool delay_reporting;
+ size_t size;
+
+ g_assert_false(a2dp_parse_persisted_endpoint("00:40:01:aabb",
+ &type, &codec,
+ &delay_reporting,
+ NULL, 0, &size));
+}
+
+static void test_endpoint_parser_rejects_oversized_caps(void)
+{
+ char value[sizeof("00:40:01:") + 16];
+
+ memset(value, 'a', sizeof(value));
+ memcpy(value, "00:40:01:", strlen("00:40:01:"));
+ value[sizeof(value) - 1] = '\0';
+
+ assert_endpoint_rejected(value);
+}
+
+static void test_endpoint_parser_fuzz_cases_keep_bounds(void)
+{
+ static const char alphabet[] = "0123456789abcdefABCDEF:gZ";
+ unsigned int i;
+
+ for (i = 0; i < 4096; i++) {
+ char value[16];
+ uint8_t type;
+ uint8_t codec;
+ bool delay_reporting;
+ uint8_t caps[6] = { 0, 0, 0, 0, 0xcc, 0xdd };
+ size_t size;
+ size_t len = i % (sizeof(value) - 1);
+ size_t j;
+
+ for (j = 0; j < len; j++)
+ value[j] = alphabet[(i + j * 7) %
+ (sizeof(alphabet) - 1)];
+ value[len] = '\0';
+
+ a2dp_parse_persisted_endpoint(value, &type, &codec,
+ &delay_reporting, caps, 4,
+ &size);
+ g_assert_cmpint(caps[4], ==, 0xcc);
+ g_assert_cmpint(caps[5], ==, 0xdd);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/a2dp/capabilities/byte-array",
+ test_capabilities_array_accepts_byte_array);
+ g_test_add_func("/a2dp/capabilities/wrong-element-type",
+ test_capabilities_array_rejects_wrong_element_type);
+ g_test_add_func("/a2dp/capabilities/empty-array",
+ test_capabilities_array_rejects_empty_array);
+ g_test_add_func("/a2dp/capabilities/missing-iter",
+ test_capabilities_array_rejects_missing_iter);
+ g_test_add_func("/a2dp/capabilities/non-array",
+ test_capabilities_array_rejects_non_array);
+ g_test_add_func("/a2dp/endpoint/current-format",
+ test_endpoint_parser_accepts_current_format);
+ g_test_add_func("/a2dp/endpoint/old-format",
+ test_endpoint_parser_accepts_old_format);
+ g_test_add_func("/a2dp/endpoint/invalid-fields",
+ test_endpoint_parser_rejects_invalid_fields);
+ g_test_add_func("/a2dp/endpoint/missing-output-buffer",
+ test_endpoint_parser_rejects_missing_output_buffer);
+ g_test_add_func("/a2dp/endpoint/oversized-caps",
+ test_endpoint_parser_rejects_oversized_caps);
+ g_test_add_func("/a2dp/endpoint/fuzz-bounds",
+ test_endpoint_parser_fuzz_cases_keep_bounds);
+
+ return g_test_run();
+}
--
2.43.0
next prev parent reply other threads:[~2026-06-20 19:17 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-20 19:17 [PATCH BlueZ 0/2] audio: harden A2DP parser handling Geraldo Netto
2026-06-20 19:17 ` Geraldo Netto [this message]
2026-06-20 21:07 ` bluez.test.bot
2026-06-20 19:17 ` [PATCH BlueZ 2/2] audio: reduce a2dp parser complexity Geraldo Netto
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260620191735.2675946-2-geraldonetto@gmail.com \
--to=geraldonetto@gmail.com \
--cc=linux-bluetooth@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox