* [PATCH BlueZ v1 01/10] shared/att: Use util_iov helpers to build and parse PDUs
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 ` 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
` (9 subsequent siblings)
10 siblings, 1 reply; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-09 15:44 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Build the PDU in encode_pdu and read the signature counter in
handle_signed with util_iov_push/pull so the length checks are done as
part of the access.
Assisted-by: OpenCode:claude-sonnet-5.5
---
src/shared/att.c | 28 ++++++++++++++++++++--------
1 file changed, 20 insertions(+), 8 deletions(-)
diff --git a/src/shared/att.c b/src/shared/att.c
index 3055f291f8f1..4fa69e597556 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -338,7 +338,9 @@ static bool encode_pdu(struct bt_att *att, struct att_send_op *op,
{
uint16_t pdu_len = 1;
struct sign_info *sign = att->local_sign;
+ struct iovec iov;
uint32_t sign_cnt;
+ void *sig;
if (sign && (op->opcode & ATT_OP_SIGNED_MASK))
pdu_len += BT_ATT_SIGNATURE_LEN;
@@ -354,9 +356,12 @@ static bool encode_pdu(struct bt_att *att, struct att_send_op *op,
if (!op->pdu)
return false;
- ((uint8_t *) op->pdu)[0] = op->opcode;
- if (pdu_len > 1)
- memcpy(op->pdu + 1, pdu, length);
+ iov.iov_base = op->pdu;
+ iov.iov_len = 0;
+
+ util_iov_push_u8(&iov, op->opcode);
+ if (length && pdu)
+ util_iov_push_mem(&iov, length, pdu);
if (!sign || !(op->opcode & ATT_OP_SIGNED_MASK) || !att->crypto)
return true;
@@ -364,8 +369,13 @@ static bool encode_pdu(struct bt_att *att, struct att_send_op *op,
if (!sign->counter(&sign_cnt, sign->user_data))
goto fail;
- if ((bt_crypto_sign_att(att->crypto, sign->key, op->pdu, 1 + length,
- sign_cnt, &((uint8_t *) op->pdu)[1 + length])))
+ sig = util_iov_push(&iov, BT_ATT_SIGNATURE_LEN);
+ if (!sig)
+ goto fail;
+
+ if ((bt_crypto_sign_att(att->crypto, sign->key, op->pdu,
+ iov.iov_len - BT_ATT_SIGNATURE_LEN,
+ sign_cnt, sig)))
return true;
DBG(att, "ATT unable to generate signature");
@@ -978,7 +988,7 @@ static void respond_not_supported(struct bt_att *att, uint8_t opcode)
static bool handle_signed(struct bt_att *att, uint8_t *pdu, ssize_t pdu_len)
{
- uint8_t *signature;
+ struct iovec iov = { pdu, pdu_len };
uint32_t sign_cnt;
struct sign_info *sign;
uint8_t opcode = pdu[0];
@@ -991,8 +1001,10 @@ static bool handle_signed(struct bt_att *att, uint8_t *pdu, ssize_t pdu_len)
if (!sign)
goto fail;
- signature = pdu + (pdu_len - BT_ATT_SIGNATURE_LEN);
- sign_cnt = get_le32(signature);
+ /* Skip to the signature trailer, the counter is its first field */
+ if (!util_iov_pull(&iov, pdu_len - BT_ATT_SIGNATURE_LEN) ||
+ !util_iov_pull_le32(&iov, &sign_cnt))
+ goto fail;
/* Validate counter */
if (!sign->counter(&sign_cnt, sign->user_data))
--
2.56.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* RE: shared/gatt: Use util_iov helpers for PDU handling
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 ` bluez.test.bot
0 siblings, 0 replies; 13+ messages in thread
From: bluez.test.bot @ 2026-11-10 6:49 UTC (permalink / raw)
To: linux-bluetooth, luiz.dentz
[-- Attachment #1: Type: text/plain, Size: 37964 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/series/1206169/
---Test result---
Test Summary:
CheckPatch FAIL 5.99 seconds
GitLint FAIL 3.11 seconds
BuildEll PASS 20.46 seconds
BluezMake PASS 305.14 seconds
MakeCheck PASS 14.46 seconds
MakeDistcheck PASS 146.01 seconds
CheckValgrind PASS 347.67 seconds
CheckSmatch WARNING 305.29 seconds
bluezmakeextell PASS 99.48 seconds
TestFunctional FAIL 3266.47 seconds
IncrementalBuild PASS 451.97 seconds
ScanBuild PASS 1167.97 seconds
Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[BlueZ,v1,07/10] shared/gatt-server: Fix double response on prepare write failure
WARNING:BAD_FIXES_TAG: Please use correct Fixes: style 'Fixes: <12+ chars of sha1> ("<title line>")' - ie: 'Fixes: 1ebfc68ff53e ("shared/gatt-server: Request authorization for prepare writes")'
#128:
Fixes: 1ebfc68f ("shared/gatt-server: Request authorization for prepare writes")
/home/runner/work/bluez/bluez/src/patch/14964768.patch total: 0 errors, 1 warnings, 17 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
/home/runner/work/bluez/bluez/src/patch/14964768.patch has style problems, please review.
NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
[BlueZ,v1,08/10] shared/gatt-server: Fix return value of handler registration
WARNING:BAD_FIXES_TAG: Please use correct Fixes: style 'Fixes: <12+ chars of sha1> ("<title line>")' - ie: 'Fixes: b3e2fa94b26b ("shared/gatt-server: Implement "Execute Write" request.")'
#125:
Fixes: b3e2fa94 ("shared/gatt-server: Implement "Execute Write" request.")
/home/runner/work/bluez/bluez/src/patch/14964769.patch total: 0 errors, 1 warnings, 8 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
/home/runner/work/bluez/bluez/src/patch/14964769.patch has style problems, please review.
NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
[BlueZ,v1,09/10] shared/att: Fix reference leak on signature verification failure
WARNING:BAD_FIXES_TAG: Please use correct Fixes: style 'Fixes: <12+ chars of sha1> ("<title line>")' - ie: 'Fixes: 051a525c4a44 ("shared/att.c: Add server signing key support")'
#126:
Fixes: 051a525c ("shared/att.c: Add server signing key support")
/home/runner/work/bluez/bluez/src/patch/14964772.patch total: 0 errors, 1 warnings, 11 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
/home/runner/work/bluez/bluez/src/patch/14964772.patch has style problems, please review.
NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
[BlueZ,v1,10/10] shared/att: Fix signature length subtracted once per handler
WARNING:BAD_FIXES_TAG: Please use correct Fixes: style 'Fixes: <12+ chars of sha1> ("<title line>")' - ie: 'Fixes: 051a525c4a44 ("shared/att.c: Add server signing key support")'
#129:
Fixes: 051a525c ("shared/att.c: Add server signing key support")
/home/runner/work/bluez/bluez/src/patch/14964774.patch total: 0 errors, 1 warnings, 23 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
/home/runner/work/bluez/bluez/src/patch/14964774.patch has style problems, please review.
NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[BlueZ,v1,02/10] shared/gatt-server: Use util_iov helpers to build and parse PDUs
1: T1 Title exceeds max length (81>80): "[BlueZ,v1,02/10] shared/gatt-server: Use util_iov helpers to build and parse PDUs"
[BlueZ,v1,03/10] shared/gatt-helpers: Use util_iov helpers to build and parse PDUs
1: T1 Title exceeds max length (82>80): "[BlueZ,v1,03/10] shared/gatt-helpers: Use util_iov helpers to build and parse PDUs"
[BlueZ,v1,04/10] shared/gatt-client: Use util_iov helpers to build and parse PDUs
1: T1 Title exceeds max length (81>80): "[BlueZ,v1,04/10] shared/gatt-client: Use util_iov helpers to build and parse PDUs"
[BlueZ,v1,06/10] shared/gatt-server: Fix authorize callback getting handle 0 on write
1: T1 Title exceeds max length (85>80): "[BlueZ,v1,06/10] shared/gatt-server: Fix authorize callback getting handle 0 on write"
[BlueZ,v1,07/10] shared/gatt-server: Fix double response on prepare write failure
1: T1 Title exceeds max length (81>80): "[BlueZ,v1,07/10] shared/gatt-server: Fix double response on prepare write failure"
[BlueZ,v1,09/10] shared/att: Fix reference leak on signature verification failure
1: T1 Title exceeds max length (81>80): "[BlueZ,v1,09/10] shared/att: Fix reference leak on signature verification failure"
##############################
Test: CheckSmatch - WARNING
Desc: Run smatch tool with source
Output:
src/shared/gatt-helpers.c:845:31: warning: Variable length array is used.src/shared/gatt-helpers.c:925:31: warning: Variable length array is used.src/shared/gatt-helpers.c:1462:31: warning: Variable length array is used.src/shared/gatt-helpers.c:1494:23: warning: Variable length array is used.src/shared/gatt-helpers.c:845:31: warning: Variable length array is used.src/shared/gatt-helpers.c:925:31: warning: Variable length array is used.src/shared/gatt-helpers.c:1462:31: warning: Variable length array is used.src/shared/gatt-helpers.c:1494:23: warning: Variable length array is used.src/shared/gatt-helpers.c:845:31: warning: Variable length array is used.src/shared/gatt-helpers.c:925:31: warning: Variable length array is used.src/shared/gatt-helpers.c:1462:31: warning: Variable length array is used.src/shared/gatt-helpers.c:1494:23: warning: Variable length array is used.
##############################
Test: TestFunctional - FAIL
Desc: Run test-functional
Output:
FAIL functional.test_adv_monitor::test_adv_monitor_GHSA_hhgc_hfgf_8m4x[hosts1-vm1]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bluetoothctl::test_bluetoothctl_show[hosts16-vm1]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bluetoothctl::test_bluetoothctl_list[hosts16-vm1]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bluetoothctl::test_bluetoothctl_script_show[hosts16-vm1]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bluetoothctl::test_bluetoothctl_script_list[hosts16-vm1]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_btmgmt::test_btmgmt_info[hosts19-vm1]: AssertionError: assert 'addr 00:00:00:00:00:00' in '\x1b[0;91mReading hci0 info failed with status 0x11 (Invalid Index)\n\x1b[0m'
+ where '\x1b[0;91mReading hci0 info failed with status 0x11 (Invalid Index)\n\x1b[0m' = CompletedProcess(args=['/home/runner/work/bluez/bluez/src/src/tools/btmgmt', '--index', '0', 'info'], returncode=0, stdout='\x1b[0;91mReading hci0 info failed with status 0x11 (Invalid Index)\n\x1b[0m').stdout
FAIL functional.test_a2dp::test_a2dp_transport_created[hosts0-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_a2dp::test_a2dp_transport_acquire[hosts0-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_a2dp::test_a2dp_disconnect_during_setconf[hosts0-vm2-accept]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_a2dp::test_a2dp_disconnect_during_setconf[hosts0-vm2-reject]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_a2dp::test_a2dp_reconnect_pending_disconnect[hosts0-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bearer::test_bearer_le_connect[hosts12-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bearer::test_bearer_le_conn_params[hosts12-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bearer::test_bearer_le_connectable[hosts12-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bearer::test_bearer_le_pair[hosts12-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bearer::test_bearer_bredr_pair[hosts13-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bearer::test_bearer_le_conn_update[hosts14-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bearer::test_bearer_le_not_connectable[hosts15-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bluetoothctl::test_bluetoothctl_pair_bredr[hosts17-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bluetoothctl::test_bluetoothctl_pair_le[hosts18-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_agent::test_agent_pair_bredr[accept-hosts2-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_agent::test_agent_pair_bredr[reject-hosts2-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_hog::test_hog[no-sci-hosts20-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_hog::test_hog[sci-hosts20-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_mpris_proxy::test_mpris_proxy_playback_control[bredr-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_mpris_proxy::test_mpris_proxy_track_control[bredr-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_obex::test_obex_ftp_list[hosts24-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_obex::test_obex_ftp_get[hosts24-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_obex::test_obexctl_list[hosts24-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_avrcp::test_avrcp_GHSA_m2vx_pw5f_rc8v[hosts3-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_unicast_transport_created[hosts4-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_unicast_transport_acquire[hosts4-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_unicast_transport_resume[0-hosts4-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_unicast_transport_resume[0.1-hosts4-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_unicast_transport_resume[1.0-hosts4-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_unicast_reconfigure_metadata[empty-hosts5-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_unicast_reconfigure_metadata[media-hosts5-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_unicast_desync[hosts6-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_unicast_duplex_enable_one[hosts6-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_broadcast_transport_created[lc3-hosts7-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_broadcast_transport_created[pbp-hosts7-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_broadcast_transport_acquire[lc3-hosts7-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_broadcast_transport_acquire[pbp-hosts7-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bass_past_transport_acquire[hosts9-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_unicast_set_transport_created[hosts10-vm3-sc]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_unicast_set_transport_created[hosts10-vm3-legacy]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_unicast_set_transport_acquire[hosts10-vm3]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bass_past_earbuds_transport_acquire[hosts11-vm3]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_broadcast_earbuds_transport_created[hosts8-vm3]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_bap::test_bap_broadcast_earbuds_transport_acquire[hosts8-vm3]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_mpris_proxy::test_mpris_proxy_playback_control[le-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
FAIL functional.test_mpris_proxy::test_mpris_proxy_track_control[le-vm2]: failed on setup with "pytest_bluezenv.rpc.RemoteError: load failed
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 238, in wait_load
raise RuntimeError("load failed")
RuntimeError: load failed"
https://github.com/bluez/bluez/pull/2783
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH BlueZ v1 02/10] shared/gatt-server: Use util_iov helpers to build and parse PDUs
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-09 15:44 ` Luiz Augusto von Dentz
2026-11-09 15:44 ` [PATCH BlueZ v1 03/10] shared/gatt-helpers: " Luiz Augusto von Dentz
` (8 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-09 15:44 UTC (permalink / raw)
To: linux-bluetooth
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
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH BlueZ v1 03/10] shared/gatt-helpers: Use util_iov helpers to build and parse PDUs
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-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 ` Luiz Augusto von Dentz
2026-11-09 15:44 ` [PATCH BlueZ v1 04/10] shared/gatt-client: " Luiz Augusto von Dentz
` (7 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-09 15:44 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Build requests with util_iov_push_* and parse responses with
util_iov_pull_*. The result iterators now check each element against the
stored PDU length and return false for a malformed one rather than
reading past it.
Assisted-by: OpenCode:claude-sonnet-5.5
---
src/shared/gatt-helpers.c | 452 +++++++++++++++++++++++++-------------
1 file changed, 296 insertions(+), 156 deletions(-)
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index e3281ec17758..ea77a41ef362 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -225,13 +225,37 @@ static struct bt_gatt_result *result_append(uint8_t opcode, const void *pdu,
return result;
}
+/* Get the data element at the current position of the iterator */
+static bool iter_elem(struct bt_gatt_iter *iter, struct iovec *elem)
+{
+ struct bt_gatt_result *result = iter->result;
+ struct iovec iov = { result->pdu, result->pdu_len };
+
+ if (!util_iov_pull(&iov, iter->pos))
+ return false;
+
+ elem->iov_base = util_iov_pull_mem(&iov, result->data_len);
+ elem->iov_len = result->data_len;
+
+ return elem->iov_base != NULL;
+}
+
+static void iter_advance(struct bt_gatt_iter *iter)
+{
+ iter->pos += iter->result->data_len;
+ if (iter->pos >= iter->result->pdu_len) {
+ iter->result = iter->result->next;
+ iter->pos = 0;
+ }
+}
+
bool bt_gatt_iter_next_included_service(struct bt_gatt_iter *iter,
uint16_t *handle, uint16_t *start_handle,
uint16_t *end_handle, uint8_t uuid[16])
{
struct bt_gatt_result *read_result;
struct bt_gatt_request *op;
- const void *pdu_ptr;
+ struct iovec elem;
int i = 0;
if (!iter || !iter->result || !handle || !start_handle || !end_handle
@@ -256,28 +280,24 @@ bool bt_gatt_iter_next_included_service(struct bt_gatt_iter *iter,
if (iter->result->data_len != 8 && iter->result->data_len != 6)
return false;
- pdu_ptr = iter->result->pdu + iter->pos;
+ if (!iter_elem(iter, &elem))
+ return false;
+
+ if (!util_iov_pull_le16(&elem, handle) ||
+ !util_iov_pull_le16(&elem, start_handle) ||
+ !util_iov_pull_le16(&elem, end_handle))
+ return false;
/* This result contains 16 bit UUID */
if (iter->result->data_len == 8) {
- *handle = get_le16(pdu_ptr);
- *start_handle = get_le16(pdu_ptr + 2);
- *end_handle = get_le16(pdu_ptr + 4);
- convert_uuid_le(pdu_ptr + 6, 2, uuid);
+ if (!convert_uuid_le(elem.iov_base, elem.iov_len, uuid))
+ return false;
- iter->pos += iter->result->data_len;
-
- if (iter->pos == iter->result->pdu_len) {
- iter->result = iter->result->next;
- iter->pos = 0;
- }
+ iter_advance(iter);
return true;
}
- *handle = get_le16(pdu_ptr);
- *start_handle = get_le16(pdu_ptr + 2);
- *end_handle = get_le16(pdu_ptr + 4);
read_result = iter->result;
/*
@@ -295,9 +315,11 @@ bool bt_gatt_iter_next_included_service(struct bt_gatt_iter *iter,
if (!read_result)
return false;
- convert_uuid_le(read_result->pdu, read_result->data_len, uuid);
+ if (!convert_uuid_le(read_result->pdu, read_result->data_len, uuid))
+ return false;
+
iter->pos += iter->result->data_len;
- if (iter->pos == iter->result->pdu_len) {
+ if (iter->pos >= iter->result->pdu_len) {
iter->result = read_result->next;
iter->pos = 0;
}
@@ -310,25 +332,25 @@ bool bt_gatt_iter_next_service(struct bt_gatt_iter *iter,
uint8_t uuid[16])
{
struct bt_gatt_request *op;
- const void *pdu_ptr;
+ struct iovec elem;
bt_uuid_t tmp;
if (!iter || !iter->result || !start_handle || !end_handle || !uuid)
return false;
op = iter->result->op;
- pdu_ptr = iter->result->pdu + iter->pos;
+
+ if (!iter_elem(iter, &elem) ||
+ !util_iov_pull_le16(&elem, start_handle) ||
+ !util_iov_pull_le16(&elem, end_handle))
+ return false;
switch (iter->result->opcode) {
case BT_ATT_OP_READ_BY_GRP_TYPE_RSP:
- *start_handle = get_le16(pdu_ptr);
- *end_handle = get_le16(pdu_ptr + 2);
- convert_uuid_le(pdu_ptr + 4, iter->result->data_len - 4, uuid);
+ if (!convert_uuid_le(elem.iov_base, elem.iov_len, uuid))
+ return false;
break;
case BT_ATT_OP_FIND_BY_TYPE_RSP:
- *start_handle = get_le16(pdu_ptr);
- *end_handle = get_le16(pdu_ptr + 2);
-
bt_uuid_to_uuid128(&op->uuid, &tmp);
memcpy(uuid, tmp.value.u128.data, 16);
break;
@@ -336,12 +358,7 @@ bool bt_gatt_iter_next_service(struct bt_gatt_iter *iter,
return false;
}
-
- iter->pos += iter->result->data_len;
- if (iter->pos == iter->result->pdu_len) {
- iter->result = iter->result->next;
- iter->pos = 0;
- }
+ iter_advance(iter);
return true;
}
@@ -352,7 +369,8 @@ bool bt_gatt_iter_next_characteristic(struct bt_gatt_iter *iter,
uint8_t uuid[16])
{
struct bt_gatt_request *op;
- const void *pdu_ptr;
+ struct iovec elem;
+ struct iovec next;
if (!iter || !iter->result || !start_handle || !end_handle ||
!value_handle || !properties || !uuid)
@@ -375,25 +393,24 @@ bool bt_gatt_iter_next_characteristic(struct bt_gatt_iter *iter,
if (iter->result->data_len != 21 && iter->result->data_len != 7)
return false;
- pdu_ptr = iter->result->pdu + iter->pos;
-
- *start_handle = get_le16(pdu_ptr);
- *properties = ((uint8_t *) pdu_ptr)[2];
- *value_handle = get_le16(pdu_ptr + 3);
- convert_uuid_le(pdu_ptr + 5, iter->result->data_len - 5, uuid);
+ if (!iter_elem(iter, &elem) ||
+ !util_iov_pull_le16(&elem, start_handle) ||
+ !util_iov_pull_u8(&elem, properties) ||
+ !util_iov_pull_le16(&elem, value_handle) ||
+ !convert_uuid_le(elem.iov_base, elem.iov_len, uuid))
+ return false;
- iter->pos += iter->result->data_len;
- if (iter->pos == iter->result->pdu_len) {
- iter->result = iter->result->next;
- iter->pos = 0;
- }
+ iter_advance(iter);
if (!iter->result) {
*end_handle = op->end_handle;
return true;
}
- *end_handle = get_le16(iter->result->pdu + iter->pos) - 1;
+ if (!iter_elem(iter, &next) || !util_iov_pull_le16(&next, end_handle))
+ return false;
+
+ (*end_handle)--;
return true;
}
@@ -401,7 +418,7 @@ bool bt_gatt_iter_next_characteristic(struct bt_gatt_iter *iter,
bool bt_gatt_iter_next_descriptor(struct bt_gatt_iter *iter, uint16_t *handle,
uint8_t uuid[16])
{
- const void *pdu_ptr;
+ struct iovec elem;
if (!iter || !iter->result || !handle || !uuid)
return false;
@@ -409,16 +426,11 @@ bool bt_gatt_iter_next_descriptor(struct bt_gatt_iter *iter, uint16_t *handle,
if (iter->result->opcode != BT_ATT_OP_FIND_INFO_RSP)
return false;
- pdu_ptr = iter->result->pdu + iter->pos;
-
- *handle = get_le16(pdu_ptr);
- convert_uuid_le(pdu_ptr + 2, iter->result->data_len - 2, uuid);
+ if (!iter_elem(iter, &elem) || !util_iov_pull_le16(&elem, handle) ||
+ !convert_uuid_le(elem.iov_base, elem.iov_len, uuid))
+ return false;
- iter->pos += iter->result->data_len;
- if (iter->pos == iter->result->pdu_len) {
- iter->result = iter->result->next;
- iter->pos = 0;
- }
+ iter_advance(iter);
return true;
}
@@ -428,7 +440,7 @@ bool bt_gatt_iter_next_read_by_type(struct bt_gatt_iter *iter,
const uint8_t **value)
{
struct bt_gatt_request *op;
- const void *pdu_ptr;
+ struct iovec elem;
if (!iter || !iter->result || !handle || !length || !value)
return false;
@@ -444,17 +456,13 @@ bool bt_gatt_iter_next_read_by_type(struct bt_gatt_iter *iter,
if (op->uuid.type == BT_UUID_UNSPEC)
return false;
- pdu_ptr = iter->result->pdu + iter->pos;
+ if (!iter_elem(iter, &elem) || !util_iov_pull_le16(&elem, handle))
+ return false;
- *handle = get_le16(pdu_ptr);
- *length = iter->result->data_len - 2;
- *value = pdu_ptr + 2;
+ *length = elem.iov_len;
+ *value = elem.iov_base;
- iter->pos += iter->result->data_len;
- if (iter->pos == iter->result->pdu_len) {
- iter->result = iter->result->next;
- iter->pos = 0;
- }
+ iter_advance(iter);
return true;
}
@@ -495,6 +503,7 @@ static void mtu_cb(uint8_t opcode, const void *pdu, uint16_t length,
struct mtu_op *op = user_data;
bool success = true;
uint8_t att_ecode = 0;
+ struct iovec iov = { (void *)pdu, length };
uint16_t server_rx_mtu;
if (opcode == BT_ATT_OP_ERROR_RSP) {
@@ -503,12 +512,13 @@ static void mtu_cb(uint8_t opcode, const void *pdu, uint16_t length,
goto done;
}
- if (opcode != BT_ATT_OP_MTU_RSP || !pdu || length != 2) {
+ if (opcode != BT_ATT_OP_MTU_RSP || !pdu ||
+ !util_iov_pull_le16(&iov, &server_rx_mtu) ||
+ iov.iov_len) {
success = false;
goto done;
}
- server_rx_mtu = get_le16(pdu);
bt_att_set_mtu(op->att, MIN(op->client_rx_mtu, server_rx_mtu));
done:
@@ -523,6 +533,7 @@ unsigned int bt_gatt_exchange_mtu(struct bt_att *att, uint16_t client_rx_mtu,
{
struct mtu_op *op;
uint8_t pdu[2];
+ struct iovec req = { pdu, 0 };
unsigned int id;
if (!att || !client_rx_mtu)
@@ -535,10 +546,10 @@ unsigned int bt_gatt_exchange_mtu(struct bt_att *att, uint16_t client_rx_mtu,
op->user_data = user_data;
op->destroy = destroy;
- put_le16(client_rx_mtu, pdu);
+ util_iov_push_le16(&req, client_rx_mtu);
- id = bt_att_send(att, BT_ATT_OP_MTU_REQ, pdu, sizeof(pdu), mtu_cb, op,
- destroy_mtu_op);
+ id = bt_att_send(att, BT_ATT_OP_MTU_REQ, req.iov_base, req.iov_len,
+ mtu_cb, op, destroy_mtu_op);
if (!id)
free(op);
@@ -553,6 +564,59 @@ static inline int get_uuid_len(const bt_uuid_t *uuid)
return (uuid->type == BT_UUID16) ? 2 : 16;
}
+static bool pull_data_length(struct iovec *iov, size_t *data_length)
+{
+ uint8_t len;
+
+ if (!util_iov_pull_u8(iov, &len))
+ return false;
+
+ *data_length = len;
+
+ return true;
+}
+
+static bool push_uuid_le(struct iovec *iov, const bt_uuid_t *uuid)
+{
+ void *data = util_iov_push(iov, get_uuid_len(uuid));
+
+ if (!data)
+ return false;
+
+ bt_uuid_to_le(uuid, data);
+
+ return true;
+}
+
+/*
+ * Pull a le16 located skip octets into the last data_length sized element of
+ * a list of data_length sized elements.
+ */
+static bool pull_last_le16(const void *list, size_t length, size_t data_length,
+ size_t skip, uint16_t *val)
+{
+ struct iovec iov = { (void *)list, length };
+
+ if (length < data_length)
+ return false;
+
+ return util_iov_pull(&iov, length - data_length + skip) &&
+ util_iov_pull_le16(&iov, val);
+}
+
+/* Push the le16 located at pos + 2 in the result into a Read Request */
+static bool push_included_handle(struct iovec *req,
+ const struct bt_gatt_result *result,
+ size_t pos)
+{
+ struct iovec iov = { result->pdu, result->pdu_len };
+ uint16_t handle;
+
+ return util_iov_pull(&iov, pos + 2) &&
+ util_iov_pull_le16(&iov, &handle) &&
+ util_iov_push_le16(req, handle);
+}
+
struct bt_gatt_request *bt_gatt_request_ref(struct bt_gatt_request *req)
{
if (!req)
@@ -621,12 +685,12 @@ static void discovery_op_complete(struct bt_gatt_request *op, bool success,
static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
+ struct iovec iov = { (void *)pdu, length };
struct bt_gatt_request *op = user_data;
bool success;
uint8_t att_ecode = 0;
struct bt_gatt_result *cur_result;
size_t data_length;
- size_t list_length;
uint16_t last_end;
if (opcode == BT_ATT_OP_ERROR_RSP) {
@@ -647,11 +711,13 @@ static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
goto done;
}
- data_length = ((uint8_t *) pdu)[0];
- list_length = length - 1;
+ if (!pull_data_length(&iov, &data_length)) {
+ success = false;
+ goto done;
+ }
if ((data_length != 6 && data_length != 20) ||
- (list_length % data_length)) {
+ (iov.iov_len % data_length)) {
success = false;
goto done;
}
@@ -659,14 +725,17 @@ static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
/* PDU is correctly formatted. Get the last end handle to process the
* next request and store the PDU.
*/
- cur_result = result_append(opcode, pdu + 1, list_length, data_length,
- op);
+ cur_result = result_append(opcode, iov.iov_base, iov.iov_len,
+ data_length, op);
if (!cur_result) {
success = false;
goto done;
}
- last_end = get_le16(pdu + length - data_length + 2);
+ if (!pull_last_le16(pdu, length, data_length, 2, &last_end)) {
+ success = false;
+ goto done;
+ }
/*
* If last handle is lower from previous start handle then it is smth
@@ -681,13 +750,14 @@ static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
if (last_end < op->end_handle) {
uint8_t pdu[6];
+ struct iovec req = { pdu, 0 };
- put_le16(op->start_handle, pdu);
- put_le16(op->end_handle, pdu + 2);
- put_le16(op->service_type, pdu + 4);
+ util_iov_push_le16(&req, op->start_handle);
+ util_iov_push_le16(&req, op->end_handle);
+ util_iov_push_le16(&req, op->service_type);
op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_GRP_TYPE_REQ,
- pdu, sizeof(pdu),
+ req.iov_base, req.iov_len,
read_by_grp_type_cb,
bt_gatt_request_ref(op),
async_req_unref);
@@ -703,9 +773,17 @@ static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
* Manually set the end group handle that we report in the result to the
* end handle in the original request.
*/
- if (last_end == 0xffff && last_end != op->end_handle)
- put_le16(op->end_handle,
- cur_result->pdu + length - data_length + 1);
+ if (last_end == 0xffff && last_end != op->end_handle) {
+ struct iovec end = { cur_result->pdu, cur_result->pdu_len };
+ void *p;
+
+ if (util_iov_pull(&end, cur_result->pdu_len -
+ data_length + 2)) {
+ p = util_iov_pull_mem(&end, 2);
+ if (p)
+ put_le16(op->end_handle, p);
+ }
+ }
success = true;
@@ -747,7 +825,10 @@ static void find_by_type_val_cb(uint8_t opcode, const void *pdu,
* 2 octets with end handle
* last_end is end handle of last data set
*/
- last_end = get_le16(pdu + length - 2);
+ if (!pull_last_le16(pdu, length, 4, 2, &last_end)) {
+ success = false;
+ goto done;
+ }
/*
* If last handle is lower from previous start handle then it is smth
@@ -762,14 +843,15 @@ static void find_by_type_val_cb(uint8_t opcode, const void *pdu,
if (last_end < op->end_handle) {
uint8_t pdu[6 + get_uuid_len(&op->uuid)];
+ struct iovec req = { pdu, 0 };
- put_le16(op->start_handle, pdu);
- put_le16(op->end_handle, pdu + 2);
- put_le16(op->service_type, pdu + 4);
- bt_uuid_to_le(&op->uuid, pdu + 6);
+ util_iov_push_le16(&req, op->start_handle);
+ util_iov_push_le16(&req, op->end_handle);
+ util_iov_push_le16(&req, op->service_type);
+ push_uuid_le(&req, &op->uuid);
op->id = bt_att_send(op->att, BT_ATT_OP_FIND_BY_TYPE_REQ,
- pdu, sizeof(pdu),
+ req.iov_base, req.iov_len,
find_by_type_val_cb,
bt_gatt_request_ref(op),
async_req_unref);
@@ -828,18 +910,20 @@ static struct bt_gatt_request *discover_services(struct bt_att *att,
/* If UUID is NULL, then discover all primary services */
if (!uuid) {
uint8_t pdu[6];
+ struct iovec req = { pdu, 0 };
- put_le16(start, pdu);
- put_le16(end, pdu + 2);
- put_le16(op->service_type, pdu + 4);
+ util_iov_push_le16(&req, start);
+ util_iov_push_le16(&req, end);
+ util_iov_push_le16(&req, op->service_type);
op->id = bt_att_send(att, BT_ATT_OP_READ_BY_GRP_TYPE_REQ,
- pdu, sizeof(pdu),
+ req.iov_base, req.iov_len,
read_by_grp_type_cb,
bt_gatt_request_ref(op),
async_req_unref);
} else {
uint8_t pdu[6 + get_uuid_len(uuid)];
+ struct iovec req = { pdu, 0 };
if (uuid->type == BT_UUID_UNSPEC) {
free(op);
@@ -849,13 +933,13 @@ static struct bt_gatt_request *discover_services(struct bt_att *att,
/* Discover by UUID */
op->uuid = *uuid;
- put_le16(start, pdu);
- put_le16(end, pdu + 2);
- put_le16(op->service_type, pdu + 4);
- bt_uuid_to_le(&op->uuid, pdu + 6);
+ util_iov_push_le16(&req, start);
+ util_iov_push_le16(&req, end);
+ util_iov_push_le16(&req, op->service_type);
+ push_uuid_le(&req, &op->uuid);
op->id = bt_att_send(att, BT_ATT_OP_FIND_BY_TYPE_REQ,
- pdu, sizeof(pdu),
+ req.iov_base, req.iov_len,
find_by_type_val_cb,
bt_gatt_request_ref(op),
async_req_unref);
@@ -949,6 +1033,7 @@ static void read_included_cb(uint8_t opcode, const void *pdu,
struct bt_gatt_request *op = data->op;
uint8_t att_ecode = 0;
uint8_t read_pdu[2];
+ struct iovec read_req = { read_pdu, 0 };
bool success;
if (opcode == BT_ATT_OP_ERROR_RSP) {
@@ -979,20 +1064,26 @@ static void read_included_cb(uint8_t opcode, const void *pdu,
if (data->pos == data->result->pdu_len) {
uint16_t last_handle;
uint8_t pdu[6];
+ struct iovec req = { pdu, 0 };
+
+ if (!pull_last_le16(data->result->pdu, data->pos,
+ data->result->data_len, 0,
+ &last_handle)) {
+ success = false;
+ goto done;
+ }
- last_handle = get_le16(data->result->pdu + data->pos -
- data->result->data_len);
if (last_handle == op->end_handle) {
success = true;
goto done;
}
- put_le16(last_handle + 1, pdu);
- put_le16(op->end_handle, pdu + 2);
- put_le16(GATT_INCLUDE_UUID, pdu + 4);
+ util_iov_push_le16(&req, last_handle + 1);
+ util_iov_push_le16(&req, op->end_handle);
+ util_iov_push_le16(&req, GATT_INCLUDE_UUID);
op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
- pdu, sizeof(pdu),
+ req.iov_base, req.iov_len,
discover_included_cb,
bt_gatt_request_ref(op),
async_req_unref);
@@ -1003,11 +1094,15 @@ static void read_included_cb(uint8_t opcode, const void *pdu,
goto done;
}
- memcpy(read_pdu, data->result->pdu + data->pos + 2, sizeof(uint16_t));
+ if (!push_included_handle(&read_req, data->result, data->pos)) {
+ success = false;
+ goto done;
+ }
data->pos += data->result->data_len;
- if (bt_att_send(op->att, BT_ATT_OP_READ_REQ, read_pdu, sizeof(read_pdu),
+ if (bt_att_send(op->att, BT_ATT_OP_READ_REQ, read_req.iov_base,
+ read_req.iov_len,
read_included_cb, read_included_ref(data),
read_included_unref))
return;
@@ -1023,17 +1118,20 @@ static void read_included(struct read_incl_data *data)
{
struct bt_gatt_request *op = data->op;
uint8_t pdu[2];
+ struct iovec req = { pdu, 0 };
- memcpy(pdu, data->result->pdu + 2, sizeof(uint16_t));
+ if (!push_included_handle(&req, data->result, 0))
+ goto failed;
data->pos += data->result->data_len;
- if (bt_att_send(op->att, BT_ATT_OP_READ_REQ, pdu, sizeof(pdu),
+ if (bt_att_send(op->att, BT_ATT_OP_READ_REQ, req.iov_base, req.iov_len,
read_included_cb,
read_included_ref(data),
read_included_unref))
return;
+failed:
if (op->callback)
op->callback(false, 0, NULL, data->op->user_data);
@@ -1043,6 +1141,7 @@ static void read_included(struct read_incl_data *data)
static void discover_included_cb(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
+ struct iovec iov = { (void *)pdu, length };
struct bt_gatt_request *op = user_data;
struct bt_gatt_result *cur_result;
uint8_t att_ecode = 0;
@@ -1061,7 +1160,10 @@ static void discover_included_cb(uint8_t opcode, const void *pdu,
goto failed;
}
- data_length = ((const uint8_t *) pdu)[0];
+ if (!pull_data_length(&iov, &data_length)) {
+ success = false;
+ goto failed;
+ }
/*
* Check if PDU contains data sets with length declared in the beginning
@@ -1073,13 +1175,13 @@ static void discover_included_cb(uint8_t opcode, const void *pdu,
* optional 2 octets - Bluetooth UUID of included service
*/
if ((data_length != 8 && data_length != 6) ||
- (length - 1) % data_length) {
+ iov.iov_len % data_length) {
success = false;
goto failed;
}
- cur_result = result_append(opcode, pdu + 1, length - 1, data_length,
- op);
+ cur_result = result_append(opcode, iov.iov_base, iov.iov_len,
+ data_length, op);
if (!cur_result) {
success = false;
goto failed;
@@ -1098,7 +1200,10 @@ static void discover_included_cb(uint8_t opcode, const void *pdu,
return;
}
- last_handle = get_le16(pdu + length - data_length);
+ if (!pull_last_le16(pdu, length, data_length, 0, &last_handle)) {
+ success = false;
+ goto failed;
+ }
/*
* If last handle is lower from previous start handle then it is smth
@@ -1112,13 +1217,14 @@ static void discover_included_cb(uint8_t opcode, const void *pdu,
op->start_handle = last_handle + 1;
if (last_handle != op->end_handle) {
uint8_t pdu[6];
+ struct iovec req = { pdu, 0 };
- put_le16(op->start_handle, pdu);
- put_le16(op->end_handle, pdu + 2);
- put_le16(GATT_INCLUDE_UUID, pdu + 4);
+ util_iov_push_le16(&req, op->start_handle);
+ util_iov_push_le16(&req, op->end_handle);
+ util_iov_push_le16(&req, GATT_INCLUDE_UUID);
op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
- pdu, sizeof(pdu),
+ req.iov_base, req.iov_len,
discover_included_cb,
bt_gatt_request_ref(op),
async_req_unref);
@@ -1143,6 +1249,7 @@ struct bt_gatt_request *bt_gatt_discover_included_services(struct bt_att *att,
{
struct bt_gatt_request *op;
uint8_t pdu[6];
+ struct iovec req = { pdu, 0 };
if (!att)
return NULL;
@@ -1155,11 +1262,12 @@ struct bt_gatt_request *bt_gatt_discover_included_services(struct bt_att *att,
op->start_handle = start;
op->end_handle = end;
- put_le16(start, pdu);
- put_le16(end, pdu + 2);
- put_le16(GATT_INCLUDE_UUID, pdu + 4);
+ util_iov_push_le16(&req, start);
+ util_iov_push_le16(&req, end);
+ util_iov_push_le16(&req, GATT_INCLUDE_UUID);
- op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu),
+ op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ,
+ req.iov_base, req.iov_len,
discover_included_cb, bt_gatt_request_ref(op),
async_req_unref);
if (!op->id) {
@@ -1173,6 +1281,7 @@ struct bt_gatt_request *bt_gatt_discover_included_services(struct bt_att *att,
static void discover_chrcs_cb(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
+ struct iovec iov = { (void *)pdu, length };
struct bt_gatt_request *op = user_data;
bool success;
uint8_t att_ecode = 0;
@@ -1198,20 +1307,26 @@ static void discover_chrcs_cb(uint8_t opcode, const void *pdu,
goto done;
}
- data_length = ((uint8_t *) pdu)[0];
+ if (!pull_data_length(&iov, &data_length)) {
+ success = false;
+ goto done;
+ }
if ((data_length != 7 && data_length != 21) ||
- ((length - 1) % data_length)) {
+ (iov.iov_len % data_length)) {
success = false;
goto done;
}
- if (!result_append(opcode, pdu + 1, length - 1,
+ if (!result_append(opcode, iov.iov_base, iov.iov_len,
data_length, op)) {
success = false;
goto done;
}
- last_handle = get_le16(pdu + length - data_length);
+ if (!pull_last_le16(pdu, length, data_length, 0, &last_handle)) {
+ success = false;
+ goto done;
+ }
/*
* If last handle is lower from previous start handle then it is smth
@@ -1226,13 +1341,14 @@ static void discover_chrcs_cb(uint8_t opcode, const void *pdu,
if (last_handle != op->end_handle) {
uint8_t pdu[6];
+ struct iovec req = { pdu, 0 };
- put_le16(op->start_handle, pdu);
- put_le16(op->end_handle, pdu + 2);
- put_le16(GATT_CHARAC_UUID, pdu + 4);
+ util_iov_push_le16(&req, op->start_handle);
+ util_iov_push_le16(&req, op->end_handle);
+ util_iov_push_le16(&req, GATT_CHARAC_UUID);
op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
- pdu, sizeof(pdu),
+ req.iov_base, req.iov_len,
discover_chrcs_cb,
bt_gatt_request_ref(op),
async_req_unref);
@@ -1257,6 +1373,7 @@ struct bt_gatt_request *bt_gatt_discover_characteristics(struct bt_att *att,
{
struct bt_gatt_request *op;
uint8_t pdu[6];
+ struct iovec req = { pdu, 0 };
if (!att)
return NULL;
@@ -1269,11 +1386,12 @@ struct bt_gatt_request *bt_gatt_discover_characteristics(struct bt_att *att,
op->start_handle = start;
op->end_handle = end;
- put_le16(start, pdu);
- put_le16(end, pdu + 2);
- put_le16(GATT_CHARAC_UUID, pdu + 4);
+ util_iov_push_le16(&req, start);
+ util_iov_push_le16(&req, end);
+ util_iov_push_le16(&req, GATT_CHARAC_UUID);
- op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu),
+ op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ,
+ req.iov_base, req.iov_len,
discover_chrcs_cb, bt_gatt_request_ref(op),
async_req_unref);
if (!op->id) {
@@ -1287,6 +1405,7 @@ struct bt_gatt_request *bt_gatt_discover_characteristics(struct bt_att *att,
static void read_by_type_cb(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
+ struct iovec iov = { (void *)pdu, length };
struct bt_gatt_request *op = user_data;
bool success;
uint8_t att_ecode = 0;
@@ -1305,21 +1424,28 @@ static void read_by_type_cb(uint8_t opcode, const void *pdu,
goto done;
}
- data_length = ((const uint8_t *) pdu)[0];
- if (data_length < 2 || data_length > length - 1U ||
- (length - 1) % data_length) {
+ if (!pull_data_length(&iov, &data_length)) {
+ success = false;
+ goto done;
+ }
+ if (data_length < 2 || data_length > iov.iov_len ||
+ iov.iov_len % data_length) {
success = false;
att_ecode = 0;
goto done;
}
- if (!result_append(opcode, pdu + 1, length - 1, data_length, op)) {
+ if (!result_append(opcode, iov.iov_base, iov.iov_len, data_length,
+ op)) {
success = false;
att_ecode = 0;
goto done;
}
- last_handle = get_le16(pdu + length - data_length);
+ if (!pull_last_le16(pdu, length, data_length, 0, &last_handle)) {
+ success = false;
+ goto done;
+ }
/*
* If last handle is lower from previous start handle then it is smth
@@ -1334,13 +1460,14 @@ static void read_by_type_cb(uint8_t opcode, const void *pdu,
if (last_handle != op->end_handle) {
uint8_t pdu[4 + get_uuid_len(&op->uuid)];
+ struct iovec req = { pdu, 0 };
- put_le16(op->start_handle, pdu);
- put_le16(op->end_handle, pdu + 2);
- bt_uuid_to_le(&op->uuid, pdu + 4);
+ util_iov_push_le16(&req, op->start_handle);
+ util_iov_push_le16(&req, op->end_handle);
+ push_uuid_le(&req, &op->uuid);
op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
- pdu, sizeof(pdu),
+ req.iov_base, req.iov_len,
read_by_type_cb,
bt_gatt_request_ref(op),
async_req_unref);
@@ -1365,6 +1492,7 @@ bool bt_gatt_read_by_type(struct bt_att *att, uint16_t start, uint16_t end,
{
struct bt_gatt_request *op;
uint8_t pdu[4 + get_uuid_len(uuid)];
+ struct iovec req = { pdu, 0 };
if (!att || !uuid || uuid->type == BT_UUID_UNSPEC)
return false;
@@ -1378,11 +1506,12 @@ bool bt_gatt_read_by_type(struct bt_att *att, uint16_t start, uint16_t end,
op->end_handle = end;
op->uuid = *uuid;
- put_le16(start, pdu);
- put_le16(end, pdu + 2);
- bt_uuid_to_le(uuid, pdu + 4);
+ util_iov_push_le16(&req, start);
+ util_iov_push_le16(&req, end);
+ push_uuid_le(&req, uuid);
- op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu),
+ op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ,
+ req.iov_base, req.iov_len,
read_by_type_cb,
bt_gatt_request_ref(op),
async_req_unref);
@@ -1400,6 +1529,7 @@ static void discover_descs_cb(uint8_t opcode, const void *pdu,
bool success;
uint8_t att_ecode = 0;
uint8_t format;
+ struct iovec iov = { (void *)pdu, length };
uint16_t last_handle;
size_t data_length;
@@ -1420,7 +1550,10 @@ static void discover_descs_cb(uint8_t opcode, const void *pdu,
goto done;
}
- format = ((uint8_t *) pdu)[0];
+ if (!util_iov_pull_u8(&iov, &format)) {
+ success = false;
+ goto done;
+ }
if (format == 0x01)
data_length = 4;
@@ -1431,17 +1564,21 @@ static void discover_descs_cb(uint8_t opcode, const void *pdu,
goto done;
}
- if ((length - 1) % data_length) {
+ if (iov.iov_len % data_length) {
success = false;
goto done;
}
- if (!result_append(opcode, pdu + 1, length - 1, data_length, op)) {
+ if (!result_append(opcode, iov.iov_base, iov.iov_len, data_length,
+ op)) {
success = false;
goto done;
}
- last_handle = get_le16(pdu + length - data_length);
+ if (!pull_last_le16(pdu, length, data_length, 0, &last_handle)) {
+ success = false;
+ goto done;
+ }
/*
* If last handle is lower from previous start handle then it is smth
@@ -1456,12 +1593,13 @@ static void discover_descs_cb(uint8_t opcode, const void *pdu,
if (last_handle != op->end_handle) {
uint8_t pdu[4];
+ struct iovec req = { pdu, 0 };
- put_le16(op->start_handle, pdu);
- put_le16(op->end_handle, pdu + 2);
+ util_iov_push_le16(&req, op->start_handle);
+ util_iov_push_le16(&req, op->end_handle);
op->id = bt_att_send(op->att, BT_ATT_OP_FIND_INFO_REQ,
- pdu, sizeof(pdu),
+ req.iov_base, req.iov_len,
discover_descs_cb,
bt_gatt_request_ref(op),
async_req_unref);
@@ -1486,6 +1624,7 @@ struct bt_gatt_request *bt_gatt_discover_descriptors(struct bt_att *att,
{
struct bt_gatt_request *op;
uint8_t pdu[4];
+ struct iovec req = { pdu, 0 };
if (!att)
return NULL;
@@ -1498,10 +1637,11 @@ struct bt_gatt_request *bt_gatt_discover_descriptors(struct bt_att *att,
op->start_handle = start;
op->end_handle = end;
- put_le16(start, pdu);
- put_le16(end, pdu + 2);
+ util_iov_push_le16(&req, start);
+ util_iov_push_le16(&req, end);
- op->id = bt_att_send(att, BT_ATT_OP_FIND_INFO_REQ, pdu, sizeof(pdu),
+ op->id = bt_att_send(att, BT_ATT_OP_FIND_INFO_REQ,
+ req.iov_base, req.iov_len,
discover_descs_cb,
bt_gatt_request_ref(op),
async_req_unref);
--
2.56.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH BlueZ v1 04/10] shared/gatt-client: Use util_iov helpers to build and parse PDUs
2026-11-09 15:44 [PATCH BlueZ v1 00/10] shared/gatt: Use util_iov helpers for PDU handling Luiz Augusto von Dentz
` (2 preceding siblings ...)
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
2026-11-09 15:44 ` [PATCH BlueZ v1 05/10] unit/test-gatt: Add malformed PDU tests Luiz Augusto von Dentz
` (6 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-09 15:44 UTC (permalink / raw)
To: linux-bluetooth
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
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH BlueZ v1 05/10] unit/test-gatt: Add malformed PDU tests
2026-11-09 15:44 [PATCH BlueZ v1 00/10] shared/gatt: Use util_iov helpers for PDU handling Luiz Augusto von Dentz
` (3 preceding siblings ...)
2026-11-09 15:44 ` [PATCH BlueZ v1 04/10] shared/gatt-client: " Luiz Augusto von Dentz
@ 2026-11-09 15:44 ` 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
` (5 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-09 15:44 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Add server tests for short and oversized requests, which must be
answered with Invalid PDU, and client tests for truncated or
inconsistent MTU, Read By Group Type, Read By Type and Notification
Multiple responses.
Client tests that are expected to fail initialization use a new
init_fail step flag.
Assisted-by: OpenCode:claude-sonnet-5.5
---
unit/test-gatt.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 175 insertions(+)
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 267bcc61802e..ad8e95453be2 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -352,6 +352,7 @@ struct test_step {
const uint8_t *value;
uint16_t length;
uint8_t features;
+ bool init_fail;
};
static void destroy_context(struct context *context)
@@ -645,6 +646,15 @@ static void match_services(struct gatt_db_attribute *client_serv_attr,
static void client_ready_cb(bool success, uint8_t att_ecode, void *user_data)
{
struct context *context = user_data;
+ const struct test_step *init_step = context->data->step;
+
+ if (init_step && init_step->init_fail) {
+ g_assert(!success);
+
+ /* The client is still in use by the caller, quit when idle */
+ g_idle_add(context_quit, context);
+ return;
+ }
g_assert(success);
@@ -2495,6 +2505,10 @@ static void test_notification_short(struct context *context)
NULL));
}
+static const struct test_step test_client_init_fail = {
+ .init_fail = true,
+};
+
static const struct test_step test_notification_short_1 = {
.handle = 0x0003,
.func = test_notification_short,
@@ -4953,6 +4967,167 @@ int main(int argc, char *argv[])
raw_pdu(0xff, 0x00),
raw_pdu());
+ define_test_client("/robustness/client-mtu-rsp-short", test_client,
+ NULL, &test_client_init_fail,
+ raw_pdu(0x02, 0x00, 0x02),
+ raw_pdu(0x03, 0x00));
+
+ define_test_client("/robustness/client-mtu-rsp-long", test_client,
+ NULL, &test_client_init_fail,
+ raw_pdu(0x02, 0x00, 0x02),
+ raw_pdu(0x03, 0x00, 0x02, 0x00));
+
+ define_test_client("/robustness/client-read-by-grp-type-short",
+ test_client, NULL, &test_client_init_fail,
+ CLIENT_INIT_PDUS,
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x05));
+
+ define_test_client("/robustness/client-read-by-grp-type-data-len",
+ test_client, NULL, &test_client_init_fail,
+ CLIENT_INIT_PDUS,
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x07, 0x01, 0x00, 0x05, 0x00, 0x01, 0x18,
+ 0x00));
+
+ define_test_client("/robustness/client-read-by-grp-type-trailing",
+ test_client, NULL, &test_client_init_fail,
+ CLIENT_INIT_PDUS,
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x05, 0x00, 0x01, 0x18,
+ 0x02));
+
+ define_test_client("/robustness/client-included-short", test_client,
+ NULL, &test_client_init_fail,
+ CLIENT_INIT_PDUS,
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),
+ raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x01, 0x10, 0x05, 0x00, 0x0a),
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28),
+ raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a),
+ raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28),
+ raw_pdu(0x09, 0x08, 0x02, 0x00, 0x01, 0x00, 0x04));
+
+ define_test_client("/robustness/client-characteristic-short",
+ test_client, NULL, &test_client_init_fail,
+ CLIENT_INIT_PDUS,
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),
+ raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x01, 0x10, 0x05, 0x00, 0x0a),
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28),
+ raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a),
+ raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+ raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x02, 0x00, 0x02, 0x03, 0x00,
+ 0x00));
+
+ define_test_client("/robustness/notify-multiple-short", test_client,
+ ts_small_db, &test_notification_short_1,
+ CLIENT_INIT_PDUS,
+ SMALL_DB_DISCOVERY_PDUS,
+ raw_pdu(0x12, 0x04, 0x00, 0x01, 0x00),
+ raw_pdu(0x13),
+ raw_pdu(),
+ raw_pdu(0x23, 0x03, 0x00, 0x03, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(),
+ raw_pdu(0x23, 0x03, 0x00, 0x03),
+ raw_pdu(),
+ raw_pdu(0x23, 0x03, 0x00, 0x09, 0x00, 0x01, 0x02,
+ 0x03));
+
+ define_test_server("/robustness/read-by-grp-type-malformed",
+ test_server, ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff),
+ raw_pdu(0x01, 0x10, 0x00, 0x00, 0x04),
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28,
+ 0x29),
+ raw_pdu(0x01, 0x10, 0x00, 0x00, 0x04));
+
+ define_test_server("/robustness/read-by-type-malformed",
+ test_server, ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff),
+ raw_pdu(0x01, 0x08, 0x00, 0x00, 0x04),
+ raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28,
+ 0x29),
+ raw_pdu(0x01, 0x08, 0x00, 0x00, 0x04));
+
+ define_test_server("/robustness/find-info-malformed",
+ test_server, ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x04, 0x01, 0x00, 0xff),
+ raw_pdu(0x01, 0x04, 0x00, 0x00, 0x04),
+ raw_pdu(0x04, 0x01, 0x00, 0xff, 0xff, 0x00),
+ raw_pdu(0x01, 0x04, 0x00, 0x00, 0x04));
+
+ define_test_server("/robustness/find-by-type-value-malformed",
+ test_server, ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00),
+ raw_pdu(0x01, 0x06, 0x00, 0x00, 0x04));
+
+ define_test_server("/robustness/read-malformed",
+ test_server, ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0a, 0x03),
+ raw_pdu(0x01, 0x0a, 0x00, 0x00, 0x04),
+ raw_pdu(0x0a, 0x03, 0x00, 0x00),
+ raw_pdu(0x01, 0x0a, 0x00, 0x00, 0x04));
+
+ define_test_server("/robustness/read-blob-malformed",
+ test_server, ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0c, 0x03, 0x00, 0x00),
+ raw_pdu(0x01, 0x0c, 0x00, 0x00, 0x04),
+ raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00, 0x00),
+ raw_pdu(0x01, 0x0c, 0x00, 0x00, 0x04));
+
+ define_test_server("/robustness/read-multiple-malformed",
+ test_server, ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0e, 0x03, 0x00),
+ raw_pdu(0x01, 0x0e, 0x00, 0x00, 0x04),
+ raw_pdu(0x20, 0x03, 0x00),
+ raw_pdu(0x01, 0x20, 0x00, 0x00, 0x04));
+
+ define_test_server("/robustness/write-malformed",
+ test_server, ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x04),
+ raw_pdu(0x01, 0x12, 0x00, 0x00, 0x04));
+
+ define_test_server("/robustness/write-cmd-malformed",
+ test_server, ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x52, 0x04),
+ raw_pdu());
+
+ define_test_server("/robustness/prep-write-malformed",
+ test_server, ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x03, 0x00, 0x00),
+ raw_pdu(0x01, 0x16, 0x00, 0x00, 0x04));
+
+ define_test_server("/robustness/exec-write-malformed",
+ test_server, ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x18),
+ raw_pdu(0x01, 0x18, 0x00, 0x00, 0x04),
+ raw_pdu(0x18, 0x01, 0x00),
+ raw_pdu(0x01, 0x18, 0x00, 0x00, 0x04));
+
+ define_test_server("/robustness/mtu-malformed",
+ test_server, ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x02, 0x17),
+ raw_pdu(0x01, 0x02, 0x00, 0x00, 0x04),
+ raw_pdu(0x02, 0x17, 0x00, 0x00),
+ raw_pdu(0x01, 0x02, 0x00, 0x00, 0x04));
+
define_test_server("/robustness/hash-db",
test_hash_db, ts_tail_db, NULL,
{});
--
2.56.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH BlueZ v1 06/10] shared/gatt-server: Fix authorize callback getting handle 0 on write
2026-11-09 15:44 [PATCH BlueZ v1 00/10] shared/gatt: Use util_iov helpers for PDU handling Luiz Augusto von Dentz
` (4 preceding siblings ...)
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 ` 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
` (4 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-09 15:44 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
write_cb called authorize_req before decoding the handle from the PDU,
so the authorize callback always saw handle 0 for Write Request, Write
Command and Signed Write Command.
Decode the handle first and add a test checking the handle that is
passed to the callback.
Fixes: 291e9a0d8e6e ("shared/gatt-server: Add bt_gatt_server_set_authorize")
Assisted-by: OpenCode:claude-sonnet-5.5
---
src/shared/gatt-server.c | 4 +---
unit/test-gatt.c | 39 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 39 insertions(+), 4 deletions(-)
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index 6796817bf324..b63745c4df2e 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -838,7 +838,7 @@ static void write_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode,
struct async_write_op *op = NULL;
uint8_t ecode;
- if (iov.iov_len < sizeof(handle)) {
+ if (!util_iov_pull_le16(&iov, &handle)) {
ecode = BT_ATT_ERROR_INVALID_PDU;
goto error;
}
@@ -847,8 +847,6 @@ static void write_cb(struct bt_att_chan *chan, uint16_t mtu, uint8_t opcode,
if (ecode)
goto error;
- util_iov_pull_le16(&iov, &handle);
-
attr = gatt_db_get_attribute(server->db, handle);
if (!attr) {
ecode = BT_ATT_ERROR_INVALID_HANDLE;
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index ad8e95453be2..bf60741077c5 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -2008,7 +2008,7 @@ static void test_client(gconstpointer data)
create_context(512, data);
}
-static void test_server(gconstpointer data)
+static struct context *create_server_context(gconstpointer data)
{
struct context *context = create_context(512, data);
ssize_t len;
@@ -2021,8 +2021,38 @@ static void test_server(gconstpointer data)
tester_monitor('<', 0x0004, 0x0000, pdu.data, len);
g_free(pdu.data);
+
+ return context;
+}
+
+static void test_server(gconstpointer data)
+{
+ create_server_context(data);
}
+static uint8_t server_authorize_cb(struct bt_att *att, uint8_t opcode,
+ uint16_t handle, void *user_data)
+{
+ struct context *context = user_data;
+ const struct test_step *step = context->data->step;
+
+ g_assert_cmpint(handle, ==, step->handle);
+
+ return 0;
+}
+
+static void test_server_authorize(gconstpointer data)
+{
+ struct context *context = create_server_context(data);
+
+ g_assert(bt_gatt_server_set_authorize(context->server,
+ server_authorize_cb, context));
+}
+
+static const struct test_step test_server_authorize_1 = {
+ .handle = 0x0004,
+};
+
static void test_search_primary(gconstpointer data)
{
struct context *context = create_context(512, data);
@@ -5038,6 +5068,13 @@ int main(int argc, char *argv[])
raw_pdu(0x23, 0x03, 0x00, 0x09, 0x00, 0x01, 0x02,
0x03));
+ define_test_server("/robustness/write-authorize-handle",
+ test_server_authorize, ts_small_db,
+ &test_server_authorize_1,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x04, 0x00, 0x01, 0x00),
+ raw_pdu(0x13));
+
define_test_server("/robustness/read-by-grp-type-malformed",
test_server, ts_small_db, NULL,
raw_pdu(0x03, 0x00, 0x02),
--
2.56.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH BlueZ v1 07/10] shared/gatt-server: Fix double response on prepare write failure
2026-11-09 15:44 [PATCH BlueZ v1 00/10] shared/gatt: Use util_iov helpers for PDU handling Luiz Augusto von Dentz
` (5 preceding siblings ...)
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 ` 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
` (3 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-09 15:44 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
When storing the prepared data fails, prep_write_complete_cb sent an
error response and then also the Prepare Write Response. The error
response also used the response opcode instead of the request opcode.
Send only the error response, with the request opcode.
Fixes: 1ebfc68f ("shared/gatt-server: Request authorization for prepare writes")
Assisted-by: OpenCode:claude-sonnet-5.5
---
src/shared/gatt-server.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index b63745c4df2e..4a6bdb4677ab 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -1310,10 +1310,15 @@ static void prep_write_complete_cb(struct gatt_db_attribute *attr, int err,
}
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,
+ iov.iov_base)) {
+ bt_att_chan_send_error_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_REQ,
handle,
BT_ATT_ERROR_INSUFFICIENT_RESOURCES);
+ free(pwcd->pdu);
+ free(pwcd);
+
+ return;
+ }
bt_att_chan_send_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_RSP, pwcd->pdu,
pwcd->length);
--
2.56.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH BlueZ v1 08/10] shared/gatt-server: Fix return value of handler registration
2026-11-09 15:44 [PATCH BlueZ v1 00/10] shared/gatt: Use util_iov helpers for PDU handling Luiz Augusto von Dentz
` (6 preceding siblings ...)
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 ` 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
` (2 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-09 15:44 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
gatt_server_register_att_handlers returns bool but returned NULL when
registering the Execute Write handler failed.
Fixes: b3e2fa94 ("shared/gatt-server: Implement "Execute Write" request.")
Assisted-by: OpenCode:claude-sonnet-5.5
---
src/shared/gatt-server.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index 4a6bdb4677ab..4ec4a59993a2 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -1653,7 +1653,7 @@ static bool gatt_server_register_att_handlers(struct bt_gatt_server *server)
BT_ATT_OP_EXEC_WRITE_REQ,
exec_write_cb, server, NULL);
if (!server->exec_write_id)
- return NULL;
+ return false;
/* Signed Write Command */
server->signed_write_cmd_id = bt_att_register(server->att,
--
2.56.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH BlueZ v1 09/10] shared/att: Fix reference leak on signature verification failure
2026-11-09 15:44 [PATCH BlueZ v1 00/10] shared/gatt: Use util_iov helpers for PDU handling Luiz Augusto von Dentz
` (7 preceding siblings ...)
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 ` 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
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-09 15:45 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
handle_notify takes a reference on the bt_att but returned without
dropping it when the signature of a Signed Write Command could not be
verified, so the bt_att was never freed.
Fixes: 051a525c ("shared/att.c: Add server signing key support")
Assisted-by: OpenCode:claude-sonnet-5.5
---
src/shared/att.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/shared/att.c b/src/shared/att.c
index 4fa69e597556..d1930f6af1d1 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -1044,8 +1044,10 @@ static void handle_notify(struct bt_att_chan *chan, uint8_t *pdu,
continue;
if ((opcode & ATT_OP_SIGNED_MASK) && att->crypto) {
- if (!handle_signed(att, pdu, pdu_len))
+ if (!handle_signed(att, pdu, pdu_len)) {
+ bt_att_unref(att);
return;
+ }
pdu_len -= BT_ATT_SIGNATURE_LEN;
}
--
2.56.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH BlueZ v1 10/10] shared/att: Fix signature length subtracted once per handler
2026-11-09 15:44 [PATCH BlueZ v1 00/10] shared/gatt: Use util_iov helpers for PDU handling Luiz Augusto von Dentz
` (8 preceding siblings ...)
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 ` 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
10 siblings, 0 replies; 13+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-09 15:45 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
handle_notify verified the signature and subtracted its length from
pdu_len inside the loop over the registered handlers, so with more than
one handler for a Signed Write Command the later handlers were passed a
truncated PDU and the signature was verified against it again.
Verify the signature and strip it only once.
Fixes: 051a525c ("shared/att.c: Add server signing key support")
Assisted-by: OpenCode:claude-sonnet-5.5
---
src/shared/att.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/shared/att.c b/src/shared/att.c
index d1930f6af1d1..98e6e23eca82 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -1028,6 +1028,7 @@ static void handle_notify(struct bt_att_chan *chan, uint8_t *pdu,
struct bt_att *att = chan->att;
const struct queue_entry *entry;
bool found;
+ bool verified = false;
uint8_t opcode = pdu[0];
bt_att_ref(att);
@@ -1043,12 +1044,15 @@ static void handle_notify(struct bt_att_chan *chan, uint8_t *pdu,
if (!opcode_match(notify->opcode, opcode))
continue;
- if ((opcode & ATT_OP_SIGNED_MASK) && att->crypto) {
+ if ((opcode & ATT_OP_SIGNED_MASK) && att->crypto && !verified) {
if (!handle_signed(att, pdu, pdu_len)) {
bt_att_unref(att);
return;
}
+
+ /* Verify and strip the signature only once */
pdu_len -= BT_ATT_SIGNATURE_LEN;
+ verified = true;
}
/* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G
--
2.56.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH BlueZ v1 00/10] shared/gatt: Use util_iov helpers for PDU handling
2026-11-09 15:44 [PATCH BlueZ v1 00/10] shared/gatt: Use util_iov helpers for PDU handling Luiz Augusto von Dentz
` (9 preceding siblings ...)
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 ` patchwork-bot+bluetooth
10 siblings, 0 replies; 13+ messages in thread
From: patchwork-bot+bluetooth @ 2026-11-10 16:40 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
Hello:
This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
On Mon, 9 Nov 2026 10:44:51 -0500 you wrote:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>
> This converts src/shared/{att,gatt-server,gatt-client,gatt-helpers} to
> build and parse PDUs with the util_iov_pull_* and util_iov_push_*
> helpers rather than pointer arithmetic on the PDU, so that the bounds
> check is part of the access.
>
> [...]
Here is the summary with links:
- [BlueZ,v1,01/10] shared/att: Use util_iov helpers to build and parse PDUs
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=8d0c7e9ef0e5
- [BlueZ,v1,02/10] shared/gatt-server: Use util_iov helpers to build and parse PDUs
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=121ee5d9cbd0
- [BlueZ,v1,03/10] shared/gatt-helpers: Use util_iov helpers to build and parse PDUs
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=414080ffdb8b
- [BlueZ,v1,04/10] shared/gatt-client: Use util_iov helpers to build and parse PDUs
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=b25e8e5d5a51
- [BlueZ,v1,05/10] unit/test-gatt: Add malformed PDU tests
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=bd9f53de7215
- [BlueZ,v1,06/10] shared/gatt-server: Fix authorize callback getting handle 0 on write
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=e927cda1b408
- [BlueZ,v1,07/10] shared/gatt-server: Fix double response on prepare write failure
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=6766a8172cc1
- [BlueZ,v1,08/10] shared/gatt-server: Fix return value of handler registration
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=0de968d4f2de
- [BlueZ,v1,09/10] shared/att: Fix reference leak on signature verification failure
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=ebd56ce3f4ee
- [BlueZ,v1,10/10] shared/att: Fix signature length subtracted once per handler
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=8f8bbbff3d10
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 13+ messages in thread