Skip to content
Snippets Groups Projects
Commit 252e1c08 authored by Adrian Bunk's avatar Adrian Bunk Committed by Dylan Aïssi
Browse files

Import Debian changes 5.55-3.1+deb11u2

parent cc759ae3
No related branches found
No related tags found
2 merge requests!50Merge changes from apertis/v2023-security into apertis/v2023,!49Update from debian/bullseye-security for apertis/v2023-security
Pipeline #760443 passed
Showing
with 1393 additions and 0 deletions
bluez (5.55-3.1+deb11u2) bullseye-security; urgency=medium
* Non-maintainer upload by the LTS Team.
* CVE-2021-3658: adapter: Fix storing discoverable setting
* CVE-2021-41229: Memory leak in the SDP protocol
* CVE-2021-43400: Use-after-free on client disconnect
* CVE-2022-0204: GATT heap overflow
* CVE-2022-39176: Proximate attackers could obtain sensitive information
* CVE-2022-39177: Proximate attackers could cause denial of service
* CVE-2023-27349: AVRCP crash while handling unsupported events.
* CVE-2023-50229: Phone Book Access profile Heap-based Buffer Overflow
* CVE-2023-50230: Phone Book Access profile Heap-based Buffer Overflow
-- Adrian Bunk <bunk@debian.org> Thu, 05 Sep 2024 19:38:29 +0300
bluez (5.55-3.1+deb11u1) bullseye-security; urgency=high
* Non-maintainer upload by the Security Team.
......
From 99e178d67b1c6cabd76604eb7fec8f3bc23c524e Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Thu, 24 Jun 2021 16:32:04 -0700
Subject: adapter: Fix storing discoverable setting
discoverable setting shall only be store when changed via Discoverable
property and not when discovery client set it as that be considered
temporary just for the lifetime of the discovery.
---
src/adapter.c | 35 ++++++++++++++++++++++-------------
1 file changed, 22 insertions(+), 13 deletions(-)
diff --git a/src/adapter.c b/src/adapter.c
index 9b97351b8..64a9665d4 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -572,7 +572,11 @@ static void settings_changed(struct btd_adapter *adapter, uint32_t settings)
if (changed_mask & MGMT_SETTING_DISCOVERABLE) {
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "Discoverable");
- store_adapter_info(adapter);
+ /* Only persist discoverable setting if it was not set
+ * temporarily by discovery.
+ */
+ if (!adapter->discovery_discoverable)
+ store_adapter_info(adapter);
btd_adv_manager_refresh(adapter->adv_manager);
}
@@ -2127,8 +2131,6 @@ static bool filters_equal(struct mgmt_cp_start_service_discovery *a,
static int update_discovery_filter(struct btd_adapter *adapter)
{
struct mgmt_cp_start_service_discovery *sd_cp;
- GSList *l;
-
DBG("");
@@ -2138,17 +2140,24 @@ static int update_discovery_filter(struct btd_adapter *adapter)
return -ENOMEM;
}
- for (l = adapter->discovery_list; l; l = g_slist_next(l)) {
- struct discovery_client *client = l->data;
+ /* Only attempt to overwrite current discoverable setting when not
+ * discoverable.
+ */
+ if (!(adapter->current_settings & MGMT_OP_SET_DISCOVERABLE)) {
+ GSList *l;
- if (!client->discovery_filter)
- continue;
+ for (l = adapter->discovery_list; l; l = g_slist_next(l)) {
+ struct discovery_client *client = l->data;
- if (client->discovery_filter->discoverable)
- break;
- }
+ if (!client->discovery_filter)
+ continue;
- set_discovery_discoverable(adapter, l ? true : false);
+ if (client->discovery_filter->discoverable) {
+ set_discovery_discoverable(adapter, true);
+ break;
+ }
+ }
+ }
/*
* If filters are equal, then don't update scan, except for when
@@ -2181,8 +2190,7 @@ static int discovery_stop(struct discovery_client *client)
return 0;
}
- if (adapter->discovery_discoverable)
- set_discovery_discoverable(adapter, false);
+ set_discovery_discoverable(adapter, false);
/*
* In the idle phase of a discovery, there is no need to stop it
@@ -6869,6 +6877,7 @@ static void adapter_stop(struct btd_adapter *adapter)
g_free(adapter->current_discovery_filter);
adapter->current_discovery_filter = NULL;
+ set_discovery_discoverable(adapter, false);
adapter->discovering = false;
while (adapter->connections) {
--
2.30.2
From 7c2727556b4c874a80d1597334464ba2e16f2a59 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Thu, 15 Jul 2021 11:01:20 -0700
Subject: sdpd: Fix leaking buffers stored in cstates cache
These buffer shall only be keep in cache for as long as they are
needed so this would cleanup any client cstates in the following
conditions:
- There is no cstate on the response
- No continuation can be found for cstate
- Different request opcode
- Respond with an error
- Client disconnect
Fixes: https://github.com/bluez/bluez/security/advisories/GHSA-3fqg-r8j5-f5xq
---
src/sdpd-request.c | 170 ++++++++++++++++++++++++++++++++-------------
src/sdpd-server.c | 20 +++---
src/sdpd.h | 3 +
unit/test-sdp.c | 2 +-
4 files changed, 135 insertions(+), 60 deletions(-)
diff --git a/src/sdpd-request.c b/src/sdpd-request.c
index deaed266f..3f325d1b1 100644
--- a/src/sdpd-request.c
+++ b/src/sdpd-request.c
@@ -55,48 +55,78 @@ typedef struct {
#define MIN(x, y) ((x) < (y)) ? (x): (y)
-typedef struct _sdp_cstate_list sdp_cstate_list_t;
+typedef struct sdp_cont_info sdp_cont_info_t;
-struct _sdp_cstate_list {
- sdp_cstate_list_t *next;
+struct sdp_cont_info {
+ int sock;
+ uint8_t opcode;
uint32_t timestamp;
sdp_buf_t buf;
};
-static sdp_cstate_list_t *cstates;
+static sdp_list_t *cstates;
-/* FIXME: should probably remove it when it's found */
-static sdp_buf_t *sdp_get_cached_rsp(sdp_cont_state_t *cstate)
+static int cstate_match(const void *data, const void *user_data)
{
- sdp_cstate_list_t *p;
+ const sdp_cont_info_t *cinfo = data;
+ const sdp_cont_state_t *cstate = user_data;
- for (p = cstates; p; p = p->next) {
- /* Check timestamp */
- if (p->timestamp != cstate->timestamp)
- continue;
+ /* Check timestamp */
+ return cinfo->timestamp - cstate->timestamp;
+}
+
+static void sdp_cont_info_free(sdp_cont_info_t *cinfo)
+{
+ if (!cinfo)
+ return;
+
+ cstates = sdp_list_remove(cstates, cinfo);
+ free(cinfo->buf.data);
+ free(cinfo);
+}
+
+static sdp_cont_info_t *sdp_get_cont_info(sdp_req_t *req,
+ sdp_cont_state_t *cstate)
+{
+ sdp_list_t *list;
+
+ list = sdp_list_find(cstates, cstate, cstate_match);
+ if (list) {
+ sdp_cont_info_t *cinfo = list->data;
- /* Check if requesting more than available */
- if (cstate->cStateValue.maxBytesSent < p->buf.data_size)
- return &p->buf;
+ if (cinfo->opcode == req->opcode)
+ return cinfo;
+
+ /* Cleanup continuation if the opcode doesn't match since its
+ * response buffer shall only be valid for the original requests
+ */
+ sdp_cont_info_free(cinfo);
+ return NULL;
}
- return 0;
+ /* Cleanup cstates if no continuation info could be found */
+ sdp_cstate_cleanup(req->sock);
+
+ return NULL;
}
-static uint32_t sdp_cstate_alloc_buf(sdp_buf_t *buf)
+static uint32_t sdp_cstate_alloc_buf(sdp_req_t *req, sdp_buf_t *buf)
{
- sdp_cstate_list_t *cstate = malloc(sizeof(sdp_cstate_list_t));
+ sdp_cont_info_t *cinfo = malloc(sizeof(sdp_cont_info_t));
uint8_t *data = malloc(buf->data_size);
memcpy(data, buf->data, buf->data_size);
- memset((char *)cstate, 0, sizeof(sdp_cstate_list_t));
- cstate->buf.data = data;
- cstate->buf.data_size = buf->data_size;
- cstate->buf.buf_size = buf->data_size;
- cstate->timestamp = sdp_get_time();
- cstate->next = cstates;
- cstates = cstate;
- return cstate->timestamp;
+ memset(cinfo, 0, sizeof(sdp_cont_info_t));
+ cinfo->buf.data = data;
+ cinfo->buf.data_size = buf->data_size;
+ cinfo->buf.buf_size = buf->data_size;
+ cinfo->timestamp = sdp_get_time();
+ cinfo->sock = req->sock;
+ cinfo->opcode = req->opcode;
+
+ cstates = sdp_list_append(cstates, cinfo);
+
+ return cinfo->timestamp;
}
/* Additional values for checking datatype (not in spec) */
@@ -287,14 +317,16 @@ static int sdp_set_cstate_pdu(sdp_buf_t *buf, sdp_cont_state_t *cstate)
return length;
}
-static int sdp_cstate_get(uint8_t *buffer, size_t len,
- sdp_cont_state_t **cstate)
+static int sdp_cstate_get(sdp_req_t *req, uint8_t *buffer, size_t len,
+ sdp_cont_state_t **cstate, sdp_cont_info_t **cinfo)
{
uint8_t cStateSize = *buffer;
SDPDBG("Continuation State size : %d", cStateSize);
if (cStateSize == 0) {
+ /* Cleanup cstates if request doesn't contain a cstate */
+ sdp_cstate_cleanup(req->sock);
*cstate = NULL;
return 0;
}
@@ -319,6 +351,8 @@ static int sdp_cstate_get(uint8_t *buffer, size_t len,
SDPDBG("Cstate TS : 0x%x", (*cstate)->timestamp);
SDPDBG("Bytes sent : %d", (*cstate)->cStateValue.maxBytesSent);
+ *cinfo = sdp_get_cont_info(req, *cstate);
+
return 0;
}
@@ -373,6 +407,7 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
uint16_t expected, actual, rsp_count = 0;
uint8_t dtd;
sdp_cont_state_t *cstate = NULL;
+ sdp_cont_info_t *cinfo = NULL;
uint8_t *pCacheBuffer = NULL;
int handleSize = 0;
uint32_t cStateId = 0;
@@ -412,9 +447,9 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
/*
* Check if continuation state exists, if yes attempt
- * to get rsp remainder from cache, else send error
+ * to get rsp remainder from continuation info, else send error
*/
- if (sdp_cstate_get(pdata, data_left, &cstate) < 0) {
+ if (sdp_cstate_get(req, pdata, data_left, &cstate, &cinfo) < 0) {
status = SDP_INVALID_SYNTAX;
goto done;
}
@@ -464,7 +499,7 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
if (rsp_count > actual) {
/* cache the rsp and generate a continuation state */
- cStateId = sdp_cstate_alloc_buf(buf);
+ cStateId = sdp_cstate_alloc_buf(req, buf);
/*
* subtract handleSize since we now send only
* a subset of handles
@@ -472,6 +507,7 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
buf->data_size -= handleSize;
} else {
/* NULL continuation state */
+ sdp_cont_info_free(cinfo);
sdp_set_cstate_pdu(buf, NULL);
}
}
@@ -481,13 +517,15 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
short lastIndex = 0;
if (cstate) {
- /*
- * Get the previous sdp_cont_state_t and obtain
- * the cached rsp
- */
- sdp_buf_t *pCache = sdp_get_cached_rsp(cstate);
- if (pCache) {
- pCacheBuffer = pCache->data;
+ if (cinfo) {
+ /* Check if requesting more than available */
+ if (cstate->cStateValue.maxBytesSent >=
+ cinfo->buf.data_size) {
+ status = SDP_INVALID_CSTATE;
+ goto done;
+ }
+
+ pCacheBuffer = cinfo->buf.data;
/* get the rsp_count from the cached buffer */
rsp_count = get_be16(pCacheBuffer);
@@ -531,6 +569,7 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
if (i == rsp_count) {
/* set "null" continuationState */
sdp_set_cstate_pdu(buf, NULL);
+ sdp_cont_info_free(cinfo);
} else {
/*
* there's more: set lastIndexSent to
@@ -553,6 +592,7 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
done:
free(cstate);
+
if (pattern)
sdp_list_free(pattern, free);
@@ -632,15 +672,21 @@ static int extract_attrs(sdp_record_t *rec, sdp_list_t *seq, sdp_buf_t *buf)
}
/* Build cstate response */
-static int sdp_cstate_rsp(sdp_cont_state_t *cstate, sdp_buf_t *buf,
- uint16_t max)
+static int sdp_cstate_rsp(sdp_cont_info_t *cinfo, sdp_cont_state_t *cstate,
+ sdp_buf_t *buf, uint16_t max)
{
- /* continuation State exists -> get from cache */
- sdp_buf_t *cache = sdp_get_cached_rsp(cstate);
+ sdp_buf_t *cache;
uint16_t sent;
- if (!cache)
+ if (!cinfo)
+ return 0;
+
+ if (cstate->cStateValue.maxBytesSent >= cinfo->buf.data_size) {
+ sdp_cont_info_free(cinfo);
return 0;
+ }
+
+ cache = &cinfo->buf;
sent = MIN(max, cache->data_size - cstate->cStateValue.maxBytesSent);
memcpy(buf->data, cache->data + cstate->cStateValue.maxBytesSent, sent);
@@ -650,8 +696,10 @@ static int sdp_cstate_rsp(sdp_cont_state_t *cstate, sdp_buf_t *buf,
SDPDBG("Response size : %d sending now : %d bytes sent so far : %d",
cache->data_size, sent, cstate->cStateValue.maxBytesSent);
- if (cstate->cStateValue.maxBytesSent == cache->data_size)
+ if (cstate->cStateValue.maxBytesSent == cache->data_size) {
+ sdp_cont_info_free(cinfo);
return sdp_set_cstate_pdu(buf, NULL);
+ }
return sdp_set_cstate_pdu(buf, cstate);
}
@@ -665,6 +713,7 @@ static int sdp_cstate_rsp(sdp_cont_state_t *cstate, sdp_buf_t *buf,
static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf)
{
sdp_cont_state_t *cstate = NULL;
+ sdp_cont_info_t *cinfo = NULL;
short cstate_size = 0;
sdp_list_t *seq = NULL;
uint8_t dtd = 0;
@@ -721,7 +770,7 @@ static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf)
* if continuation state exists, attempt
* to get rsp remainder from cache, else send error
*/
- if (sdp_cstate_get(pdata, data_left, &cstate) < 0) {
+ if (sdp_cstate_get(req, pdata, data_left, &cstate, &cinfo) < 0) {
status = SDP_INVALID_SYNTAX;
goto done;
}
@@ -750,7 +799,7 @@ static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf)
buf->buf_size -= sizeof(uint16_t);
if (cstate) {
- cstate_size = sdp_cstate_rsp(cstate, buf, max_rsp_size);
+ cstate_size = sdp_cstate_rsp(cinfo, cstate, buf, max_rsp_size);
if (!cstate_size) {
status = SDP_INVALID_CSTATE;
error("NULL cache buffer and non-NULL continuation state");
@@ -762,7 +811,7 @@ static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf)
sdp_cont_state_t newState;
memset((char *)&newState, 0, sizeof(sdp_cont_state_t));
- newState.timestamp = sdp_cstate_alloc_buf(buf);
+ newState.timestamp = sdp_cstate_alloc_buf(req, buf);
/*
* Reset the buffer size to the maximum expected and
* set the sdp_cont_state_t
@@ -806,6 +855,7 @@ static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf)
int scanned, rsp_count = 0;
sdp_list_t *pattern = NULL, *seq = NULL, *svcList;
sdp_cont_state_t *cstate = NULL;
+ sdp_cont_info_t *cinfo = NULL;
short cstate_size = 0;
uint8_t dtd = 0;
sdp_buf_t tmpbuf;
@@ -865,7 +915,7 @@ static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf)
* if continuation state exists attempt
* to get rsp remainder from cache, else send error
*/
- if (sdp_cstate_get(pdata, data_left, &cstate) < 0) {
+ if (sdp_cstate_get(req, pdata, data_left, &cstate, &cinfo) < 0) {
status = SDP_INVALID_SYNTAX;
goto done;
}
@@ -919,7 +969,7 @@ static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf)
sdp_cont_state_t newState;
memset((char *)&newState, 0, sizeof(sdp_cont_state_t));
- newState.timestamp = sdp_cstate_alloc_buf(buf);
+ newState.timestamp = sdp_cstate_alloc_buf(req, buf);
/*
* Reset the buffer size to the maximum expected and
* set the sdp_cont_state_t
@@ -930,7 +980,7 @@ static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf)
} else
cstate_size = sdp_set_cstate_pdu(buf, NULL);
} else {
- cstate_size = sdp_cstate_rsp(cstate, buf, max);
+ cstate_size = sdp_cstate_rsp(cinfo, cstate, buf, max);
if (!cstate_size) {
status = SDP_INVALID_CSTATE;
SDPDBG("Non-null continuation state, but null cache buffer");
@@ -987,6 +1037,9 @@ static void process_request(sdp_req_t *req)
status = SDP_INVALID_PDU_SIZE;
goto send_rsp;
}
+
+ req->opcode = reqhdr->pdu_id;
+
switch (reqhdr->pdu_id) {
case SDP_SVC_SEARCH_REQ:
SDPDBG("Got a svc srch req");
@@ -1033,6 +1086,8 @@ static void process_request(sdp_req_t *req)
send_rsp:
if (status) {
+ /* Cleanup cstates on error */
+ sdp_cstate_cleanup(req->sock);
rsphdr->pdu_id = SDP_ERROR_RSP;
put_be16(status, rsp.data);
rsp.data_size = sizeof(uint16_t);
@@ -1121,3 +1176,20 @@ void handle_request(int sk, uint8_t *data, int len)
process_request(&req);
}
+
+void sdp_cstate_cleanup(int sock)
+{
+ sdp_list_t *list;
+
+ /* Remove any cinfo for the client */
+ for (list = cstates; list;) {
+ sdp_cont_info_t *cinfo = list->data;
+
+ list = list->next;
+
+ if (cinfo->sock != sock)
+ continue;
+
+ sdp_cont_info_free(cinfo);
+ }
+}
diff --git a/src/sdpd-server.c b/src/sdpd-server.c
index ef35309ce..8ebd5ddb1 100644
--- a/src/sdpd-server.c
+++ b/src/sdpd-server.c
@@ -159,16 +159,12 @@ static gboolean io_session_event(GIOChannel *chan, GIOCondition cond, gpointer d
sk = g_io_channel_unix_get_fd(chan);
- if (cond & (G_IO_HUP | G_IO_ERR)) {
- sdp_svcdb_collect_all(sk);
- return FALSE;
- }
+ if (cond & (G_IO_HUP | G_IO_ERR))
+ goto cleanup;
len = recv(sk, &hdr, sizeof(sdp_pdu_hdr_t), MSG_PEEK);
- if (len < 0 || (unsigned int) len < sizeof(sdp_pdu_hdr_t)) {
- sdp_svcdb_collect_all(sk);
- return FALSE;
- }
+ if (len < 0 || (unsigned int) len < sizeof(sdp_pdu_hdr_t))
+ goto cleanup;
size = sizeof(sdp_pdu_hdr_t) + ntohs(hdr.plen);
buf = malloc(size);
@@ -181,14 +177,18 @@ static gboolean io_session_event(GIOChannel *chan, GIOCondition cond, gpointer d
* inside handle_request() in order to produce ErrorResponse.
*/
if (len <= 0) {
- sdp_svcdb_collect_all(sk);
free(buf);
- return FALSE;
+ goto cleanup;
}
handle_request(sk, buf, len);
return TRUE;
+
+cleanup:
+ sdp_svcdb_collect_all(sk);
+ sdp_cstate_cleanup(sk);
+ return FALSE;
}
static gboolean io_accept_event(GIOChannel *chan, GIOCondition cond, gpointer data)
diff --git a/src/sdpd.h b/src/sdpd.h
index 49cd98a2b..2066368e9 100644
--- a/src/sdpd.h
+++ b/src/sdpd.h
@@ -40,8 +40,11 @@ typedef struct request {
int flags;
uint8_t *buf;
int len;
+ uint8_t opcode;
} sdp_req_t;
+void sdp_cstate_cleanup(int sock);
+
void handle_internal_request(int sk, int mtu, void *data, int len);
void handle_request(int sk, uint8_t *data, int len);
diff --git a/unit/test-sdp.c b/unit/test-sdp.c
index 03501d021..24beee4f5 100644
--- a/unit/test-sdp.c
+++ b/unit/test-sdp.c
@@ -248,7 +248,7 @@ static gboolean client_handler(GIOChannel *channel, GIOCondition cond,
tester_monitor('>', 0x0000, 0x0001, buf, len);
g_assert(len > 0);
- g_assert((size_t) len == rsp_pdu->raw_size + rsp_pdu->cont_len);
+ g_assert_cmpuint(len, ==, rsp_pdu->raw_size + rsp_pdu->cont_len);
g_assert(memcmp(buf, rsp_pdu->raw_data, rsp_pdu->raw_size) == 0);
--
2.30.2
From 82691b90f88a62856c75052c8f2c6acfcfa4f601 Mon Sep 17 00:00:00 2001
From: Sebastian Urban <surban@surban.net>
Date: Sat, 12 Jun 2021 11:56:01 +0200
Subject: gatt-database: No multiple calls to AcquireWrite
This checks if an outstanding call to AcquireWrite is already in
progress. If so, the write request is placed into the queue, but
AcquireWrite is not called again. When a response to AcquireWrite is
received, acquire_write_reply sends all queued writes over the acquired
socket.
Making multiple simultaneous calls to AcquireWrite makes no sense,
as this would open multiple socket pairs and only the last returned
socket would be used for further writes.
---
src/gatt-database.c | 41 +++++++++++++++++++++++++++++++++--------
1 file changed, 33 insertions(+), 8 deletions(-)
diff --git a/src/gatt-database.c b/src/gatt-database.c
index 3a8136961..b2a44fccc 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -2386,6 +2386,26 @@ static struct pending_op *send_write(struct btd_device *device,
return NULL;
}
+static void flush_pending_write(void *data, void *user_data)
+{
+ GDBusProxy *proxy = user_data;
+ struct pending_op *op = data;
+
+ if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup_cb,
+ write_reply_cb,
+ op, pending_op_free) == TRUE)
+ return;
+
+ pending_op_free(op);
+}
+
+static void flush_pending_writes(GDBusProxy *proxy,
+ struct queue *owner_queue)
+{
+ queue_foreach(owner_queue, flush_pending_write, proxy);
+ queue_remove_all(owner_queue, NULL, NULL, NULL);
+}
+
static bool sock_hup(struct io *io, void *user_data)
{
struct external_chrc *chrc = user_data;
@@ -2496,18 +2516,19 @@ static void acquire_write_reply(DBusMessage *message, void *user_data)
chrc->write_io = sock_io_new(fd, chrc);
- if (sock_io_send(chrc->write_io, op->data.iov_base,
- op->data.iov_len) < 0)
- goto retry;
+ while ((op = queue_peek_head(chrc->pending_writes)) != NULL) {
+ if (sock_io_send(chrc->write_io, op->data.iov_base,
+ op->data.iov_len) < 0)
+ goto retry;
- gatt_db_attribute_write_result(op->attrib, op->id, 0);
+ gatt_db_attribute_write_result(op->attrib, op->id, 0);
+ pending_op_free(op);
+ }
return;
retry:
- send_write(op->device, op->attrib, chrc->proxy, NULL, op->id,
- op->data.iov_base, op->data.iov_len, 0,
- op->link_type, false, false);
+ flush_pending_writes(chrc->proxy, chrc->pending_writes);
}
static void acquire_write_setup(DBusMessageIter *iter, void *user_data)
@@ -2535,14 +2556,18 @@ static struct pending_op *acquire_write(struct external_chrc *chrc,
uint8_t link_type)
{
struct pending_op *op;
+ bool acquiring = !queue_isempty(chrc->pending_writes);
op = pending_write_new(device, chrc->pending_writes, attrib, id, value,
len, 0, link_type, false, false);
+ if (acquiring)
+ return op;
+
if (g_dbus_proxy_method_call(chrc->proxy, "AcquireWrite",
acquire_write_setup,
acquire_write_reply,
- op, pending_op_free))
+ op, NULL))
return op;
pending_op_free(op);
--
2.30.2
From a8d1cbf1fbbb9b21dcdff054eb43503861da33f9 Mon Sep 17 00:00:00 2001
From: Bernie Conrad <bernie@allthenticate.net>
Date: Tue, 28 Sep 2021 16:00:15 -0700
Subject: gatt: Fix not cleaning up when disconnected
There is a current use after free possible on a gatt server if a client
disconnects while a WriteValue call is being processed with dbus.
This patch includes the addition of a pending disconnect callback to handle
cleanup better if a disconnect occurs during a write, an acquire write
or read operation using bt_att_register_disconnect with the cb.
---
src/gatt-database.c | 128 +++++++++++++++++++++++++-------------------
1 file changed, 74 insertions(+), 54 deletions(-)
diff --git a/src/gatt-database.c b/src/gatt-database.c
index b2a44fccc..d31a82590 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -150,8 +150,9 @@ struct external_desc {
};
struct pending_op {
- struct btd_device *device;
+ struct bt_att *att;
unsigned int id;
+ unsigned int disconn_id;
uint16_t offset;
uint8_t link_type;
struct gatt_db_attribute *attrib;
@@ -926,6 +927,26 @@ static struct btd_device *att_get_device(struct bt_att *att)
return btd_adapter_find_device(adapter, &dst, dst_type);
}
+
+static void pending_op_free(void *data)
+{
+ struct pending_op *op = data;
+
+ if (op->owner_queue)
+ queue_remove(op->owner_queue, op);
+
+ bt_att_unregister_disconnect(op->att, op->disconn_id);
+ bt_att_unref(op->att);
+ free(op);
+}
+
+static void pending_disconnect_cb(int err, void *user_data)
+{
+ struct pending_op *op = user_data;
+
+ op->owner_queue = NULL;
+}
+
static struct pending_op *pending_ccc_new(struct bt_att *att,
struct gatt_db_attribute *attrib,
uint16_t value,
@@ -945,21 +966,16 @@ static struct pending_op *pending_ccc_new(struct bt_att *att,
op->data.iov_base = UINT_TO_PTR(value);
op->data.iov_len = sizeof(value);
- op->device = device;
+ op->att = bt_att_ref(att);
op->attrib = attrib;
op->link_type = link_type;
- return op;
-}
+ bt_att_register_disconnect(att,
+ pending_disconnect_cb,
+ op,
+ NULL);
-static void pending_op_free(void *data)
-{
- struct pending_op *op = data;
-
- if (op->owner_queue)
- queue_remove(op->owner_queue, op);
-
- free(op);
+ return op;
}
static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib,
@@ -2160,31 +2176,35 @@ done:
gatt_db_attribute_read_result(op->attrib, op->id, ecode, value, len);
}
-static struct pending_op *pending_read_new(struct btd_device *device,
+
+static struct pending_op *pending_read_new(struct bt_att *att,
struct queue *owner_queue,
struct gatt_db_attribute *attrib,
- unsigned int id, uint16_t offset,
- uint8_t link_type)
+ unsigned int id, uint16_t offset)
{
struct pending_op *op;
op = new0(struct pending_op, 1);
op->owner_queue = owner_queue;
- op->device = device;
+ op->att = bt_att_ref(att);
op->attrib = attrib;
op->id = id;
op->offset = offset;
- op->link_type = link_type;
+ op->link_type = bt_att_get_link_type(att);
queue_push_tail(owner_queue, op);
+ op->disconn_id = bt_att_register_disconnect(att, pending_disconnect_cb,
+ op, NULL);
+
return op;
}
static void append_options(DBusMessageIter *iter, void *user_data)
{
struct pending_op *op = user_data;
- const char *path = device_get_path(op->device);
+ struct btd_device *device = att_get_device(op->att);
+ const char *path = device_get_path(device);
struct bt_gatt_server *server;
const char *link;
uint16_t mtu;
@@ -2211,7 +2231,7 @@ static void append_options(DBusMessageIter *iter, void *user_data)
dict_append_entry(iter, "prepare-authorize", DBUS_TYPE_BOOLEAN,
&op->prep_authorize);
- server = btd_device_get_gatt_server(op->device);
+ server = btd_device_get_gatt_server(device);
mtu = bt_gatt_server_get_mtu(server);
dict_append_entry(iter, "mtu", DBUS_TYPE_UINT16, &mtu);
@@ -2234,18 +2254,16 @@ static void read_setup_cb(DBusMessageIter *iter, void *user_data)
dbus_message_iter_close_container(iter, &dict);
}
-static struct pending_op *send_read(struct btd_device *device,
+static struct pending_op *send_read(struct bt_att *att,
struct gatt_db_attribute *attrib,
GDBusProxy *proxy,
struct queue *owner_queue,
unsigned int id,
- uint16_t offset,
- uint8_t link_type)
+ uint16_t offset)
{
struct pending_op *op;
- op = pending_read_new(device, owner_queue, attrib, id, offset,
- link_type);
+ op = pending_read_new(att, owner_queue, attrib, id, offset);
if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup_cb,
read_reply_cb, op, pending_op_free) == TRUE)
@@ -2328,15 +2346,17 @@ static void write_reply_cb(DBusMessage *message, void *user_data)
}
done:
- gatt_db_attribute_write_result(op->attrib, op->id, ecode);
+ /* Make sure that only reply if the device is connected */
+ if (!bt_att_get_fd(op->att))
+ gatt_db_attribute_write_result(op->attrib, op->id, ecode);
}
-static struct pending_op *pending_write_new(struct btd_device *device,
+static struct pending_op *pending_write_new(struct bt_att *att,
struct queue *owner_queue,
struct gatt_db_attribute *attrib,
unsigned int id,
const uint8_t *value, size_t len,
- uint16_t offset, uint8_t link_type,
+ uint16_t offset,
bool is_characteristic,
bool prep_authorize)
{
@@ -2347,33 +2367,37 @@ static struct pending_op *pending_write_new(struct btd_device *device,
op->data.iov_base = (uint8_t *) value;
op->data.iov_len = len;
- op->device = device;
+ op->att = bt_att_ref(att);
op->owner_queue = owner_queue;
op->attrib = attrib;
op->id = id;
op->offset = offset;
- op->link_type = link_type;
+ op->link_type = bt_att_get_link_type(att);
op->is_characteristic = is_characteristic;
op->prep_authorize = prep_authorize;
queue_push_tail(owner_queue, op);
+ bt_att_register_disconnect(att,
+ pending_disconnect_cb,
+ op, NULL);
+
return op;
}
-static struct pending_op *send_write(struct btd_device *device,
+static struct pending_op *send_write(struct bt_att *att,
struct gatt_db_attribute *attrib,
GDBusProxy *proxy,
struct queue *owner_queue,
unsigned int id,
const uint8_t *value, size_t len,
- uint16_t offset, uint8_t link_type,
+ uint16_t offset,
bool is_characteristic,
bool prep_authorize)
{
struct pending_op *op;
- op = pending_write_new(device, owner_queue, attrib, id, value, len,
- offset, link_type, is_characteristic,
+ op = pending_write_new(att, owner_queue, attrib, id, value, len,
+ offset, is_characteristic,
prep_authorize);
if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup_cb,
@@ -2549,17 +2573,16 @@ static void acquire_write_setup(DBusMessageIter *iter, void *user_data)
}
static struct pending_op *acquire_write(struct external_chrc *chrc,
- struct btd_device *device,
+ struct bt_att *att,
struct gatt_db_attribute *attrib,
unsigned int id,
- const uint8_t *value, size_t len,
- uint8_t link_type)
+ const uint8_t *value, size_t len)
{
struct pending_op *op;
bool acquiring = !queue_isempty(chrc->pending_writes);
- op = pending_write_new(device, chrc->pending_writes, attrib, id, value,
- len, 0, link_type, false, false);
+ op = pending_write_new(att, chrc->pending_writes, attrib, id, value,
+ len, 0, false, false);
if (acquiring)
return op;
@@ -2842,8 +2865,8 @@ static void desc_read_cb(struct gatt_db_attribute *attrib,
goto fail;
}
- if (send_read(device, attrib, desc->proxy, desc->pending_reads, id,
- offset, bt_att_get_link_type(att)))
+ if (send_read(att, attrib, desc->proxy, desc->pending_reads, id,
+ offset))
return;
fail:
@@ -2874,10 +2897,9 @@ static void desc_write_cb(struct gatt_db_attribute *attrib,
if (opcode == BT_ATT_OP_PREP_WRITE_REQ) {
if (!device_is_trusted(device) && !desc->prep_authorized &&
desc->req_prep_authorization)
- send_write(device, attrib, desc->proxy,
+ send_write(att, attrib, desc->proxy,
desc->pending_writes, id, value, len,
- offset, bt_att_get_link_type(att),
- false, true);
+ offset, false, true);
else
gatt_db_attribute_write_result(attrib, id, 0);
@@ -2887,9 +2909,8 @@ static void desc_write_cb(struct gatt_db_attribute *attrib,
if (opcode == BT_ATT_OP_EXEC_WRITE_REQ)
desc->prep_authorized = false;
- if (send_write(device, attrib, desc->proxy, desc->pending_writes, id,
- value, len, offset, bt_att_get_link_type(att), false,
- false))
+ if (send_write(att, attrib, desc->proxy, desc->pending_writes, id,
+ value, len, offset, false, false))
return;
fail:
@@ -2968,8 +2989,8 @@ static void chrc_read_cb(struct gatt_db_attribute *attrib,
goto fail;
}
- if (send_read(device, attrib, chrc->proxy, chrc->pending_reads, id,
- offset, bt_att_get_link_type(att)))
+ if (send_read(att, attrib, chrc->proxy, chrc->pending_reads, id,
+ offset))
return;
fail:
@@ -3007,9 +3028,9 @@ static void chrc_write_cb(struct gatt_db_attribute *attrib,
if (opcode == BT_ATT_OP_PREP_WRITE_REQ) {
if (!device_is_trusted(device) && !chrc->prep_authorized &&
chrc->req_prep_authorization)
- send_write(device, attrib, chrc->proxy, queue,
+ send_write(att, attrib, chrc->proxy, queue,
id, value, len, offset,
- bt_att_get_link_type(att), true, true);
+ true, true);
else
gatt_db_attribute_write_result(attrib, id, 0);
@@ -3030,13 +3051,12 @@ static void chrc_write_cb(struct gatt_db_attribute *attrib,
}
if (g_dbus_proxy_get_property(chrc->proxy, "WriteAcquired", &iter)) {
- if (acquire_write(chrc, device, attrib, id, value, len,
- bt_att_get_link_type(att)))
+ if (acquire_write(chrc, att, attrib, id, value, len))
return;
}
- if (send_write(device, attrib, chrc->proxy, queue, id, value, len,
- offset, bt_att_get_link_type(att), false, false))
+ if (send_write(att, attrib, chrc->proxy, queue, id, value, len,
+ offset, false, false))
return;
fail:
--
2.30.2
From eb60882c013d6515e6eff698008e3194ca700636 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Tue, 8 Jun 2021 16:46:49 -0700
Subject: shared/gatt-server: Fix heap overflow when appending prepare writes
The code shall check if the prepare writes would append more the
allowed maximum attribute length.
Fixes https://github.com/bluez/bluez/security/advisories/GHSA-479m-xcq5-9g2q
---
src/shared/gatt-server.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index 0c25a976e..20e14bca9 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -816,6 +816,20 @@ static uint8_t authorize_req(struct bt_gatt_server *server,
server->authorize_data);
}
+static uint8_t check_length(uint16_t length, uint16_t offset)
+{
+ if (length > BT_ATT_MAX_VALUE_LEN)
+ return BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+
+ if (offset > BT_ATT_MAX_VALUE_LEN)
+ return BT_ATT_ERROR_INVALID_OFFSET;
+
+ if (length + offset > BT_ATT_MAX_VALUE_LEN)
+ return BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+
+ return 0;
+}
+
static void write_cb(struct bt_att_chan *chan, uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
@@ -846,6 +860,10 @@ static void write_cb(struct bt_att_chan *chan, uint8_t opcode, const void *pdu,
(opcode == BT_ATT_OP_WRITE_REQ) ? "Req" : "Cmd",
handle);
+ ecode = check_length(length, 0);
+ if (ecode)
+ goto error;
+
ecode = check_permissions(server, attr, BT_ATT_PERM_WRITE_MASK);
if (ecode)
goto error;
@@ -1353,6 +1371,10 @@ static void prep_write_cb(struct bt_att_chan *chan, uint8_t opcode,
util_debug(server->debug_callback, server->debug_data,
"Prep Write Req - handle: 0x%04x", handle);
+ ecode = check_length(length, offset);
+ if (ecode)
+ goto error;
+
ecode = check_permissions(server, attr, BT_ATT_PERM_WRITE_MASK);
if (ecode)
goto error;
--
2.30.2
From 9dd6b2feb6c54c4964c945d1dd7bc9da7b16164a Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Thu, 29 Apr 2021 18:18:57 -0700
Subject: avrcp: Fix not checking if params_len match number of received bytes
This makes sure the number of bytes in the params_len matches the
remaining bytes received so the code don't end up accessing invalid
memory.
---
profiles/audio/avrcp.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index d9471c083..0233d53d4 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -1916,6 +1916,14 @@ static size_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
goto err_metadata;
}
+ operands += sizeof(*pdu);
+ operand_count -= sizeof(*pdu);
+
+ if (pdu->params_len != operand_count) {
+ DBG("AVRCP PDU parameters length don't match");
+ pdu->params_len = operand_count;
+ }
+
for (handler = session->control_handlers; handler->pdu_id; handler++) {
if (handler->pdu_id == pdu->pdu_id)
break;
--
2.30.2
From 8b8f1e45b9b8219b803acb384007b45559e88a01 Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Thu, 29 Apr 2021 17:10:50 -0700
Subject: avdtp: Fix accepting invalid/malformed capabilities
Check if capabilities are valid before attempting to copy them.
---
profiles/audio/avdtp.c | 56 +++++++++++++++++++++++++++---------------
1 file changed, 36 insertions(+), 20 deletions(-)
diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 782268c08..7b309d80c 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -1261,43 +1261,53 @@ struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
return NULL;
}
-static GSList *caps_to_list(uint8_t *data, int size,
+static GSList *caps_to_list(uint8_t *data, size_t size,
struct avdtp_service_capability **codec,
gboolean *delay_reporting)
{
+ struct avdtp_service_capability *cap;
GSList *caps;
- int processed;
if (delay_reporting)
*delay_reporting = FALSE;
- for (processed = 0, caps = NULL; processed + 2 <= size;) {
- struct avdtp_service_capability *cap;
- uint8_t length, category;
+ if (size < sizeof(*cap))
+ return NULL;
+
+ for (caps = NULL; size >= sizeof(*cap);) {
+ struct avdtp_service_capability *cpy;
- category = data[0];
- length = data[1];
+ cap = (struct avdtp_service_capability *)data;
- if (processed + 2 + length > size) {
+ if (sizeof(*cap) + cap->length >= size) {
error("Invalid capability data in getcap resp");
break;
}
- cap = g_malloc(sizeof(struct avdtp_service_capability) +
- length);
- memcpy(cap, data, 2 + length);
+ if (cap->category == AVDTP_MEDIA_CODEC &&
+ cap->length < sizeof(**codec)) {
+ error("Invalid codec data in getcap resp");
+ break;
+ }
+
+ cpy = btd_malloc(sizeof(*cpy) + cap->length);
+ memcpy(cpy, cap, sizeof(*cap) + cap->length);
- processed += 2 + length;
- data += 2 + length;
+ size -= sizeof(*cap) + cap->length;
+ data += sizeof(*cap) + cap->length;
- caps = g_slist_append(caps, cap);
+ caps = g_slist_append(caps, cpy);
- if (category == AVDTP_MEDIA_CODEC &&
- length >=
- sizeof(struct avdtp_media_codec_capability))
- *codec = cap;
- else if (category == AVDTP_DELAY_REPORTING && delay_reporting)
- *delay_reporting = TRUE;
+ switch (cap->category) {
+ case AVDTP_MEDIA_CODEC:
+ if (codec)
+ *codec = cap;
+ break;
+ case AVDTP_DELAY_REPORTING:
+ if (delay_reporting)
+ *delay_reporting = TRUE;
+ break;
+ }
}
return caps;
@@ -1494,6 +1504,12 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction,
&stream->codec,
&stream->delay_reporting);
+ if (!stream->caps || !stream->codec) {
+ err = AVDTP_UNSUPPORTED_CONFIGURATION;
+ category = 0x00;
+ goto failed_stream;
+ }
+
/* Verify that the Media Transport capability's length = 0. Reject otherwise */
for (l = stream->caps; l != NULL; l = g_slist_next(l)) {
struct avdtp_service_capability *cap = l->data;
--
2.30.2
From 8d6d15edd1f6268b70b1db3db73402ea766ed4da Mon Sep 17 00:00:00 2001
From: Archie Pusaka <apusaka@chromium.org>
Date: Thu, 17 Jun 2021 08:53:34 +0800
Subject: avdtp: Fix parsing capabilities
This patch fixes size comparison and variable misassignment.
Reviewed-by: Alain Michaud <alainm@chromium.org>
Reviewed-by: Michael Sun <michaelfsun@google.com>
---
profiles/audio/avdtp.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 7b309d80c..0adf41397 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -1279,7 +1279,7 @@ static GSList *caps_to_list(uint8_t *data, size_t size,
cap = (struct avdtp_service_capability *)data;
- if (sizeof(*cap) + cap->length >= size) {
+ if (sizeof(*cap) + cap->length > size) {
error("Invalid capability data in getcap resp");
break;
}
@@ -1301,7 +1301,7 @@ static GSList *caps_to_list(uint8_t *data, size_t size,
switch (cap->category) {
case AVDTP_MEDIA_CODEC:
if (codec)
- *codec = cap;
+ *codec = cpy;
break;
case AVDTP_DELAY_REPORTING:
if (delay_reporting)
--
2.30.2
From 0f3ca0c6f99f2348f60239d3da0a2b1b080b5945 Mon Sep 17 00:00:00 2001
From: Marijn Suijten <marijn.suijten@somainline.org>
Date: Sun, 8 Aug 2021 16:35:26 +0200
Subject: audio/avrcp: Use host/network order as appropriate for
pdu->params_len
When comparing against or writing to pdu->params_len to enforce matching
length with total packet length, take into account that pdu->params_len
is in network order (big endian) while packet size (operand_count) is in
host order (usually little endian).
This silently breaks a number of AVRCP commands that perform a quick
length check based on params_len and bail if it doesn't match exactly.
Fixes: e2b0f0d8d ("avrcp: Fix not checking if params_len match number of received bytes")
---
profiles/audio/avrcp.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 0233d53d4..fe969d1ed 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -1919,9 +1919,9 @@ static size_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
operands += sizeof(*pdu);
operand_count -= sizeof(*pdu);
- if (pdu->params_len != operand_count) {
+ if (ntohs(pdu->params_len) != operand_count) {
DBG("AVRCP PDU parameters length don't match");
- pdu->params_len = operand_count;
+ pdu->params_len = htons(operand_count);
}
for (handler = session->control_handlers; handler->pdu_id; handler++) {
--
2.30.2
From f1a22ddbc7474bfd9b0b50ac6b17af249f541d2e Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Wed, 22 Mar 2023 11:34:24 -0700
Subject: avrcp: Fix crash while handling unsupported events
The following crash can be observed if the remote peer send and
unsupported event:
ERROR: AddressSanitizer: heap-use-after-free on address 0x60b000148f11
at pc 0x559644552088 bp 0x7ffe28b3c7b0 sp 0x7ffe28b3c7a0
WRITE of size 1 at 0x60b000148f11 thread T0
#0 0x559644552087 in avrcp_handle_event profiles/audio/avrcp.c:3907
#1 0x559644536c22 in control_response profiles/audio/avctp.c:939
#2 0x5596445379ab in session_cb profiles/audio/avctp.c:1108
#3 0x7fbcb3e51c43 in g_main_context_dispatch (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x55c43)
#4 0x7fbcb3ea66c7 (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0xaa6c7)
#5 0x7fbcb3e512b2 in g_main_loop_run (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x552b2)
#6 0x559644754ab6 in mainloop_run src/shared/mainloop-glib.c:66
#7 0x559644755606 in mainloop_run_with_signal src/shared/mainloop-notify.c:188
#8 0x5596445bb963 in main src/main.c:1289
#9 0x7fbcb3bafd8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#10 0x7fbcb3bafe3f in __libc_start_main_impl ../csu/libc-start.c:392
#11 0x5596444e8224 in _start (/usr/local/libexec/bluetooth/bluetoothd+0xf0224)
---
profiles/audio/avrcp.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index fe969d1ed..35481f93f 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -3846,6 +3846,12 @@ static gboolean avrcp_handle_event(struct avctp *conn, uint8_t code,
case AVRCP_EVENT_UIDS_CHANGED:
avrcp_uids_changed(session, pdu);
break;
+ default:
+ if (event > AVRCP_EVENT_LAST) {
+ warn("Unsupported event: %u", event);
+ return FALSE;
+ }
+ break;
}
session->registered_events |= (1 << event);
--
2.30.2
From 9b389d27d8b4f44838ab35210c7aaf13d4d0d2ba Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Tue, 19 Sep 2023 12:14:01 -0700
Subject: pbap: Fix not checking Primary/Secundary Counter length
Primary/Secundary Counters are supposed to be 16 bytes values, if the
server has implemented them incorrectly it may lead to the following
crash:
=================================================================
==31860==ERROR: AddressSanitizer: heap-buffer-overflow on address
0x607000001878 at pc 0x7f95a1575638 bp 0x7fff58c6bb80 sp 0x7fff58c6b328
READ of size 48 at 0x607000001878 thread T0
#0 0x7f95a1575637 in MemcmpInterceptorCommon(void*, int (*)(void const*, void const*, unsigned long), void const*, void const*, unsigned long) ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:860
#1 0x7f95a1575ba6 in __interceptor_memcmp ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:892
#2 0x7f95a1575ba6 in __interceptor_memcmp ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:887
#3 0x564df69c77a0 in read_version obexd/client/pbap.c:288
#4 0x564df69c77a0 in read_return_apparam obexd/client/pbap.c:352
#5 0x564df69c77a0 in phonebook_size_callback obexd/client/pbap.c:374
#6 0x564df69bea3c in session_terminate_transfer obexd/client/session.c:921
#7 0x564df69d56b0 in get_xfer_progress_first obexd/client/transfer.c:729
#8 0x564df698b9ee in handle_response gobex/gobex.c:1140
#9 0x564df698cdea in incoming_data gobex/gobex.c:1385
#10 0x7f95a12fdc43 in g_main_context_dispatch (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x55c43)
#11 0x7f95a13526c7 (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0xaa6c7)
#12 0x7f95a12fd2b2 in g_main_loop_run (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x552b2)
#13 0x564df6977d41 in main obexd/src/main.c:307
#14 0x7f95a10a7d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#15 0x7f95a10a7e3f in __libc_start_main_impl ../csu/libc-start.c:392
#16 0x564df6978704 in _start (/usr/local/libexec/bluetooth/obexd+0x8b704)
0x607000001878 is located 0 bytes to the right of 72-byte region [0x607000001830,0x607000001878)
allocated by thread T0 here:
#0 0x7f95a1595a37 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154
#1 0x564df69c8b6a in pbap_probe obexd/client/pbap.c:1259
---
obexd/client/pbap.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/obexd/client/pbap.c b/obexd/client/pbap.c
index 3f5665fcd..46ecaf84c 100644
--- a/obexd/client/pbap.c
+++ b/obexd/client/pbap.c
@@ -298,7 +298,7 @@ static void read_version(struct pbap_data *pbap, GObexApparam *apparam)
data = value;
}
- if (memcmp(pbap->primary, data, len)) {
+ if (len == sizeof(pbap->primary) && memcmp(pbap->primary, data, len)) {
memcpy(pbap->primary, data, len);
g_dbus_emit_property_changed(conn,
obc_session_get_path(pbap->session),
@@ -312,7 +312,8 @@ static void read_version(struct pbap_data *pbap, GObexApparam *apparam)
data = value;
}
- if (memcmp(pbap->secondary, data, len)) {
+ if (len == sizeof(pbap->secondary) &&
+ memcmp(pbap->secondary, data, len)) {
memcpy(pbap->secondary, data, len);
g_dbus_emit_property_changed(conn,
obc_session_get_path(pbap->session),
--
2.30.2
......@@ -14,3 +14,14 @@ main-Don-t-warn-for-unset-config-option.patch
shared-gatt-server-Fix-not-properly-checking-for-sec.patch
gatt-Fix-potential-buffer-out-of-bound.patch
input.conf-Change-default-of-ClassicBondedOnly.patch
0001-adapter-Fix-storing-discoverable-setting.patch
0002-sdpd-Fix-leaking-buffers-stored-in-cstates-cache.patch
0003-gatt-database-No-multiple-calls-to-AcquireWrite.patch
0004-gatt-Fix-not-cleaning-up-when-disconnected.patch
0005-shared-gatt-server-Fix-heap-overflow-when-appending-.patch
0006-avrcp-Fix-not-checking-if-params_len-match-number-of.patch
0007-avdtp-Fix-accepting-invalid-malformed-capabilities.patch
0008-avdtp-Fix-parsing-capabilities.patch
0009-audio-avrcp-Use-host-network-order-as-appropriate-fo.patch
0010-avrcp-Fix-crash-while-handling-unsupported-events.patch
0011-pbap-Fix-not-checking-Primary-Secundary-Counter-leng.patch
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment