Skip to content

Commit 4517c45

Browse files
committed
fix(gap): cast UUID bytes to unsigned int before shift to avoid signed UB
1 parent 721f76a commit 4517c45

1 file changed

Lines changed: 17 additions & 12 deletions

File tree

  • src/bluetooth_data_tools

src/bluetooth_data_tools/gap.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
from functools import lru_cache, partial
77
from typing import TYPE_CHECKING
88

9+
import cython
10+
911
BLE_UUID = "0000-1000-8000-00805f9b34fb"
1012
_LOGGER = logging.getLogger(__name__)
1113

@@ -265,15 +267,16 @@ def _uncached_parse_advertisement_bytes(
265267
# per-iteration bounds branch; any 1-3 byte tail is dropped.
266268
safe_end = start + ((end - start) & ~3)
267269
for i in range(start, safe_end, 4):
268-
# Assemble via uint local: in Cython the shift-by-24 of an
269-
# unsigned char promotes to signed int and would yield a
270-
# negative value when bit 31 is set; assigning to a
271-
# cython.uint local recovers the unsigned 32-bit value.
270+
# Cast each byte to unsigned int before shifting: in C an
271+
# `unsigned char` operand of `<<` is promoted to (signed)
272+
# `int`, so `gap_data[i + 3] << 24` would be undefined
273+
# behavior when bit 31 is set. Casting keeps the operation
274+
# well-defined on `unsigned int`.
272275
uuid32_int = (
273-
gap_data[i]
274-
| (gap_data[i + 1] << 8)
275-
| (gap_data[i + 2] << 16)
276-
| (gap_data[i + 3] << 24)
276+
cython.cast(cython.uint, gap_data[i])
277+
| (cython.cast(cython.uint, gap_data[i + 1]) << 8)
278+
| (cython.cast(cython.uint, gap_data[i + 2]) << 16)
279+
| (cython.cast(cython.uint, gap_data[i + 3]) << 24)
277280
)
278281
service_uuids.append(_cached_uint32_int_as_uuid(uuid32_int))
279282
elif gap_type_num in {
@@ -306,11 +309,13 @@ def _uncached_parse_advertisement_bytes(
306309
continue
307310
if service_data is _EMPTY_SERVICE_DATA:
308311
service_data = {}
312+
# Cast each byte to unsigned int before shifting to keep the
313+
# << 24 well-defined (see TYPE_32BIT_SERVICE_UUID_* branch above).
309314
uuid32_int = (
310-
gap_data[start]
311-
| (gap_data[start + 1] << 8)
312-
| (gap_data[start + 2] << 16)
313-
| (gap_data[start + 3] << 24)
315+
cython.cast(cython.uint, gap_data[start])
316+
| (cython.cast(cython.uint, gap_data[start + 1]) << 8)
317+
| (cython.cast(cython.uint, gap_data[start + 2]) << 16)
318+
| (cython.cast(cython.uint, gap_data[start + 3]) << 24)
314319
)
315320
service_data[_cached_uint32_int_as_uuid(uuid32_int)] = gap_data[
316321
splice_pos:end

0 commit comments

Comments
 (0)