chipweinberger / flutter_blue_plus

Flutter plugin for connecting and communicationg with Bluetooth Low Energy devices, on Android, iOS, macOS
Other
788 stars 478 forks source link

[Fix] iOS: crash caused by 1-byte advertisements #1022

Closed cervm closed 1 month ago

cervm commented 1 month ago

Our app was crashing when a nearby device advertised with only 1 byte of data. This was due to the assumption that the first two bytes always contained the manufacturer ID. This fix prevents going to a negative index causing the app to crash completely.

Screenshot 2024-10-17 at 13 18 30
chipweinberger commented 1 month ago

thanks

chipweinberger commented 1 month ago

fixed in 1.33.5

MrCsabaToth commented 1 month ago

What device was that? I may check the Android port if I have time for this case.

chipweinberger commented 1 month ago

I don't see a way it could break.

lets say bytes.length is 2

fieldLen is 1

and n is 1

then this check will still work // Ensuring we don't go past the bytes array

    Map<Integer, byte[]> getManufacturerSpecificData(ScanRecord adv) {
        byte[] bytes = adv.getBytes();
        Map<Integer, byte[]> manufacturerDataMap = new HashMap<>();
        int n = 0;
        while (n < bytes.length) {

            // layout:
            // n[0] = fieldlen
            // n[1] = datatype (MSD)
            // n[2] = manufacturerId (low)
            // n[3] = manufacturerId (high)
            // n[4] = data...
            int fieldLen = bytes[n] & 0xFF;

            // no more or malformed data
            if (fieldLen <= 0) {
                break;
            }

            // Ensuring we don't go past the bytes array
            if (n + fieldLen >= bytes.length) {
                break;
            }

            int dataType = bytes[n + 1] & 0xFF;

            // Manufacturer Specific Data magic number
            // At least 3 bytes: 2 for manufacturer ID & 1 for dataType
            if (dataType == 0xFF && fieldLen >= 3) {

                // Manufacturer Id
                int high = (bytes[n + 3] & 0xFF) << 8;
                int low = (bytes[n + 2] & 0xFF);
                int manufacturerId = high | low;

                // the length of the msd data,
                // excluding manufacturerId & dataType
                int msdLen = fieldLen - 3;

                // ptr to msd data
                // excluding manufacturerId & dataType
                int msdPtr = n + 4;

                // add to map
                if (manufacturerDataMap.containsKey(manufacturerId)) {
                    // If the manufacturer ID already exists, append the new data to the existing list
                    byte[] existingData = manufacturerDataMap.get(manufacturerId);
                    byte[] mergedData = new byte[existingData.length + msdLen];
                    // Merge arrays
                    System.arraycopy(existingData, 0, mergedData, 0, existingData.length);
                    System.arraycopy(bytes, msdPtr, mergedData, existingData.length, msdLen);
                    manufacturerDataMap.put(manufacturerId, mergedData);
                } else {
                    // Otherwise, put the new manufacturer ID and its data into the map
                    byte[] data = new byte[msdLen];
                    // Starting from n+4 because manufacturerId occupies n+2 and n+3
                    System.arraycopy(bytes, msdPtr, data, 0, data.length);
                    manufacturerDataMap.put(manufacturerId, data);
                }
            }

            n += fieldLen + 1;
        }

        return manufacturerDataMap;
    }