@@ -1113,6 +1113,40 @@ def test_parse_advertisement_data_32bit_uuid_malformed_length():
11131113 assert adv .service_uuids == ["efbeadde-0000-1000-8000-00805f9b34fb" ]
11141114
11151115
1116+ def test_parse_advertisement_data_32bit_uuid_high_bit_set ():
1117+ """32-bit UUIDs with bit 31 set must decode as the unsigned value.
1118+
1119+ Guards the signed-shift UB fix: ``gap_data[i + 3] << 24`` on an
1120+ ``unsigned char`` promotes to (signed) ``int`` in C, so values >= 0x80
1121+ in the top byte would be undefined behavior under Cython without the
1122+ intermediate ``unsigned int`` staging. The decoded UUID must reflect
1123+ the full unsigned 32-bit value, not a sign-extended negative.
1124+ """
1125+ # length=0x09 (9 = 1 type + 8 bytes), type=0x05 — two 32-bit UUIDs
1126+ # in little-endian: 0xFF112233 -> 33 22 11 FF, 0x80000001 -> 01 00 00 80.
1127+ payload = b"\x09 \x05 \x33 \x22 \x11 \xff \x01 \x00 \x00 \x80 "
1128+ adv = parse_advertisement_data ((payload ,))
1129+ assert adv .service_uuids == [
1130+ "ff112233-0000-1000-8000-00805f9b34fb" ,
1131+ "80000001-0000-1000-8000-00805f9b34fb" ,
1132+ ]
1133+
1134+
1135+ def test_parse_advertisement_data_32bit_service_data_high_bit_set ():
1136+ """32-bit service-data UUID with bit 31 set must key on the unsigned value.
1137+
1138+ Same UB guard as the list branch, applied to the
1139+ ``TYPE_SERVICE_DATA`` 32-bit decode.
1140+ """
1141+ # length=0x07 (7 = 1 type + 4 UUID bytes + 2 data bytes), type=0x20
1142+ # UUID little-endian: 0xFF112233 -> 33 22 11 FF; data: AA BB.
1143+ payload = b"\x07 \x20 \x33 \x22 \x11 \xff \xaa \xbb "
1144+ adv = parse_advertisement_data ((payload ,))
1145+ assert adv .service_data == {
1146+ "ff112233-0000-1000-8000-00805f9b34fb" : b"\xaa \xbb " ,
1147+ }
1148+
1149+
11161150def test_parse_advertisement_data_multiple_manufacturer_entries ():
11171151 """Two manufacturer-specific-data AD structs in one packet must both
11181152 survive into the resulting dict, keyed by company ID.
0 commit comments