From: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
To: linux-bluetooth@vger.kernel.org
Subject: [PATCH BlueZ v1 02/10] shared/gatt-server: Use util_iov helpers to build and parse PDUs
Date: Mon, 9 Nov 2026 10:44:53 -0500 [thread overview]
Message-ID: <20261109154501.1362649-3-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>
Parse requests with util_iov_pull_* and build responses with
util_iov_push_* instead of get_le16/put_le16/memcpy on pdu offsets, so
a short request is rejected with Invalid PDU by the pull itself.
Notification buffering and indication encoding now also fail instead of
using an unchecked allocation.
Assisted-by: OpenCode:claude-sonnet-5.5
---
src/shared/gatt-server.c | 373 +++++++++++++++++++--------------------
1 file changed, 185 insertions(+), 188 deletions(-)
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index 15944bd1478f..6796817bf324 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -43,8 +43,7 @@ struct async_read_op {
uint8_t opcode;
bool done;
uint16_t mtu;
- uint8_t *pdu;
- size_t pdu_len;
+ struct iovec rsp;
size_t value_len;
struct queue *db_data;
};
@@ -75,8 +74,7 @@ static void prep_write_data_destroy(void *user_data)
struct mnotify_data {
unsigned int id;
- uint8_t *pdu;
- uint16_t offset;
+ struct iovec pdu;
uint16_t len;
};
@@ -126,7 +124,7 @@ static void mnotify_free(struct bt_gatt_server *server)
if (server->mnotify->id)
timeout_remove(server->mnotify->id);
- free(server->mnotify->pdu);
+ free(server->mnotify->pdu.iov_base);
free(server->mnotify);
server->mnotify = NULL;
}
@@ -160,23 +158,37 @@ static void bt_gatt_server_free(struct bt_gatt_server *server)
free(server);
}
-static bool get_uuid_le(const uint8_t *uuid, size_t len, bt_uuid_t *out_uuid)
+/* Pull a 16 or 128 bits UUID taking all the remaining data in iov */
+static bool pull_uuid_le(struct iovec *iov, bt_uuid_t *out_uuid)
{
uint128_t u128;
+ uint16_t u16;
+ void *data;
- switch (len) {
+ switch (iov->iov_len) {
case 2:
- bt_uuid16_create(out_uuid, get_le16(uuid));
+ if (!util_iov_pull_le16(iov, &u16))
+ return false;
+
+ bt_uuid16_create(out_uuid, u16);
return true;
case 16:
- bswap_128(uuid, &u128.data);
+ data = util_iov_pull_mem(iov, 16);
+ if (!data)
+ return false;
+
+ bswap_128(data, &u128.data);
bt_uuid128_create(out_uuid, u128);
return true;
default:
return false;
}
+}
- return false;
+/* Remaining space in a response, where cap excludes the opcode */
+static size_t rsp_room(const struct iovec *rsp, size_t cap)
+{
+ return rsp->iov_len < cap ? cap - rsp->iov_len : 0;
}
static void attribute_read_cb(struct gatt_db_attribute *attrib, int err,
@@ -191,15 +203,12 @@ static void attribute_read_cb(struct gatt_db_attribute *attrib, int err,
static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q,
struct bt_att *att,
- uint16_t mtu, uint8_t *pdu,
- uint16_t *len)
+ uint16_t mtu,
+ struct iovec *rsp)
{
- int iter = 0;
uint16_t start_handle, end_handle;
struct iovec value;
- uint8_t data_val_len;
-
- *len = 0;
+ uint8_t data_val_len = 0;
while (queue_peek_head(q)) {
struct gatt_db_attribute *attrib = queue_pop_head(q);
@@ -222,30 +231,25 @@ static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q,
* attribute data unit. Stop the list when a different attribute
* value is seen.
*/
- if (iter == 0) {
+ if (!rsp->iov_len) {
data_val_len = MIN(MIN((unsigned int)mtu - 6, 251),
value.iov_len);
- pdu[0] = data_val_len + 4;
- iter++;
+ util_iov_push_u8(rsp, data_val_len + 4);
} else if (value.iov_len != data_val_len)
break;
/* Stop if this unit would surpass the MTU */
- if (iter + data_val_len + 4 > mtu - 1)
+ if (rsp_room(rsp, mtu - 1) < (size_t)data_val_len + 4)
break;
gatt_db_attribute_get_service_handles(attrib, &start_handle,
&end_handle);
- put_le16(start_handle, pdu + iter);
- put_le16(end_handle, pdu + iter + 2);
- memcpy(pdu + iter + 4, value.iov_base, data_val_len);
-
- iter += data_val_len + 4;
+ util_iov_push_le16(rsp, start_handle);
+ util_iov_push_le16(rsp, end_handle);
+ util_iov_push_mem(rsp, data_val_len, value.iov_base);
}
- *len = iter;
-
return true;
}
@@ -269,23 +273,21 @@ static void read_by_grp_type_cb(struct bt_att_chan *chan, uint16_t mtu,
uint16_t start, end;
bt_uuid_t type;
bt_uuid_t prim, snd;
- uint8_t *rsp_pdu = NULL;
- uint16_t rsp_len;
+ struct iovec iov = { (void *)pdu, length };
+ struct iovec rsp = {};
uint8_t ecode = 0;
uint16_t ehandle = 0;
struct queue *q = NULL;
- if (length != 6 && length != 20) {
+ if (!util_iov_pull_le16(&iov, &start) ||
+ !util_iov_pull_le16(&iov, &end) ||
+ !pull_uuid_le(&iov, &type)) {
ecode = BT_ATT_ERROR_INVALID_PDU;
goto error;
}
q = queue_new();
- start = get_le16(pdu);
- end = get_le16(pdu + 2);
- get_uuid_le(pdu + 4, length - 4, &type);
-
DBG(server, "Read By Grp Type - start: 0x%04x end: 0x%04x", start, end);
if (!start || !end) {
@@ -320,14 +322,14 @@ static void read_by_grp_type_cb(struct bt_att_chan *chan, uint16_t mtu,
goto error;
}
- rsp_pdu = malloc(mtu);
- if (!rsp_pdu) {
+ rsp.iov_base = malloc(mtu);
+ if (!rsp.iov_base) {
ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
goto error;
}
if (!encode_read_by_grp_type_rsp(server->db, q, server->att, mtu,
- rsp_pdu, &rsp_len)) {
+ &rsp)) {
ecode = BT_ATT_ERROR_UNLIKELY;
goto error;
}
@@ -335,23 +337,23 @@ static void read_by_grp_type_cb(struct bt_att_chan *chan, uint16_t mtu,
queue_destroy(q, NULL);
bt_att_chan_send_rsp(chan, BT_ATT_OP_READ_BY_GRP_TYPE_RSP,
- rsp_pdu, rsp_len);
+ rsp.iov_base, rsp.iov_len);
- free(rsp_pdu);
+ free(rsp.iov_base);
return;
error:
queue_destroy(q, NULL);
bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode);
- free(rsp_pdu);
+ free(rsp.iov_base);
}
static void async_read_op_destroy(struct async_read_op *op)
{
bt_gatt_server_unref(op->server);
queue_destroy(op->db_data, NULL);
- free(op->pdu);
+ free(op->rsp.iov_base);
free(op);
}
@@ -374,28 +376,25 @@ static void read_by_type_read_complete_cb(struct gatt_db_attribute *attr,
return;
}
- if (op->pdu_len == 0) {
+ if (!op->rsp.iov_len) {
op->value_len = MIN(MIN((unsigned int)op->mtu - 4, 253), len);
- op->pdu[0] = op->value_len + 2;
- op->pdu_len++;
+ util_iov_push_u8(&op->rsp, op->value_len + 2);
} else if (len != op->value_len) {
op->done = true;
goto done;
}
/* Stop if this would surpass the MTU */
- if (op->pdu_len + op->value_len + 2 > (unsigned int) op->mtu - 1) {
+ if (rsp_room(&op->rsp, op->mtu - 1) < op->value_len + 2) {
op->done = true;
goto done;
}
/* Encode the current value */
- put_le16(handle, op->pdu + op->pdu_len);
- memcpy(op->pdu + op->pdu_len + 2, value, op->value_len);
-
- op->pdu_len += op->value_len + 2;
+ util_iov_push_le16(&op->rsp, handle);
+ util_iov_push_mem(&op->rsp, op->value_len, value);
- if (op->pdu_len == (unsigned int) op->mtu - 1)
+ if (op->rsp.iov_len == (unsigned int) op->mtu - 1)
op->done = true;
done:
@@ -473,7 +472,7 @@ static void process_read_by_type(struct async_read_op *op)
if (op->done || !attr) {
bt_att_chan_send_rsp(op->chan, BT_ATT_OP_READ_BY_TYPE_RSP,
- op->pdu, op->pdu_len);
+ op->rsp.iov_base, op->rsp.iov_len);
async_read_op_destroy(op);
return;
}
@@ -501,22 +500,21 @@ static void read_by_type_cb(struct bt_att_chan *chan, uint16_t mtu,
struct bt_gatt_server *server = user_data;
uint16_t start, end;
bt_uuid_t type;
+ struct iovec iov = { (void *)pdu, length };
uint16_t ehandle = 0;
uint8_t ecode;
struct queue *q = NULL;
struct async_read_op *op;
- if (length != 6 && length != 20) {
+ if (!util_iov_pull_le16(&iov, &start) ||
+ !util_iov_pull_le16(&iov, &end) ||
+ !pull_uuid_le(&iov, &type)) {
ecode = BT_ATT_ERROR_INVALID_PDU;
goto error;
}
q = queue_new();
- start = get_le16(pdu);
- end = get_le16(pdu + 2);
- get_uuid_le(pdu + 4, length - 4, &type);
-
DBG(server, "Read By Type - start: 0x%04x end: 0x%04x", start, end);
if (!start || !end) {
@@ -540,8 +538,8 @@ static void read_by_type_cb(struct bt_att_chan *chan, uint16_t mtu,
op = new0(struct async_read_op, 1);
op->mtu = mtu;
- op->pdu = malloc(mtu);
- if (!op->pdu) {
+ op->rsp.iov_base = malloc(mtu);
+ if (!op->rsp.iov_base) {
free(op);
ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
goto error;
@@ -563,15 +561,13 @@ error:
static bool encode_find_info_rsp(struct gatt_db *db, struct queue *q,
uint16_t mtu,
- uint8_t *pdu, uint16_t *len)
+ struct iovec *rsp)
{
uint16_t handle;
struct gatt_db_attribute *attr;
const bt_uuid_t *type;
- int uuid_len, cur_uuid_len;
- int iter = 0;
-
- *len = 0;
+ void *uuid;
+ int uuid_len = 0, cur_uuid_len;
while (queue_peek_head(q)) {
attr = queue_pop_head(q);
@@ -582,35 +578,34 @@ static bool encode_find_info_rsp(struct gatt_db *db, struct queue *q,
cur_uuid_len = bt_uuid_len(type);
- if (iter == 0) {
+ if (!rsp->iov_len) {
switch (cur_uuid_len) {
case 2:
uuid_len = 2;
- pdu[0] = 0x01;
+ util_iov_push_u8(rsp, 0x01);
break;
case 4:
case 16:
uuid_len = 16;
- pdu[0] = 0x02;
+ util_iov_push_u8(rsp, 0x02);
break;
default:
return false;
}
-
- iter++;
} else if (cur_uuid_len != uuid_len)
break;
- if (iter + uuid_len + 2 > mtu - 1)
+ if (rsp_room(rsp, mtu - 1) < (size_t)uuid_len + 2)
break;
- put_le16(handle, pdu + iter);
- bt_uuid_to_le(type, pdu + iter + 2);
+ util_iov_push_le16(rsp, handle);
- iter += uuid_len + 2;
- }
+ uuid = util_iov_push(rsp, uuid_len);
+ if (!uuid)
+ return false;
- *len = iter;
+ bt_uuid_to_le(type, uuid);
+ }
return true;
}
@@ -621,22 +616,20 @@ static void find_info_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode,
{
struct bt_gatt_server *server = user_data;
uint16_t start, end;
- uint8_t *rsp_pdu = NULL;
- uint16_t rsp_len;
+ struct iovec iov = { (void *)pdu, length };
+ struct iovec rsp = {};
uint8_t ecode = 0;
uint16_t ehandle = 0;
struct queue *q = NULL;
- if (length != 4) {
+ if (!util_iov_pull_le16(&iov, &start) ||
+ !util_iov_pull_le16(&iov, &end) || iov.iov_len) {
ecode = BT_ATT_ERROR_INVALID_PDU;
goto error;
}
q = queue_new();
- start = get_le16(pdu);
- end = get_le16(pdu + 2);
-
DBG(server, "Find Info - start: 0x%04x end: 0x%04x", start, end);
if (!start || !end) {
@@ -658,35 +651,35 @@ static void find_info_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode,
goto error;
}
- rsp_pdu = malloc(mtu);
- if (!rsp_pdu) {
+ rsp.iov_base = malloc(mtu);
+ if (!rsp.iov_base) {
ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
goto error;
}
- if (!encode_find_info_rsp(server->db, q, mtu, rsp_pdu, &rsp_len)) {
+ if (!encode_find_info_rsp(server->db, q, mtu, &rsp)) {
ecode = BT_ATT_ERROR_UNLIKELY;
goto error;
}
- bt_att_chan_send_rsp(chan, BT_ATT_OP_FIND_INFO_RSP, rsp_pdu, rsp_len);
+ bt_att_chan_send_rsp(chan, BT_ATT_OP_FIND_INFO_RSP, rsp.iov_base,
+ rsp.iov_len);
queue_destroy(q, NULL);
- free(rsp_pdu);
+ free(rsp.iov_base);
return;
error:
bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode);
queue_destroy(q, NULL);
- free(rsp_pdu);
+ free(rsp.iov_base);
}
struct find_by_type_val_data {
- uint8_t *pdu;
- uint16_t len;
+ struct iovec rsp;
uint16_t mtu;
uint8_t ecode;
};
@@ -700,7 +693,7 @@ static void find_by_type_val_att_cb(struct gatt_db_attribute *attrib,
if (data->ecode)
return;
- if (data->len + 4 > data->mtu - 1)
+ if (rsp_room(&data->rsp, data->mtu - 1) < 4)
return;
/*
@@ -715,10 +708,8 @@ static void find_by_type_val_att_cb(struct gatt_db_attribute *attrib,
return;
}
- put_le16(handle, data->pdu + data->len);
- put_le16(end_handle, data->pdu + data->len + 2);
-
- data->len += 4;
+ util_iov_push_le16(&data->rsp, handle);
+ util_iov_push_le16(&data->rsp, end_handle);
}
static void find_by_type_val_cb(struct bt_att_chan *chan, uint16_t mtu,
@@ -727,30 +718,25 @@ static void find_by_type_val_cb(struct bt_att_chan *chan, uint16_t mtu,
{
struct bt_gatt_server *server = user_data;
uint16_t start, end, uuid16;
- struct find_by_type_val_data data;
- uint8_t *rsp_pdu = NULL;
+ struct find_by_type_val_data data = {};
+ struct iovec iov = { (void *)pdu, length };
uint16_t ehandle = 0;
bt_uuid_t uuid;
- if (length < 6) {
+ if (!util_iov_pull_le16(&iov, &start) ||
+ !util_iov_pull_le16(&iov, &end) ||
+ !util_iov_pull_le16(&iov, &uuid16)) {
data.ecode = BT_ATT_ERROR_INVALID_PDU;
goto error;
}
- rsp_pdu = malloc(mtu);
- if (!rsp_pdu) {
+ data.rsp.iov_base = malloc(mtu);
+ if (!data.rsp.iov_base) {
data.ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
goto error;
}
- data.pdu = rsp_pdu;
- data.len = 0;
data.mtu = mtu;
- data.ecode = 0;
-
- start = get_le16(pdu);
- end = get_le16(pdu + 2);
- uuid16 = get_le16(pdu + 4);
DBG(server,
"Find By Type Value - start: 0x%04x end: 0x%04x uuid: 0x%04x",
@@ -763,27 +749,28 @@ static void find_by_type_val_cb(struct bt_att_chan *chan, uint16_t mtu,
}
bt_uuid16_create(&uuid, uuid16);
- gatt_db_find_by_type_value(server->db, start, end, &uuid, pdu + 6,
- length - 6,
+ gatt_db_find_by_type_value(server->db, start, end, &uuid,
+ iov.iov_base,
+ iov.iov_len,
find_by_type_val_att_cb,
&data);
- if (!data.len)
+ if (!data.rsp.iov_len)
data.ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND;
if (data.ecode)
goto error;
bt_att_chan_send_rsp(chan, BT_ATT_OP_FIND_BY_TYPE_RSP,
- data.pdu, data.len);
+ data.rsp.iov_base, data.rsp.iov_len);
- free(rsp_pdu);
+ free(data.rsp.iov_base);
return;
error:
bt_att_chan_send_error_rsp(chan, opcode, ehandle, data.ecode);
- free(rsp_pdu);
+ free(data.rsp.iov_base);
}
static void async_write_op_destroy(struct async_write_op *op)
@@ -846,11 +833,12 @@ static void write_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode,
{
struct bt_gatt_server *server = user_data;
struct gatt_db_attribute *attr;
+ struct iovec iov = { (void *)pdu, length };
uint16_t handle = 0;
struct async_write_op *op = NULL;
uint8_t ecode;
- if (length < 2) {
+ if (iov.iov_len < sizeof(handle)) {
ecode = BT_ATT_ERROR_INVALID_PDU;
goto error;
}
@@ -859,7 +847,8 @@ static void write_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode,
if (ecode)
goto error;
- handle = get_le16(pdu);
+ util_iov_pull_le16(&iov, &handle);
+
attr = gatt_db_get_attribute(server->db, handle);
if (!attr) {
ecode = BT_ATT_ERROR_INVALID_HANDLE;
@@ -869,7 +858,7 @@ static void write_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode,
DBG(server, "Write %s - handle: 0x%04x",
(opcode == BT_ATT_OP_WRITE_REQ) ? "Req" : "Cmd", handle);
- ecode = check_length(length - 2, 0);
+ ecode = check_length(iov.iov_len, 0);
if (ecode)
goto error;
@@ -882,7 +871,8 @@ static void write_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode,
op->server = bt_gatt_server_ref(server);
op->opcode = opcode;
- if (gatt_db_attribute_write(attr, 0, pdu + 2, length - 2, opcode,
+ if (gatt_db_attribute_write(attr, 0, iov.iov_base,
+ iov.iov_len, opcode,
server->att,
write_complete_cb, op))
return;
@@ -996,16 +986,15 @@ static void read_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode,
const void *pdu, uint16_t length, void *user_data)
{
struct bt_gatt_server *server = user_data;
+ struct iovec iov = { (void *)pdu, length };
uint16_t handle;
- if (length != 2) {
+ if (!util_iov_pull_le16(&iov, &handle) || iov.iov_len) {
bt_att_chan_send_error_rsp(chan, opcode, 0,
BT_ATT_ERROR_INVALID_PDU);
return;
}
- handle = get_le16(pdu);
-
handle_read_req(chan, server, mtu, opcode, handle, 0);
}
@@ -1014,17 +1003,16 @@ static void read_blob_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode,
void *user_data)
{
struct bt_gatt_server *server = user_data;
+ struct iovec iov = { (void *)pdu, length };
uint16_t handle, offset;
- if (length != 4) {
+ if (!util_iov_pull_le16(&iov, &handle) ||
+ !util_iov_pull_le16(&iov, &offset) || iov.iov_len) {
bt_att_chan_send_error_rsp(chan, opcode, 0,
BT_ATT_ERROR_INVALID_PDU);
return;
}
- handle = get_le16(pdu);
- offset = get_le16(pdu + 2);
-
handle_read_req(chan, server, mtu, opcode, handle, offset);
}
@@ -1035,15 +1023,14 @@ struct read_mult_data {
uint16_t *handles;
size_t cur_handle;
size_t num_handles;
- uint8_t *rsp_data;
- size_t length;
+ struct iovec rsp;
size_t mtu;
};
static void read_mult_data_free(struct read_mult_data *data)
{
free(data->handles);
- free(data->rsp_data);
+ free(data->rsp.iov_base);
free(data);
}
@@ -1055,36 +1042,36 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
struct gatt_db_attribute *next_attr;
uint16_t handle = gatt_db_attribute_get_handle(attr);
uint8_t ecode;
- uint16_t length;
+ size_t room;
if (err != 0) {
ecode = err;
goto error;
}
- length = data->opcode == BT_ATT_OP_READ_MULT_VL_REQ ?
- MIN(len, MAX(data->mtu - data->length, 3) - 3) :
- MIN(len, data->mtu - data->length - 1);
+ room = rsp_room(&data->rsp, data->mtu - 1);
if (data->opcode == BT_ATT_OP_READ_MULT_VL_REQ) {
/* 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, but the first two octets cannot be separated.
*/
- if (data->mtu - data->length >= 3) {
- put_le16(len, data->rsp_data + data->length);
- data->length += 2;
- }
+ if (room >= 2) {
+ util_iov_push_le16(&data->rsp, len);
+ room -= 2;
+ } else
+ room = 0;
}
- memcpy(data->rsp_data + data->length, value, length);
- data->length += length;
+ if (len && room)
+ util_iov_push_mem(&data->rsp, MIN(len, room), value);
data->cur_handle++;
if (data->cur_handle == data->num_handles) {
bt_att_chan_send_rsp(data->chan, data->opcode + 1,
- data->rsp_data, data->length);
+ data->rsp.iov_base,
+ data->rsp.iov_len);
read_mult_data_free(data);
return;
}
@@ -1135,13 +1122,11 @@ static struct read_mult_data *read_mult_data_new(struct bt_gatt_server *server,
data->chan = chan;
data->opcode = opcode;
data->handles = new0(uint16_t, num_handles);
- data->rsp_data = NULL;
data->server = server;
data->num_handles = num_handles;
data->cur_handle = 0;
data->mtu = mtu;
- data->length = 0;
- data->rsp_data = new0(uint8_t, data->mtu - 1);
+ data->rsp.iov_base = new0(uint8_t, data->mtu - 1);
return data;
}
@@ -1153,6 +1138,7 @@ static void read_multiple_cb(struct bt_att_chan *chan, uint16_t mtu,
struct bt_gatt_server *server = user_data;
struct gatt_db_attribute *attr;
struct read_mult_data *data = NULL;
+ struct iovec iov = { (void *)pdu, length };
uint8_t ecode;
size_t i = 0;
uint16_t handle = 0;
@@ -1164,8 +1150,12 @@ static void read_multiple_cb(struct bt_att_chan *chan, uint16_t mtu,
data = read_mult_data_new(server, chan, mtu, opcode, length / 2);
- for (i = 0; i < data->num_handles; i++)
- data->handles[i] = get_le16(pdu + i * 2);
+ for (i = 0; i < data->num_handles; i++) {
+ if (!util_iov_pull_le16(&iov, &data->handles[i])) {
+ ecode = BT_ATT_ERROR_INVALID_PDU;
+ goto error;
+ }
+ }
handle = data->handles[0];
@@ -1298,10 +1288,19 @@ static void prep_write_complete_cb(struct gatt_db_attribute *attr, int err,
void *user_data)
{
struct prep_write_complete_data *pwcd = user_data;
+ struct iovec iov = { pwcd->pdu, pwcd->length };
uint16_t handle = 0;
uint16_t offset;
- handle = get_le16(pwcd->pdu);
+ if (!util_iov_pull_le16(&iov, &handle) ||
+ !util_iov_pull_le16(&iov, &offset)) {
+ bt_att_chan_send_error_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_REQ,
+ handle, BT_ATT_ERROR_INVALID_PDU);
+ free(pwcd->pdu);
+ free(pwcd);
+
+ return;
+ }
if (err) {
bt_att_chan_send_error_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_REQ,
@@ -1312,10 +1311,8 @@ static void prep_write_complete_cb(struct gatt_db_attribute *attr, int err,
return;
}
- offset = get_le16(pwcd->pdu + 2);
-
- if (!store_prep_data(pwcd->server, handle, offset, pwcd->length - 4,
- &((uint8_t *) pwcd->pdu)[4]))
+ if (!store_prep_data(pwcd->server, handle, offset, iov.iov_len,
+ iov.iov_base))
bt_att_chan_send_error_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_RSP,
handle,
BT_ATT_ERROR_INSUFFICIENT_RESOURCES);
@@ -1336,9 +1333,10 @@ static void prep_write_cb(struct bt_att_chan *chan, uint16_t mtu,
uint16_t offset;
struct gatt_db_attribute *attr;
struct prep_write_complete_data *pwcd;
+ struct iovec iov = { (void *)pdu, length };
uint8_t ecode, status;
- if (length < 4) {
+ if (iov.iov_len < sizeof(handle) + sizeof(offset)) {
ecode = BT_ATT_ERROR_INVALID_PDU;
goto error;
}
@@ -1348,8 +1346,8 @@ static void prep_write_cb(struct bt_att_chan *chan, uint16_t mtu,
goto error;
}
- handle = get_le16(pdu);
- offset = get_le16(pdu + 2);
+ util_iov_pull_le16(&iov, &handle);
+ util_iov_pull_le16(&iov, &offset);
attr = gatt_db_get_attribute(server->db, handle);
if (!attr) {
@@ -1359,7 +1357,7 @@ static void prep_write_cb(struct bt_att_chan *chan, uint16_t mtu,
DBG(server, "Prep Write Req - handle: 0x%04x", handle);
- ecode = check_length(length - 4, offset);
+ ecode = check_length(iov.iov_len, offset);
if (ecode)
goto error;
@@ -1369,8 +1367,7 @@ static void prep_write_cb(struct bt_att_chan *chan, uint16_t mtu,
pwcd = new0(struct prep_write_complete_data, 1);
pwcd->chan = chan;
- pwcd->pdu = malloc(length);
- memcpy(pwcd->pdu, pdu, length);
+ pwcd->pdu = util_memdup(pdu, length);
pwcd->length = length;
pwcd->server = server;
@@ -1465,18 +1462,17 @@ static void exec_write_cb(struct bt_att_chan *chan, uint16_t mtu,
{
struct bt_gatt_server *server = user_data;
struct exec_data *data;
+ struct iovec iov = { (void *)pdu, length };
uint8_t flags;
uint8_t ecode;
bool write;
uint16_t ehandle = 0;
- if (length != 1) {
+ if (!util_iov_pull_u8(&iov, &flags) || iov.iov_len) {
ecode = BT_ATT_ERROR_INVALID_PDU;
goto error;
}
- flags = ((uint8_t *) pdu)[0];
-
DBG(server, "Exec Write Req - flags: 0x%02x", flags);
if (flags == 0x00)
@@ -1527,22 +1523,24 @@ static void exchange_mtu_cb(struct bt_att_chan *chan, uint16_t mtu,
uint16_t length, void *user_data)
{
struct bt_gatt_server *server = user_data;
+ struct iovec iov = { (void *)pdu, length };
+ uint8_t rsp_pdu[2];
+ struct iovec rsp = { rsp_pdu, 0 };
uint16_t client_rx_mtu;
uint16_t final_mtu;
- uint8_t rsp_pdu[2];
- if (length != 2) {
+ if (!util_iov_pull_le16(&iov, &client_rx_mtu) || iov.iov_len) {
bt_att_chan_send_error_rsp(chan, opcode, 0,
BT_ATT_ERROR_INVALID_PDU);
return;
}
- client_rx_mtu = get_le16(pdu);
final_mtu = MAX(MIN(client_rx_mtu, server->mtu), BT_ATT_DEFAULT_LE_MTU);
/* Respond with the server MTU */
- put_le16(server->mtu, rsp_pdu);
- bt_att_chan_send_rsp(chan, BT_ATT_OP_MTU_RSP, rsp_pdu, 2);
+ util_iov_push_le16(&rsp, server->mtu);
+ bt_att_chan_send_rsp(chan, BT_ATT_OP_MTU_RSP, rsp.iov_base,
+ rsp.iov_len);
/* Set MTU to be the minimum */
server->mtu = final_mtu;
@@ -1771,8 +1769,8 @@ static bool mnotify_send(void *user_data)
server->mnotify->id = 0;
bt_att_send(server->att, BT_ATT_OP_HANDLE_NFY_MULT,
- server->mnotify->pdu, server->mnotify->offset, NULL,
- NULL, NULL);
+ server->mnotify->pdu.iov_base,
+ server->mnotify->pdu.iov_len, NULL, NULL, NULL);
mnotify_free(server);
@@ -1798,13 +1796,10 @@ bool bt_gatt_server_set_mnotify_timeout(struct bt_gatt_server *server,
static bool notify_append_le16(struct mnotify_data *data, uint16_t value)
{
- if (data->offset + sizeof(value) > data->len)
+ if (rsp_room(&data->pdu, data->len) < sizeof(value))
return false;
- put_le16(value, data->pdu + data->offset);
- data->offset += sizeof(value);
-
- return true;
+ return util_iov_push_le16(&data->pdu, value) != NULL;
}
bool bt_gatt_server_send_notification(struct bt_gatt_server *server,
@@ -1825,8 +1820,8 @@ bool bt_gatt_server_send_notification(struct bt_gatt_server *server,
data = server->mnotify;
/* flush buffered data if this request hits buffer size limit */
- if (data && data->offset > 0 &&
- data->len - data->offset < 4 + length) {
+ if (data && data->pdu.iov_len > 0 &&
+ rsp_room(&data->pdu, data->len) < (size_t)4 + length) {
mnotify_timeout_remove(server);
mnotify_send(server);
/* data has been freed by mnotify_send */
@@ -1837,24 +1832,24 @@ bool bt_gatt_server_send_notification(struct bt_gatt_server *server,
if (!data) {
data = new0(struct mnotify_data, 1);
data->len = bt_att_get_mtu(server->att) - 1;
- data->pdu = malloc(data->len);
+ data->pdu.iov_base = malloc(data->len);
+ if (!data->pdu.iov_base)
+ goto error;
}
if (!notify_append_le16(data, handle))
goto error;
if (multiple) {
- length = MIN(data->len - data->offset - 2, length);
+ length = MIN(rsp_room(&data->pdu, data->len) - 2, length);
if (!notify_append_le16(data, length))
goto error;
} else {
- length = MIN(data->len - data->offset, length);
+ length = MIN(rsp_room(&data->pdu, data->len), length);
}
- if (value)
- memcpy(data->pdu + data->offset, value, length);
-
- data->offset += length;
+ if (length && !util_iov_push_mem(&data->pdu, length, value))
+ goto error;
if (multiple) {
if (!server->mnotify)
@@ -1869,15 +1864,16 @@ bool bt_gatt_server_send_notification(struct bt_gatt_server *server,
}
result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_NFY,
- data->pdu, data->offset, NULL, NULL, NULL);
- free(data->pdu);
+ data->pdu.iov_base, data->pdu.iov_len, NULL,
+ NULL, NULL);
+ free(data->pdu.iov_base);
free(data);
return result;
error:
if (data) {
- free(data->pdu);
+ free(data->pdu.iov_base);
free(data);
}
@@ -1917,7 +1913,7 @@ bool bt_gatt_server_send_indication(struct bt_gatt_server *server,
bt_gatt_server_destroy_func_t destroy)
{
uint16_t pdu_len;
- uint8_t *pdu;
+ struct iovec pdu = {};
struct ind_data *data;
bool result;
@@ -1925,8 +1921,8 @@ bool bt_gatt_server_send_indication(struct bt_gatt_server *server,
return false;
pdu_len = MIN(bt_att_get_mtu(server->att) - 1, length + 2);
- pdu = malloc(pdu_len);
- if (!pdu)
+ pdu.iov_base = malloc(pdu_len);
+ if (!pdu.iov_base)
return false;
data = new0(struct ind_data, 1);
@@ -1935,16 +1931,17 @@ bool bt_gatt_server_send_indication(struct bt_gatt_server *server,
data->destroy = destroy;
data->user_data = user_data;
- put_le16(handle, pdu);
- memcpy(pdu + 2, value, pdu_len - 2);
+ util_iov_push_le16(&pdu, handle);
+ if (pdu_len > 2)
+ util_iov_push_mem(&pdu, pdu_len - 2, value);
- result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_IND, pdu,
- pdu_len, conf_cb,
- data, destroy_ind_data);
+ result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_IND, pdu.iov_base,
+ pdu.iov_len, conf_cb,
+ data, destroy_ind_data);
if (!result)
destroy_ind_data(data);
- free(pdu);
+ free(pdu.iov_base);
return result;
}
--
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 ` Luiz Augusto von Dentz [this message]
2026-11-09 15:44 ` [PATCH BlueZ v1 03/10] shared/gatt-helpers: Use util_iov helpers to build and parse PDUs Luiz Augusto von Dentz
2026-11-09 15:44 ` [PATCH BlueZ v1 04/10] shared/gatt-client: " Luiz Augusto von Dentz
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-3-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