From: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
To: linux-bluetooth@vger.kernel.org
Subject: [PATCH BlueZ v1 04/10] shared/gatt-client: Use util_iov helpers to build and parse PDUs
Date: Mon, 9 Nov 2026 10:44:55 -0500 [thread overview]
Message-ID: <20261109154501.1362649-5-luiz.dentz@gmail.com> (raw)
In-Reply-To: <20261109154501.1362649-1-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Use util_iov_pull_* to parse notifications, Service Changed and Read
Multiple Variable Length responses, and util_iov_push_* to build read,
write and prepare write requests. Prepare write responses are compared
with util_iov_memcmp.
Assisted-by: OpenCode:claude-sonnet-5.5
---
src/shared/gatt-client.c | 203 ++++++++++++++++++---------------------
1 file changed, 94 insertions(+), 109 deletions(-)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index a923699b74bb..7bebc50dcf9d 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -2004,14 +2004,13 @@ static void service_changed_cb(uint16_t value_handle, const uint8_t *value,
{
struct bt_gatt_client *client = user_data;
struct service_changed_op *op;
+ struct iovec iov = { (void *)value, length };
uint16_t start, end;
- if (length != 4)
+ if (!util_iov_pull_le16(&iov, &start) ||
+ !util_iov_pull_le16(&iov, &end) || iov.iov_len)
return;
- start = get_le16(value);
- end = get_le16(value + 2);
-
if (start > end) {
DBG(client,
"Service Changed received with invalid handles");
@@ -2271,6 +2270,7 @@ static void notify_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode,
void *user_data)
{
struct bt_gatt_client *client = user_data;
+ struct iovec iov = { (void *)pdu, length };
struct value_data data;
bt_gatt_client_ref(client);
@@ -2281,36 +2281,27 @@ static void notify_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode,
memset(&data, 0, sizeof(data));
if (opcode == BT_ATT_OP_HANDLE_NFY_MULT) {
- while (length >= 4) {
- data.handle = get_le16(pdu);
- length -= 2;
- pdu += 2;
-
- data.len = get_le16(pdu);
- length -= 2;
- pdu += 2;
+ while (iov.iov_len >= 4) {
+ if (!util_iov_pull_le16(&iov, &data.handle) ||
+ !util_iov_pull_le16(&iov, &data.len))
+ break;
- if (data.len > length)
- data.len = length;
+ if (data.len > iov.iov_len)
+ data.len = iov.iov_len;
- data.data = pdu;
+ data.data = util_iov_pull_mem(&iov, data.len);
+ if (!data.data)
+ break;
queue_foreach(client->notify_list, notify_handler,
&data);
-
- length -= data.len;
- pdu += data.len;
}
} else {
- if (length < 2)
+ if (!util_iov_pull_le16(&iov, &data.handle))
goto done;
- data.handle = get_le16(pdu);
- length -= 2;
- pdu += 2;
-
- data.len = length;
- data.data = pdu;
+ data.len = iov.iov_len;
+ data.data = iov.iov_base;
queue_foreach(client->notify_list, notify_handler, &data);
}
@@ -2901,6 +2892,7 @@ static void read_multiple_cb(uint8_t opcode, const void *pdu, uint16_t length,
struct read_op *op = req->data;
uint8_t att_ecode;
bool success;
+ struct iovec iov;
if ((opcode != BT_ATT_OP_READ_MULT_RSP &&
opcode != BT_ATT_OP_READ_MULT_VL_RSP) ||
@@ -2935,25 +2927,29 @@ static void read_multiple_cb(uint8_t opcode, const void *pdu, uint16_t length,
/* Keep the request alive in case a callback cancels it */
request_ref(req);
+ iov.iov_base = (void *)pdu;
+ iov.iov_len = length;
+
/* Parse response */
- while (length >= 2 && !req->removed) {
+ while (iov.iov_len >= 2 && !req->removed) {
+ const uint8_t *value;
uint16_t len;
- len = get_le16(pdu);
- length -= 2;
- pdu += 2;
+ if (!util_iov_pull_le16(&iov, &len))
+ break;
/* The Length Value Tuple List may be truncated within the
* first two octets of a tuple due to the size limits of the
* current ATT_MTU.
*/
- if (len > length)
- len = length;
+ if (len > iov.iov_len)
+ len = iov.iov_len;
- op->callback(success, att_ecode, pdu, len, op->user_data);
+ value = util_iov_pull_mem(&iov, len);
+ if (!value)
+ break;
- length -= len;
- pdu += len;
+ op->callback(success, att_ecode, value, len, op->user_data);
}
request_unref(req);
@@ -2966,6 +2962,7 @@ unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client,
bt_gatt_client_destroy_func_t destroy)
{
uint8_t *pdu = newa(uint8_t, num_handles * 2);
+ struct iovec iov = { pdu, 0 };
struct request *req;
struct read_op *op;
uint8_t opcode;
@@ -2996,13 +2993,14 @@ unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client,
req->destroy = destroy_read_op;
for (i = 0; i < num_handles; i++)
- put_le16(handles[i], pdu + (2 * i));
+ util_iov_push_le16(&iov, handles[i]);
opcode = bt_gatt_client_get_features(client) &
BT_GATT_CHRC_CLI_FEAT_EATT ? BT_ATT_OP_READ_MULT_VL_REQ :
BT_ATT_OP_READ_MULT_REQ;
- req->att_id = bt_att_send(client->att, opcode, pdu, num_handles * 2,
+ req->att_id = bt_att_send(client->att, opcode, iov.iov_base,
+ iov.iov_len,
read_multiple_cb, req,
request_unref);
if (!req->att_id) {
@@ -3039,21 +3037,13 @@ static void destroy_read_long_op(void *data)
static bool append_chunk(struct read_long_op *op, const uint8_t *data,
uint16_t len)
{
- void *buf;
-
/* Truncate if the data would exceed maximum length */
if (op->offset + len > BT_ATT_MAX_VALUE_LEN)
len = BT_ATT_MAX_VALUE_LEN - op->offset;
- buf = realloc(op->iov.iov_base, op->iov.iov_len + len);
- if (!buf)
+ if (!util_iov_append(&op->iov, data, len))
return false;
- op->iov.iov_base = buf;
-
- memcpy(op->iov.iov_base + op->iov.iov_len, data, len);
-
- op->iov.iov_len += len;
op->offset += len;
return true;
@@ -3093,14 +3083,16 @@ static void read_long_cb(uint8_t opcode, const void *pdu,
if (length >= bt_att_get_mtu(op->client->att) - 1) {
uint8_t pdu[4];
+ struct iovec iov = { pdu, 0 };
int err;
- put_le16(op->value_handle, pdu);
- put_le16(op->offset, pdu + 2);
+ util_iov_push_le16(&iov, op->value_handle);
+ util_iov_push_le16(&iov, op->offset);
err = bt_att_resend(op->client->att, req->att_id,
BT_ATT_OP_READ_BLOB_REQ,
- pdu, sizeof(pdu),
+ iov.iov_base,
+ iov.iov_len,
read_long_cb,
request_ref(req),
request_unref);
@@ -3131,7 +3123,7 @@ unsigned int bt_gatt_client_read_long_value(struct bt_gatt_client *client,
struct read_long_op *op;
uint8_t att_op;
uint8_t pdu[4];
- uint16_t pdu_len;
+ struct iovec iov = { pdu, 0 };
if (!client)
return 0;
@@ -3154,8 +3146,7 @@ unsigned int bt_gatt_client_read_long_value(struct bt_gatt_client *client,
req->data = op;
req->destroy = destroy_read_long_op;
- put_le16(value_handle, pdu);
- pdu_len = sizeof(value_handle);
+ util_iov_push_le16(&iov, value_handle);
/*
* Core v4.2, part F, section 1.3.4.4.5:
@@ -3171,15 +3162,14 @@ unsigned int bt_gatt_client_read_long_value(struct bt_gatt_client *client,
if (op->offset) {
att_op = BT_ATT_OP_READ_BLOB_REQ;
- pdu_len += sizeof(op->offset);
-
- put_le16(op->offset, pdu + 2);
+ util_iov_push_le16(&iov, op->offset);
} else {
att_op = BT_ATT_OP_READ_REQ;
}
- req->att_id = bt_att_send(client->att, att_op, pdu, pdu_len,
- read_long_cb, req, request_unref);
+ req->att_id = bt_att_send(client->att, att_op, iov.iov_base,
+ iov.iov_len, read_long_cb, req,
+ request_unref);
if (!req->att_id) {
op->destroy = NULL;
@@ -3196,6 +3186,7 @@ unsigned int bt_gatt_client_write_without_response(
bool signed_write,
const uint8_t *value, uint16_t length) {
uint8_t *pdu = newa(uint8_t, 2 + length);
+ struct iovec iov = { pdu, 0 };
struct request *req;
int security;
uint8_t op;
@@ -3215,11 +3206,11 @@ unsigned int bt_gatt_client_write_without_response(
} else
op = BT_ATT_OP_WRITE_CMD;
- put_le16(value_handle, pdu);
- memcpy(pdu + 2, value, length);
+ util_iov_push_le16(&iov, value_handle);
+ util_iov_push_mem(&iov, length, value);
- req->att_id = bt_att_send(client->att, op, pdu, 2 + length, NULL, req,
- request_unref);
+ req->att_id = bt_att_send(client->att, op, iov.iov_base, iov.iov_len,
+ NULL, req, request_unref);
if (!req->att_id) {
request_unref(req);
return 0;
@@ -3277,6 +3268,7 @@ unsigned int bt_gatt_client_write_value(struct bt_gatt_client *client,
struct request *req;
struct write_op *op;
uint8_t *pdu = newa(uint8_t, 2 + length);
+ struct iovec iov = { pdu, 0 };
if (!client)
return 0;
@@ -3296,11 +3288,11 @@ unsigned int bt_gatt_client_write_value(struct bt_gatt_client *client,
req->data = op;
req->destroy = destroy_write_op;
- put_le16(value_handle, pdu);
- memcpy(pdu + 2, value, length);
+ util_iov_push_le16(&iov, value_handle);
+ util_iov_push_mem(&iov, length, value);
req->att_id = bt_att_send(client->att, BT_ATT_OP_WRITE_REQ,
- pdu, 2 + length,
+ iov.iov_base, iov.iov_len,
write_cb, req,
request_unref);
if (!req->att_id) {
@@ -3349,22 +3341,22 @@ static void handle_next_prep_write(struct request *req)
{
struct long_write_op *op = req->data;
bool success = true;
- uint8_t *pdu;
+ struct iovec iov = {};
int err;
- pdu = malloc(op->cur_length + 4);
- if (!pdu) {
+ iov.iov_base = malloc(op->cur_length + 4);
+ if (!iov.iov_base) {
success = false;
goto done;
}
- put_le16(op->value_handle, pdu);
- put_le16(op->offset + op->index, pdu + 2);
- memcpy(pdu + 4, op->value + op->index, op->cur_length);
+ util_iov_push_le16(&iov, op->value_handle);
+ util_iov_push_le16(&iov, op->offset + op->index);
+ util_iov_push_mem(&iov, op->cur_length, op->value + op->index);
err = bt_att_resend(op->client->att, req->att_id,
BT_ATT_OP_PREP_WRITE_REQ,
- pdu, op->cur_length + 4,
+ iov.iov_base, iov.iov_len,
prepare_write_cb,
request_ref(req),
request_unref);
@@ -3373,7 +3365,7 @@ static void handle_next_prep_write(struct request *req)
success = false;
}
- free(pdu);
+ free(iov.iov_base);
/* If so far successful, then the operation should continue.
* Otherwise, there was an error and the procedure should be
@@ -3493,20 +3485,15 @@ static void prepare_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
}
if (op->reliable) {
- if (!pdu || length != (op->cur_length + 4)) {
- success = false;
- reliable_error = true;
- goto done;
- }
-
- if (get_le16(pdu) != op->value_handle ||
- get_le16(pdu + 2) != (op->offset + op->index)) {
- success = false;
- reliable_error = true;
- goto done;
- }
-
- if (memcmp(pdu + 4, op->value + op->index, op->cur_length)) {
+ struct iovec iov = { (void *)pdu, length };
+ struct iovec value = { op->value + op->index, op->cur_length };
+ uint16_t handle, offset;
+
+ if (!pdu || !util_iov_pull_le16(&iov, &handle) ||
+ !util_iov_pull_le16(&iov, &offset) ||
+ handle != op->value_handle ||
+ offset != (op->offset + op->index) ||
+ util_iov_memcmp(&iov, &value)) {
success = false;
reliable_error = true;
goto done;
@@ -3544,7 +3531,7 @@ unsigned int bt_gatt_client_write_long_value(struct bt_gatt_client *client,
{
struct request *req;
struct long_write_op *op;
- uint8_t *pdu;
+ struct iovec iov = {};
if (!client)
return 0;
@@ -3593,22 +3580,23 @@ unsigned int bt_gatt_client_write_long_value(struct bt_gatt_client *client,
return req->id;
}
- pdu = malloc(op->cur_length + 4);
- if (!pdu) {
+ iov.iov_base = malloc(op->cur_length + 4);
+ if (!iov.iov_base) {
free(op->value);
free(op);
return 0;
}
- put_le16(value_handle, pdu);
- put_le16(offset, pdu + 2);
- memcpy(pdu + 4, op->value, op->cur_length);
+ util_iov_push_le16(&iov, value_handle);
+ util_iov_push_le16(&iov, offset);
+ util_iov_push_mem(&iov, op->cur_length, op->value);
req->att_id = bt_att_send(client->att, BT_ATT_OP_PREP_WRITE_REQ,
- pdu, op->cur_length + 4,
+ iov.iov_base,
+ iov.iov_len,
prepare_write_cb, req,
request_unref);
- free(pdu);
+ free(iov.iov_base);
if (!req->att_id) {
op->destroy = NULL;
@@ -3645,6 +3633,8 @@ static void prep_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
{
struct request *req = user_data;
struct prep_write_op *op = req->data;
+ struct iovec rsp = { (void *)pdu, length };
+ struct iovec exp = { op->pdu, op->pdu_len };
bool success;
uint8_t att_ecode;
bool reliable_error;
@@ -3663,8 +3653,7 @@ static void prep_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
goto done;
}
- if (!pdu || length != op->pdu_len ||
- memcmp(pdu, op->pdu, op->pdu_len)) {
+ if (!pdu || util_iov_memcmp(&rsp, &exp)) {
success = false;
reliable_error = true;
att_ecode = 0;
@@ -3716,6 +3705,7 @@ unsigned int bt_gatt_client_prepare_write(struct bt_gatt_client *client,
struct request *req;
struct prep_write_op *op;
uint8_t *pdu = newa(uint8_t, 4 + length);
+ struct iovec iov = { pdu, 0 };
if (!client)
return 0;
@@ -3747,35 +3737,30 @@ unsigned int bt_gatt_client_prepare_write(struct bt_gatt_client *client,
req->destroy = destroy_prep_write_op;
req->prep_write = true;
- put_le16(value_handle, pdu);
- put_le16(offset, pdu + 2);
- memcpy(pdu + 4, value, length);
+ util_iov_push_le16(&iov, value_handle);
+ util_iov_push_le16(&iov, offset);
+ util_iov_push_mem(&iov, length, value);
/*
* Before sending command we need to remember pdu as we need to validate
- * it in the response. Store handle, offset and value. Therefore
- * increase length by 4 (handle + offset) as we need it in couple places
- * below
+ * it in the response. Store handle, offset and value.
*/
- length += 4;
-
- op->pdu = malloc(length);
+ op->pdu = util_memdup(iov.iov_base, iov.iov_len);
if (!op->pdu) {
op->destroy = NULL;
request_unref(req);
return 0;
}
- memcpy(op->pdu, pdu, length);
- op->pdu_len = length;
+ op->pdu_len = iov.iov_len;
/*
* Now we are ready to send command
* Note that request_unref will be done on write execute
*/
- req->att_id = bt_att_send(client->att, BT_ATT_OP_PREP_WRITE_REQ, pdu,
- length, prep_write_cb, req,
- NULL);
+ req->att_id = bt_att_send(client->att, BT_ATT_OP_PREP_WRITE_REQ,
+ iov.iov_base, iov.iov_len,
+ prep_write_cb, req, NULL);
if (!req->att_id) {
op->destroy = NULL;
request_unref(req);
--
2.56.0
next prev parent reply other threads:[~2026-11-09 15:45 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-09 15:44 [PATCH BlueZ v1 00/10] shared/gatt: Use util_iov helpers for PDU handling Luiz Augusto von Dentz
2026-11-09 15:44 ` [PATCH BlueZ v1 01/10] shared/att: Use util_iov helpers to build and parse PDUs Luiz Augusto von Dentz
2026-11-10 6:49 ` shared/gatt: Use util_iov helpers for PDU handling bluez.test.bot
2026-11-09 15:44 ` [PATCH BlueZ v1 02/10] shared/gatt-server: Use util_iov helpers to build and parse PDUs Luiz Augusto von Dentz
2026-11-09 15:44 ` [PATCH BlueZ v1 03/10] shared/gatt-helpers: " Luiz Augusto von Dentz
2026-11-09 15:44 ` Luiz Augusto von Dentz [this message]
2026-11-09 15:44 ` [PATCH BlueZ v1 05/10] unit/test-gatt: Add malformed PDU tests Luiz Augusto von Dentz
2026-11-09 15:44 ` [PATCH BlueZ v1 06/10] shared/gatt-server: Fix authorize callback getting handle 0 on write Luiz Augusto von Dentz
2026-11-09 15:44 ` [PATCH BlueZ v1 07/10] shared/gatt-server: Fix double response on prepare write failure Luiz Augusto von Dentz
2026-11-09 15:44 ` [PATCH BlueZ v1 08/10] shared/gatt-server: Fix return value of handler registration Luiz Augusto von Dentz
2026-11-09 15:45 ` [PATCH BlueZ v1 09/10] shared/att: Fix reference leak on signature verification failure Luiz Augusto von Dentz
2026-11-09 15:45 ` [PATCH BlueZ v1 10/10] shared/att: Fix signature length subtracted once per handler Luiz Augusto von Dentz
2026-11-10 16:40 ` [PATCH BlueZ v1 00/10] shared/gatt: Use util_iov helpers for PDU handling patchwork-bot+bluetooth
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=20261109154501.1362649-5-luiz.dentz@gmail.com \
--to=luiz.dentz@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