Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cc8d1146 authored by Ying Hsu's avatar Ying Hsu
Browse files

floss: Validate advertisement data

This patch validates advertisement data used in IBluetoothGatt
advertising API before actually starting or updating an advertising
set.

Bug: 284234145
Tag: #floss
Test: manual test with btclient
Test: nearby sharing and phone hub on brya
Change-Id: I3dfcdbb3eb15ce955491ecf29573614c5ed12f82
parent e759dd8d
Loading
Loading
Loading
Loading
+24 −2
Original line number Diff line number Diff line
@@ -194,6 +194,9 @@ const SOLICIT_AD_TYPES: [u8; 3] = [
    LIST_128_BIT_SERVICE_SOLICITATION_UUIDS,
];

const LEGACY_ADV_DATA_LEN_MAX: usize = 31;
const EXT_ADV_DATA_LEN_MAX: usize = 254;

// Invalid advertising set id.
const INVALID_ADV_ID: i32 = 0xff;

@@ -341,6 +344,11 @@ impl AdvertiseData {
        AdvertiseData::append_transport_discovery_data(&mut bytes, &self.transport_discovery_data);
        bytes
    }

    /// Validates the raw data as advertisement data.
    pub fn validate_raw_data(is_legacy: bool, bytes: &Vec<u8>) -> bool {
        bytes.len() <= if is_legacy { LEGACY_ADV_DATA_LEN_MAX } else { EXT_ADV_DATA_LEN_MAX }
    }
}

impl Into<bt_topshim::profiles::gatt::PeriodicAdvertisingParameters>
@@ -394,10 +402,18 @@ pub(crate) struct AdvertisingSetInfo {
    /// Maximum number of extended advertising events the controller
    /// shall attempt to send before terminating the extended advertising.
    adv_events: u8,

    /// Whether the legacy advertisement will be used.
    legacy: bool,
}

impl AdvertisingSetInfo {
    pub(crate) fn new(callback_id: CallbackId, adv_timeout: u16, adv_events: u8) -> Self {
    pub(crate) fn new(
        callback_id: CallbackId,
        adv_timeout: u16,
        adv_events: u8,
        legacy: bool,
    ) -> Self {
        let mut reg_id = REG_ID_COUNTER.fetch_add(1, Ordering::SeqCst) as RegId;
        if reg_id == INVALID_REG_ID {
            reg_id = REG_ID_COUNTER.fetch_add(1, Ordering::SeqCst) as RegId;
@@ -410,6 +426,7 @@ impl AdvertisingSetInfo {
            paused: false,
            adv_timeout,
            adv_events,
            legacy,
        }
    }

@@ -463,6 +480,11 @@ impl AdvertisingSetInfo {
    pub(crate) fn adv_events(&self) -> u8 {
        self.adv_events
    }

    /// Returns whether the legacy advertisement will be used.
    pub(crate) fn is_legacy(&self) -> bool {
        self.legacy
    }
}

// Manages advertising sets and the callbacks.
+30 −1
Original line number Diff line number Diff line
@@ -2012,10 +2012,19 @@ impl IBluetoothGatt for BluetoothGatt {
        }

        let device_name = self.get_adapter_name();
        let is_legacy = parameters.is_legacy;
        let params = parameters.into();
        let adv_bytes = advertise_data.make_with(&device_name);
        if !AdvertiseData::validate_raw_data(is_legacy, &adv_bytes) {
            log::warn!("Failed to start advertising set with invalid advertise data");
            return INVALID_REG_ID;
        }
        let scan_bytes =
            if let Some(d) = scan_response { d.make_with(&device_name) } else { Vec::<u8>::new() };
        if !AdvertiseData::validate_raw_data(is_legacy, &scan_bytes) {
            log::warn!("Failed to start advertising set with invalid scan response");
            return INVALID_REG_ID;
        }
        let periodic_params = if let Some(p) = periodic_parameters {
            p.into()
        } else {
@@ -2023,10 +2032,14 @@ impl IBluetoothGatt for BluetoothGatt {
        };
        let periodic_bytes =
            if let Some(d) = periodic_data { d.make_with(&device_name) } else { Vec::<u8>::new() };
        if !AdvertiseData::validate_raw_data(false, &periodic_bytes) {
            log::warn!("Failed to start advertising set with invalid periodic data");
            return INVALID_REG_ID;
        }
        let adv_timeout = clamp(duration, 0, 0xffff) as u16;
        let adv_events = clamp(max_ext_adv_events, 0, 0xff) as u8;

        let s = AdvertisingSetInfo::new(callback_id, adv_timeout, adv_events);
        let s = AdvertisingSetInfo::new(callback_id, adv_timeout, adv_events, is_legacy);
        let reg_id = s.reg_id();
        self.advertisers.add(s);

@@ -2105,6 +2118,10 @@ impl IBluetoothGatt for BluetoothGatt {
        let bytes = data.make_with(&device_name);

        if let Some(s) = self.advertisers.get_by_advertiser_id(advertiser_id) {
            if !AdvertiseData::validate_raw_data(s.is_legacy(), &bytes) {
                log::warn!("Advertiser {}: invalid advertise data to update", advertiser_id);
                return;
            }
            self.gatt.as_ref().unwrap().lock().unwrap().advertiser.set_data(
                s.adv_id(),
                false,
@@ -2119,6 +2136,10 @@ impl IBluetoothGatt for BluetoothGatt {
        }

        if let Some(s) = self.advertisers.get_by_advertiser_id(advertiser_id) {
            if !AdvertiseData::validate_raw_data(s.is_legacy(), &data) {
                log::warn!("Advertiser {}: invalid raw advertise data to update", advertiser_id);
                return;
            }
            self.gatt.as_ref().unwrap().lock().unwrap().advertiser.set_data(
                s.adv_id(),
                false,
@@ -2136,6 +2157,10 @@ impl IBluetoothGatt for BluetoothGatt {
        let bytes = data.make_with(&device_name);

        if let Some(s) = self.advertisers.get_by_advertiser_id(advertiser_id) {
            if !AdvertiseData::validate_raw_data(s.is_legacy(), &bytes) {
                log::warn!("Advertiser {}: invalid scan response to update", advertiser_id);
                return;
            }
            self.gatt.as_ref().unwrap().lock().unwrap().advertiser.set_data(
                s.adv_id(),
                true,
@@ -2214,6 +2239,10 @@ impl IBluetoothGatt for BluetoothGatt {
        let bytes = data.make_with(&device_name);

        if let Some(s) = self.advertisers.get_by_advertiser_id(advertiser_id) {
            if !AdvertiseData::validate_raw_data(false, &bytes) {
                log::warn!("Advertiser {}: invalid periodic data to update", advertiser_id);
                return;
            }
            self.gatt
                .as_ref()
                .unwrap()