Per @zxzxwu review on #918:
- bumble/avdtp.py: replace `if len(pdu) < 1:` with `if not pdu:`
- tests/avdtp_test.py: replace `assert completed == []` with
`assert not completed`
Both are idiomatic Python truthy checks; behavior identical.
A remote peer can send an AVDTP frame shorter than the assembler expects.
The current MessageAssembler.on_pdu() unconditionally accesses pdu[0],
pdu[1], and (for START packets) pdu[2], so a 0-, 1-, or 2-byte frame
raises IndexError. The exception propagates up through L2CAP's read loop
and tears down the channel — same DoS class as #912 (empty ATT PDU) and
#914 (unbounded SDP recursion).
Fix: validate length before each access. Empty PDUs and packets shorter
than the type-specific minimum are logged and dropped; the assembler
stays alive so the L2CAP channel is not torn down.
- bumble/avdtp.py: length guards in MessageAssembler.on_pdu before
accessing pdu[0], pdu[1], pdu[2].
- tests/avdtp_test.py: regression test covering empty PDU, 1-byte SINGLE,
1-byte START, 2-byte START — all four would have raised IndexError
pre-fix; assembler now drops without raising.
- bumble/sdp.py: replace raise ValueError with raise InvalidPacketError
in DataElement.list_from_bytes depth guard. InvalidPacketError
already imported at line 34 and extends ValueError so the existing
regression test continues to match.
- tests/sdp_test.py: remove duplicate 'import pytest' inside
test_nested_sequence_recursion_guard; pytest already imported at
module top (line 23).
Threading.local counter left as-is per zxzxwu's 'leave it here and
refactor later' comment on the PR.
DataElement.from_bytes -> list_from_bytes -> (SEQUENCE/ALTERNATIVE
constructor dispatches back to list_from_bytes) had no depth limit. A
malicious SDP peer could send a PDU of a few kilobytes containing ~1000
nested SEQUENCE tags and exhaust the Python recursion stack, crashing the
host with an unhandled RecursionError propagating out of the SDP handler.
Reachable via: any remote Bluetooth device that Bumble performs SDP
service discovery against (default during Classic connection setup).
Same family as PR #912 (ATT_PDU.from_bytes empty PDU IndexError) - remote
unchecked-input parser crash in the Bluetooth stack.
Fix: thread-local depth counter, cap nesting at 32 (well above anything a
legitimate service record uses). Added two regression tests covering the
deep-nesting reject path and normal 16-level-nested SEQUENCE parsing.
Reproducer (4.5 KB payload, deterministic crash on 0.0.228):
from bumble.sdp import DataElement
inner = b"\x35\x00"
for _ in range(1500):
size = len(inner)
if size < 65535:
inner = bytes([0x36, (size >> 8) & 0xFF, size & 0xFF]) + inner
DataElement.from_bytes(inner) # RecursionError before fix
Signed-off-by: ibondarenko1 <ibondarenko1@users.noreply.github.com>
- att.py: raise core.InvalidPacketError instead of generic ValueError
- smp.py: raise core.InvalidPacketError instead of generic ValueError
- hfp.py: add MAX_BUFFER_SIZE class constant (64KB)
- hfp.py: drop incoming data when it would overflow buffer instead of
truncating, preserving existing partial-packet state
Per review comments on PR #912 by @zxzxwu.
Add length checks in from_bytes() for ATT and SMP protocol parsers
to prevent IndexError crashes from empty PDUs sent by remote Bluetooth
devices. Also add buffer size limit and UTF-8 error handling in HFP
protocol to prevent memory exhaustion and decode crashes.
- bumble/att.py: validate PDU is non-empty before accessing pdu[0]
- bumble/smp.py: validate PDU is non-empty before accessing pdu[0]
- bumble/hfp.py: limit buffer to 64KB, handle invalid UTF-8 gracefully
These issues can be triggered by a remote Bluetooth device sending
malformed packets, causing denial of service on the host.
Prior to this, these web pages fail to load with
`ImportError: cannot import name 'TypeIs' from 'typing_extensions'
(/lib/python3.11/site-packages/typing_extensions.py)`
Prior to this, these web pages fail to load with
`ValueError: Can't find a pure Python 3 wheel for 'cryptography>=44.0.3;
platform_system == "Emscripten"'.`