* [bluez/bluez] a678fa: shared/gatt-client: verify a synthesized CCC befor...
@ 2026-09-04 23:15 Proxy Alt
0 siblings, 0 replies; only message in thread
From: Proxy Alt @ 2026-09-04 23:15 UTC (permalink / raw)
To: linux-bluetooth
Branch: refs/heads/1158378
Home: https://github.com/bluez/bluez
Commit: a678fa2d00fa989b073d882a87149a65907df8b7
https://github.com/bluez/bluez/commit/a678fa2d00fa989b073d882a87149a65907df8b7
Author: Proxy <proxy-alt@proxy-alt.dev>
Date: 2026-09-04 (Fri, 04 Sep 2026)
Changed paths:
M src/shared/gatt-client.c
Log Message:
-----------
shared/gatt-client: verify a synthesized CCC before writing
discover_descs() still synthesizes a 0x2902 for a notify/indicate
characteristic's lone descriptor without ever asking the peer - that
part is unchanged, since always discovering costs a round trip on
every characteristic for the sake of devices that violate Vol 3, Part
G 3.3.1.1. What changes is register_notify(): before it writes to a
handle discover_descs() only guessed at, it now issues one
single-handle FIND_INFORMATION to let the peer answer for itself, and
only after that answer confirms a real 0x2902 does the CCC write
happen at all.
If the peer's answer is anything else - a different UUID, or no
answer - chrc->ccc_handle is cleared instead of written to.
register_notify() already handles a characteristic with no CCC
correctly (gatt_db_attribute_get_ccc() returning NULL takes the same
path), so this reaches that existing, correct behaviour instead of
writing 0x0100 into an attribute the peer never claimed was a CCC.
v3 fixes two real bugs v2 had, both found by actually running it
against real hardware and then a real unit test rather than trusting
that it read correctly:
1. unverified_ccc lived on struct bt_gatt_client, but discover_descs()
only ever runs on the root client, while register_notify() is
commonly called through a clone (bt_gatt_client_clone(), used by
src/gatt-client.c per D-Bus consumer / by profile implementations
like bt_micp) - whose own copy of that queue is always empty. The
verify step silently never triggered. Fixed with root_client(), a
two-line walk up ->parent, used at both call sites instead of
client->unverified_ccc directly.
2. Once (1) was fixed and verify genuinely ran, two more bugs showed
up together under unit/test-micp: the CCC write could get skipped
entirely, and requests the client issued after it could go out on
the wire ahead of the (still in-flight) verify - reordering ATT
traffic relative to what every existing caller of register_notify()
was written to expect from a synchronous write.
Root cause: bt_gatt_discover_descriptors(), which the verify step
uses, is not tracked in client->pending_requests the way
bt_gatt_client_write_value()/read are. notify_client_idle() only
watches pending_requests, so it considered the client idle - and
fired every registered idle callback, letting application code run
- while the verify FIND_INFORMATION was still genuinely outstanding
on the wire. That is what let a later application write jump ahead
of the CCC write register_notify() had not had a chance to send
yet. Separately, resume_after_ccc_verify() used the same
"notify_count > 1 means someone already wrote it" check
register_notify() itself uses - correct there, but not after an
async gap, since other callers for the same characteristic can
(correctly) queue up behind chrc->ccc_verify_req and bump
notify_count before verification even resolves, with nobody having
written anything yet.
Fixed both: notify_client_idle() now also checks whether any
notify_chrc on the client still has a CCC verify outstanding before
firing idle callbacks, chrc_has_pending_ccc_verify() added for that;
resume_after_ccc_verify() no longer rechecks notify_count, since by
construction this is the first and only place that can write once a
characteristic was ccc_unverified; and verify_ccc_cb() calls
notify_client_idle() itself once verification concludes without a
write, since nothing else naturally rechecks idle for a request
this codebase's idle-tracking never had to account for before now.
Also fixes a real leak (3 struct bt_gatt_result plus their backing
allocations, confirmed via LeakSanitizer against the same test):
verify_ccc_cb() never released the reference
bt_gatt_discover_descriptors() returns, unlike every other discovery
completion in this file (see discovery_req_clear()). Fixed with the
same bt_gatt_request_unref() pattern.
unit/test-micp.c is updated to match: three subtests
(MICP/CL/CGGIT/SER/BV-01-C, MICP/CL/CGGIT/CHA/BV-01-C, MICP/CL/SPE/BI-01-C)
drive a real notify-enable through a synthesized CCC and now expect the
FIND_INFORMATION exchange this patch adds before the CCC write. With
v2 (before the fixes above), this test reproduced the original bug
exactly as reported: a WRITE_REQ to the synthesized handle that got no
response and would have hung until the 30s ATT timeout in a real
session. With v3, ./unit/test-micp passes 7/7 with zero leaks under
LeakSanitizer.
unit/test-mcp.c and unit/test-bap.c hit the same class of pre-existing
mock-script gap (their own CCC-enable sequences need the equivalent
FIND_INFORMATION step added) but I have not finished updating those
yet - flagging rather than shipping a partial fix for them silently.
Cost: one extra FIND_INFORMATION per notify/indicate characteristic
whose sole descriptor was synthesized, the first time register_notify()
is called for it.
Fixes: https://github.com/bluez/bluez/issues/2383
Signed-off-by: Proxy <proxy-alt@proxy-alt.dev>
Commit: 063d77929b6c5e39516f066399c8edc1c1041bc2
https://github.com/bluez/bluez/commit/063d77929b6c5e39516f066399c8edc1c1041bc2
Author: Proxy <proxy-alt@proxy-alt.dev>
Date: 2026-09-04 (Fri, 04 Sep 2026)
Changed paths:
M unit/test-micp.c
Log Message:
-----------
unit/test-micp: expect the FIND_INFORMATION the CCC fix adds
Companion to the shared/gatt-client.c fix (bluez/bluez#2383): the
lazy-verify patch adds one FIND_INFORMATION exchange before the first
CCC write to a characteristic whose descriptor discover_descs()
synthesized. MICS_MUTE's CCC (handle 0x0004) is exactly that case, so
the three client subtests that drive it now need that exchange in
their scripted ATT sequence, right where register_notify() actually
issues it (after the value read, before the CCC-enable write).
Verified: ./unit/test-micp passes 7/7 with zero leaks under
LeakSanitizer, against the corresponding shared/gatt-client.c fix.
unit/test-mcp.c and unit/test-bap.c need the equivalent update for
their own synthesized-CCC subtests; not included here.
Signed-off-by: Proxy <proxy-alt@proxy-alt.dev>
Compare: https://github.com/bluez/bluez/compare/a678fa2d00fa%5E...063d77929b6c
To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-09-04 23:15 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-04 23:15 [bluez/bluez] a678fa: shared/gatt-client: verify a synthesized CCC befor Proxy Alt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox