From 3190c6ecb721185247ba46412a4facf42ed549ad Mon Sep 17 00:00:00 2001 From: Ajay Panicker <apanicke@google.com> Date: Fri, 14 Sep 2018 15:10:13 -0700 Subject: [PATCH] Add DataElementReader for SDP DataElementReader extracts DataElement's from packets which are the basic container for all data in the SDP protocol. Bug: 114751344 Test: run host test bluetooth_test_sdp Change-Id: If01f22d81af1b80e7c9ac8993ee161eba779bfa3 --- system/profile/sdp/Android.bp | 34 ++ .../profile/sdp/common/data_element_reader.cc | 238 ++++++++++ .../profile/sdp/common/data_element_reader.h | 65 +++ .../common/test/data_element_reader_test.cc | 412 ++++++++++++++++++ system/profile/sdp/sdp_common.h | 95 ++++ system/profile/sdp/sdp_logging_helper.h | 123 ++++++ 6 files changed, 967 insertions(+) create mode 100644 system/profile/sdp/Android.bp create mode 100644 system/profile/sdp/common/data_element_reader.cc create mode 100644 system/profile/sdp/common/data_element_reader.h create mode 100644 system/profile/sdp/common/test/data_element_reader_test.cc create mode 100644 system/profile/sdp/sdp_common.h create mode 100644 system/profile/sdp/sdp_logging_helper.h diff --git a/system/profile/sdp/Android.bp b/system/profile/sdp/Android.bp new file mode 100644 index 00000000000..9c6f5b5b963 --- /dev/null +++ b/system/profile/sdp/Android.bp @@ -0,0 +1,34 @@ +cc_library_static { + name: "sdp_service", + defaults: ["fluoride_defaults"], + host_supported: true, + include_dirs: [ + "packages/modules/Bluetooth/system/", + ], + srcs: [ + "common/data_element_reader.cc", + ], + static_libs: [ + "lib-bt-packets", + "libbluetooth-types", + ], +} + +cc_test { + name: "bluetooth_test_sdp", + test_suites: ["general-tests"], + defaults: ["fluoride_defaults"], + host_supported: true, + include_dirs: [ + "packages/modules/Bluetooth/system/", + ], + srcs: [ + "common/test/data_element_reader_test.cc", + ], + static_libs: [ + "libgmock", + "sdp_service", + "lib-bt-packets", + "libbluetooth-types", + ], +} diff --git a/system/profile/sdp/common/data_element_reader.cc b/system/profile/sdp/common/data_element_reader.cc new file mode 100644 index 00000000000..675100f5980 --- /dev/null +++ b/system/profile/sdp/common/data_element_reader.cc @@ -0,0 +1,238 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "data_element_reader.h" + +#include <base/logging.h> +#include <type_traits> + +#include "sdp_logging_helper.h" + +// A helper macro that can verify that there is enough data remaining in the +// reader to extract without overflowing. end_ - it_ should never be negative +// so casting it to a size_t is always safe. If it does fail, set it_ to end_ +// so that all additional readings fail. +#define CHECK_REMAINING_LEN(x) \ + do { \ + if ((size_t)(end_ - it_) < x) { \ + LOG(WARNING) << __func__ << ": Extract would read past end of data."; \ + return ParseFail(); \ + } \ + } while (0) + +namespace bluetooth { +namespace sdp { + +DataElementReader::DataElement DataElementReader::ReadNext() { + if (it_ > end_) LOG(FATAL) << "Beginning of buffer is past end of buffer."; + if (it_ == end_) return std::monostate(); + + uint8_t descriptor = *it_++; + DataElementType type = static_cast<DataElementType>(descriptor >> 3); + DataElementSize size = static_cast<DataElementSize>(descriptor & 0b00000111); + + // All types with a value greater than URL are currently reserved. + if (type > DataElementType::MAX_VALUE) { + LOG(WARNING) << __func__ << ": Trying to use a reserved data element type"; + return ParseFail(); + } + + switch (type) { + case DataElementType::BOOLEAN: + if (size != DataElementSize::BYTE1) { + LOG(WARNING) << __func__ << ": Invalid size for bool: " << size; + return ParseFail(); + } + + CHECK_REMAINING_LEN(1); + return (it_.extract<uint8_t>() != 0); + case DataElementType::SIGNED_INT: + return ReadSignedInt(size); + case DataElementType::UNSIGNED_INT: + return ReadUnsignedInt(size); + case DataElementType::UUID: + return ReadUuid(size); + case DataElementType::STRING: + return ReadString(size); + case DataElementType::DATA_ELEMENT_SEQUENCE: + return ReadSequence(size); + default: + // TODO: The other data element types are never used in the previous SDP + // implementation. We should properly handle them in the future though + // for completeness. + LOG(ERROR) << __func__ << ": Unhandled Data Element Type: " << type; + } + + return ParseFail(); +} + +DataElementReader::DataElement DataElementReader::ParseFail() { + it_ = end_; + return std::monostate(); +} + +template <class IntegerType> +DataElementReader::DataElement DataElementReader::ReadInteger() { + static_assert(std::is_integral<IntegerType>::value, + "ReadInteger requires an integral type."); + + CHECK_REMAINING_LEN(sizeof(IntegerType)); + return it_.extractBE<IntegerType>(); +} + +DataElementReader::DataElement DataElementReader::ReadLargeInt() { + CHECK_REMAINING_LEN(16); + + std::array<uint8_t, 16> array; + for (size_t i = 0; i < sizeof(uint8_t[16]); i++) { + array[i] = it_.extract<uint8_t>(); + } + + return array; +} + +DataElementReader::DataElement DataElementReader::ReadSignedInt( + DataElementSize size) { + switch (size) { + case DataElementSize::BYTE1: + return ReadInteger<int8_t>(); + case DataElementSize::BYTE2: + return ReadInteger<int16_t>(); + case DataElementSize::BYTE4: + return ReadInteger<int32_t>(); + case DataElementSize::BYTE8: + return ReadInteger<int64_t>(); + case DataElementSize::BYTE16: + return ReadLargeInt(); + default: + LOG(WARNING) << __func__ << ": Invalid size for int: " << size; + } + + return ParseFail(); +} + +DataElementReader::DataElement DataElementReader::ReadUnsignedInt( + DataElementSize size) { + switch (size) { + case DataElementSize::BYTE1: + return ReadInteger<uint8_t>(); + case DataElementSize::BYTE2: + return ReadInteger<uint16_t>(); + case DataElementSize::BYTE4: + return ReadInteger<uint32_t>(); + case DataElementSize::BYTE8: + return ReadInteger<uint64_t>(); + case DataElementSize::BYTE16: + return ReadLargeInt(); + default: + LOG(WARNING) << __func__ << ": Invalid size for uint: " << size; + } + + return ParseFail(); +} + +DataElementReader::DataElement DataElementReader::ReadUuid( + DataElementSize size) { + if (size == DataElementSize::BYTE2) { + CHECK_REMAINING_LEN(2); + return Uuid::From16Bit(it_.extractBE<uint16_t>()); + } + + if (size == DataElementSize::BYTE4) { + CHECK_REMAINING_LEN(4); + return Uuid::From32Bit(it_.extractBE<uint32_t>()); + } + + if (size == DataElementSize::BYTE16) { + CHECK_REMAINING_LEN(16); + + Uuid::UUID128Bit uuid_array; + for (int i = 0; i < 16; i++) { + uuid_array[i] = it_.extract<uint8_t>(); + } + + return Uuid::From128BitBE(uuid_array); + } + + LOG(WARNING) << __func__ << ": Invalid size for UUID: " << size; + return ParseFail(); +} + +DataElementReader::DataElement DataElementReader::ReadString( + DataElementSize size) { + uint32_t num_bytes = 0; + + switch (size) { + case DataElementSize::ADDITIONAL_8BIT: + CHECK_REMAINING_LEN(1); + num_bytes = it_.extractBE<uint8_t>(); + break; + case DataElementSize::ADDITIONAL_16BIT: + CHECK_REMAINING_LEN(2); + num_bytes = it_.extractBE<uint16_t>(); + break; + case DataElementSize::ADDITIONAL_32BIT: + CHECK_REMAINING_LEN(4); + num_bytes = it_.extractBE<uint32_t>(); + break; + default: + LOG(WARNING) << __func__ << ": Invalid size for string: " << size; + return ParseFail(); + } + + CHECK_REMAINING_LEN(num_bytes); + + std::string str; + for (uint32_t i = 0; i < num_bytes; i++) { + str.push_back(it_.extractBE<uint8_t>()); + } + + return str; +} + +DataElementReader::DataElement DataElementReader::ReadSequence( + DataElementSize size) { + uint32_t num_bytes = 0; + + switch (size) { + case DataElementSize::ADDITIONAL_8BIT: + CHECK_REMAINING_LEN(1); + num_bytes = it_.extractBE<uint8_t>(); + break; + case DataElementSize::ADDITIONAL_16BIT: + CHECK_REMAINING_LEN(2); + num_bytes = it_.extractBE<uint16_t>(); + break; + case DataElementSize::ADDITIONAL_32BIT: + CHECK_REMAINING_LEN(4); + num_bytes = it_.extractBE<uint32_t>(); + break; + default: + LOG(WARNING) << __func__ << ": Invalid size for string: " << size; + return ParseFail(); + } + + CHECK_REMAINING_LEN(num_bytes); + + // Create a parser that points to the beginning of the next sequence and move + // the iterator to past the end of the new sequence. + auto&& temp = DataElementReader(it_, it_ + num_bytes); + it_ += num_bytes; + return std::move(temp); +} + +} // namespace sdp +} // namespace bluetooth diff --git a/system/profile/sdp/common/data_element_reader.h b/system/profile/sdp/common/data_element_reader.h new file mode 100644 index 00000000000..13346bd8838 --- /dev/null +++ b/system/profile/sdp/common/data_element_reader.h @@ -0,0 +1,65 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <array> +#include <variant> + +#include "bluetooth/uuid.h" +#include "packet.h" +#include "sdp_common.h" +#include "stack/include/bt_types.h" + +namespace bluetooth { +namespace sdp { + +// A helper class that helps extract data element objects from SDP packets. +class DataElementReader { + public: + // If the DataElement contains monostate, that means parsing has failed. + using DataElement = + std::variant<std::monostate, bool, int8_t, int16_t, int32_t, int64_t, + uint8_t, uint16_t, uint32_t, uint64_t, Octet16, Uuid, + std::string, DataElementReader>; + + DataElementReader(Iterator begin, Iterator end) : it_(begin), end_(end){}; + + // Get the next Data Element in the data. If reading fails for any reason, + // the DataElementReader becomes invalid and will continuously fail to read + // from that point onward. + DataElement ReadNext(); + + private: + // Extraction Helpers + DataElement ParseFail(); + template <class IntegerType> + DataElement ReadInteger(); + DataElement ReadLargeInt(); + + // Extraction Functions + DataElement ReadSignedInt(DataElementSize size); + DataElement ReadUnsignedInt(DataElementSize size); + DataElement ReadUuid(DataElementSize size); + DataElement ReadString(DataElementSize size); + DataElement ReadSequence(DataElementSize size); + + Iterator it_; + Iterator end_; +}; + +} // namespace sdp +} // namespace bluetooth diff --git a/system/profile/sdp/common/test/data_element_reader_test.cc b/system/profile/sdp/common/test/data_element_reader_test.cc new file mode 100644 index 00000000000..9361ac79a84 --- /dev/null +++ b/system/profile/sdp/common/test/data_element_reader_test.cc @@ -0,0 +1,412 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <base/logging.h> +#include <gtest/gtest.h> + +#include "common/data_element_reader.h" + +namespace bluetooth { +namespace sdp { + +using namespace testing; +using DataElement = DataElementReader::DataElement; + +// A helper class to help work with the Data Element classes. +class ReaderPacket : public ::bluetooth::Packet { + public: + using Packet::Packet; + + static std::shared_ptr<ReaderPacket> Make(std::vector<uint8_t> payload) { + auto pkt = std::shared_ptr<ReaderPacket>(new ReaderPacket()); + pkt->packet_start_index_ = 0; + pkt->packet_end_index_ = payload.size(); + pkt->data_ = std::make_shared<std::vector<uint8_t>>(std::move(payload)); + return pkt; + } + + std::string ToString() const override { return ""; } + bool IsValid() const override { return true; } + std::pair<size_t, size_t> GetPayloadIndecies() const override { + return std::pair<size_t, size_t>(packet_start_index_, packet_end_index_); + } +}; + +bool operator!=(DataElementReader a, DataElementReader b); + +// A helper function to help compare DataElementReader objects. +bool operator==(DataElementReader a, DataElementReader b) { + while (true) { + DataElement a_elem = a.ReadNext(); + DataElement b_elem = b.ReadNext(); + + if (a_elem != b_elem) return false; + + // If we get here that means both a and b have reached the end. + if (a_elem == DataElement(std::monostate())) break; + } + + return true; +} + +bool operator!=(DataElementReader a, DataElementReader b) { return !(a == b); } + +// A helper function to convert a type and a size to a descriptor byte. +constexpr uint8_t Desc(DataElementType t, DataElementSize s) { + return static_cast<uint8_t>(t) << 3 | static_cast<uint8_t>(s); +} + +// Helper that can create a Data Element reader from a vector. +DataElementReader CreateReader(std::vector<uint8_t> payload) { + auto packet = ReaderPacket::Make(std::move(payload)); + return DataElementReader(packet->begin(), packet->end()); +} + +// Test all the valid cases of reading the next Data Element. +using ValidTestParam = std::tuple<std::vector<uint8_t>, DataElement>; +class ValidReadTest : public TestWithParam<ValidTestParam> {}; + +std::vector<ValidTestParam> valid_values = { + // Boolean Tests + ValidTestParam{ + {Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01}, + true, + }, + ValidTestParam{ + {Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00}, + false, + }, + + // Signed Integer Tests + ValidTestParam{ + {Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE1), 0xFF}, + static_cast<int8_t>(-1)}, + ValidTestParam{ + {Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE2), 0xFF, 0xFF}, + static_cast<int16_t>(-1)}, + ValidTestParam{{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE4), + 0xFF, 0xFF, 0xFF, 0xFF}, + static_cast<int32_t>(-1)}, + ValidTestParam{{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE8), + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + static_cast<int64_t>(-1)}, + ValidTestParam{{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE16), + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + std::array<uint8_t, 16>{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF}}, + + // Unsigned Integer Tests + ValidTestParam{ + {Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE1), 0x01}, + static_cast<uint8_t>(1)}, + ValidTestParam{{Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE2), + 0x00, 0x01}, + static_cast<uint16_t>(1)}, + ValidTestParam{{Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE4), + 0x00, 0x00, 0x00, 0x01}, + static_cast<uint32_t>(1)}, + ValidTestParam{{Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE8), + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + static_cast<uint64_t>(1)}, + ValidTestParam{ + {Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE16), 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01}, + std::array<uint8_t, 16>{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01}}, + + // UUID Tests + ValidTestParam{ + {Desc(DataElementType::UUID, DataElementSize::BYTE2), 0x01, 0x02}, + Uuid::From16Bit(0x0102)}, + ValidTestParam{{Desc(DataElementType::UUID, DataElementSize::BYTE4), 0x01, + 0x02, 0x03, 0x04}, + Uuid::From32Bit(0x01020304)}, + ValidTestParam{ + {Desc(DataElementType::UUID, DataElementSize::BYTE16), 0x00, 0x01, 0x02, + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F}, + Uuid::From128BitBE({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F})}, + + // String Tests + ValidTestParam{ + {Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_8BIT), 0x05, + 'T', 'e', 's', 't', '1'}, + std::string("Test1")}, + ValidTestParam{ + {Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_16BIT), 0x00, + 0x05, 'T', 'e', 's', 't', '2'}, + std::string("Test2")}, + ValidTestParam{ + {Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_32BIT), 0x00, + 0x00, 0x00, 0x05, 'T', 'e', 's', 't', '3'}, + std::string("Test3")}, + + // Nested Data Element List Tests + ValidTestParam{ + {Desc(DataElementType::DATA_ELEMENT_SEQUENCE, + DataElementSize::ADDITIONAL_8BIT), + 0x04, Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01, + Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00}, + CreateReader( + {Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01, + Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00})}, + ValidTestParam{ + {Desc(DataElementType::DATA_ELEMENT_SEQUENCE, + DataElementSize::ADDITIONAL_16BIT), + 0x00, 0x04, Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), + 0x01, Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00}, + CreateReader( + {Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01, + Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00})}, + ValidTestParam{ + {Desc(DataElementType::DATA_ELEMENT_SEQUENCE, + DataElementSize::ADDITIONAL_32BIT), + 0x00, 0x00, 0x00, 0x04, + Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01, + Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00}, + CreateReader( + {Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01, + Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00})}, +}; + +INSTANTIATE_TEST_CASE_P(ReadNext, ValidReadTest, ValuesIn(valid_values)); +TEST_P(ValidReadTest, Test) { + auto packet = ReaderPacket::Make(std::get<0>(GetParam())); + auto value = std::get<1>(GetParam()); + + DataElementReader reader(packet->begin(), packet->end()); + auto read_value = reader.ReadNext(); + + ASSERT_EQ(value, read_value); + + // Test that there is no additional data to read. + ASSERT_EQ(reader.ReadNext(), DataElement(std::monostate())); +} + +// Test that a nested reader is correctly bounded and can't read past its +// defined end. +TEST(ReadNext, BoundedSubreaderTest) { + std::vector<uint8_t> payload = { + // Subsequence descriptor byte. + Desc(DataElementType::DATA_ELEMENT_SEQUENCE, + DataElementSize::ADDITIONAL_8BIT), + // Subsequence length. + 0x04, + // Subsequence that contains two booleans with values true and false. + Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01, + Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00, + // Additional int16 at the end of the original sequence. + Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE2), 0x01, 0x23}; + + auto packet = ReaderPacket::Make(payload); + DataElementReader reader(packet->begin(), packet->end()); + + // The first thing read should be the subsequence. + auto data_element = reader.ReadNext(); + ASSERT_TRUE(std::holds_alternative<DataElementReader>(data_element)); + + // Check that the subsequence matches the premade sequence. + auto subreader = std::get<DataElementReader>(data_element); + data_element = subreader.ReadNext(); + ASSERT_TRUE(std::holds_alternative<bool>(data_element)); + ASSERT_TRUE(std::get<bool>(data_element)); + data_element = subreader.ReadNext(); + ASSERT_TRUE(std::holds_alternative<bool>(data_element)); + ASSERT_FALSE(std::get<bool>(data_element)); + + // Check that there is no additional data to be read from the subreader. + ASSERT_EQ(subreader.ReadNext(), DataElement(std::monostate())); + + // Check that we can still read the int16 from the original reader. + data_element = reader.ReadNext(); + ASSERT_TRUE(std::holds_alternative<int16_t>(data_element)); + auto int16_value = std::get<int16_t>(data_element); + ASSERT_EQ(int16_value, 0x0123); + + // Check that there is no additional data to be read from the base reader. + ASSERT_EQ(reader.ReadNext(), DataElement(std::monostate())); +} + +// Test that trying to read an empty packet fails. +TEST(ReadNext, NoDataTest) { + auto packet = ReaderPacket::Make({}); + DataElementReader reader(packet->begin(), packet->end()); + + ASSERT_EQ(reader.ReadNext(), DataElement(std::monostate())); +} + +// Test that using a reserved value for type fails. +TEST(ReadNext, InvalidTypeTest) { + auto packet = ReaderPacket::Make({0xFF}); + DataElementReader reader(packet->begin(), packet->end()); + + ASSERT_EQ(reader.ReadNext(), DataElement(std::monostate())); +} + +// Test all invalid parses due to incorrect lengths or invalid sizes. All tests +// should return std::monostate. +using InvalidTestParam = std::vector<uint8_t>; +class InvalidReadTest : public TestWithParam<InvalidTestParam> {}; + +std::vector<InvalidTestParam> invalid_values = { + // Boolean Tests: + // Invalid size field. + InvalidTestParam{ + Desc(DataElementType::BOOLEAN, DataElementSize::BYTE2), + }, + // Insufficient data. + InvalidTestParam{Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1)}, + + // Signed Integer Tests: + // Invalid size field. + InvalidTestParam{ + Desc(DataElementType::SIGNED_INT, DataElementSize::ADDITIONAL_8BIT)}, + // 1 byte insufficient data. + InvalidTestParam{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE1)}, + // 2 byte insufficient data. + InvalidTestParam{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE2), + 0x00}, + // 4 byte insufficient data. + InvalidTestParam{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE4), + 0x00, 0x00, 0x00}, + // 8 Byte insufficient data. + InvalidTestParam{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE8), + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + // 16 Byte insufficient data. + InvalidTestParam{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE16), + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}, + + // Unsigned Integer Tests: + // Invalid size field. + InvalidTestParam{ + Desc(DataElementType::UNSIGNED_INT, DataElementSize::ADDITIONAL_8BIT)}, + // 1 byte insufficient data. + InvalidTestParam{ + Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE1)}, + // 2 byte insufficient data. + InvalidTestParam{ + Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE2), 0x00}, + // 4 byte insufficient data. + InvalidTestParam{ + Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE4), 0x00, 0x00, + 0x00}, + // 8 Byte insufficient data. + InvalidTestParam{ + Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE8), 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00}, + // 16 Byte insufficient data. + InvalidTestParam{ + Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE16), 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + + // UUID Tests: + // Invalid size field. + InvalidTestParam{ + Desc(DataElementType::UUID, DataElementSize::ADDITIONAL_8BIT)}, + // 2 byte insufficient data. + InvalidTestParam{Desc(DataElementType::UUID, DataElementSize::BYTE2), 0x00}, + // 4 byte insufficient data. + InvalidTestParam{Desc(DataElementType::UUID, DataElementSize::BYTE4), 0x00, + 0x00, 0x00}, + // 16 Byte insufficient data. + InvalidTestParam{Desc(DataElementType::UUID, DataElementSize::BYTE16), 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00}, + + // String Tests: + // Invalid size field. + InvalidTestParam{Desc(DataElementType::STRING, DataElementSize::BYTE1)}, + // Insufficient data for additional 8 bits len. + InvalidTestParam{ + Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_8BIT)}, + // Insufficient data for additional 16 bits len. + InvalidTestParam{ + Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_16BIT), + 0x00, + }, + // Insufficient data for additional 32 bit len. + InvalidTestParam{ + Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_32BIT), + 0x00, + 0x00, + 0x00, + }, + // Insufficient data for reported length. + InvalidTestParam{ + Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_8BIT), 0x04, + '1', '2', '3'}, + + // Nested Data Element List Tests: + // Invalid size field. + InvalidTestParam{ + Desc(DataElementType::DATA_ELEMENT_SEQUENCE, DataElementSize::BYTE1)}, + // Insufficient data for additional 8 bits len. + InvalidTestParam{Desc(DataElementType::DATA_ELEMENT_SEQUENCE, + DataElementSize::ADDITIONAL_8BIT)}, + // Insufficient data for additional 16 bits len. + InvalidTestParam{ + Desc(DataElementType::DATA_ELEMENT_SEQUENCE, + DataElementSize::ADDITIONAL_16BIT), + 0x00, + }, + // Insufficient data for additional 32 bit len. + InvalidTestParam{ + Desc(DataElementType::DATA_ELEMENT_SEQUENCE, + DataElementSize::ADDITIONAL_32BIT), + 0x00, + 0x00, + 0x00, + }, + // Insufficient data for reported length. + InvalidTestParam{Desc(DataElementType::DATA_ELEMENT_SEQUENCE, + DataElementSize::ADDITIONAL_8BIT), + 0x04, 0x00, 0x00, 0x00}, + + // Unhandled Data Element Types Tests: + // NOTE: These tests should go away as we begin to handle the types. + // Nil Type. + InvalidTestParam{Desc(DataElementType::NIL, DataElementSize::BYTE1)}, + // Data Element Alternative List Type. + InvalidTestParam{Desc(DataElementType::DATA_ELEMENT_ALTERNATIVE, + DataElementSize::ADDITIONAL_8BIT), + 0x00}, + // URL Type. + InvalidTestParam{ + Desc(DataElementType::URL, DataElementSize::ADDITIONAL_8BIT), 0x00}}; + +INSTANTIATE_TEST_CASE_P(ReadNext, InvalidReadTest, ValuesIn(invalid_values)); +TEST_P(InvalidReadTest, Test) { + auto packet = ReaderPacket::Make(GetParam()); + DataElementReader reader(packet->begin(), packet->end()); + + ASSERT_EQ(reader.ReadNext(), DataElement(std::monostate())); +} + +// Test that trying to read from a reader with start > end crashes. +TEST(DataElementReader, BadBoundsDeathTest) { + auto packet = ReaderPacket::Make({0x00, 0x00, 0x00, 0x00}); + DataElementReader reader(packet->end(), packet->begin()); + ASSERT_DEATH(reader.ReadNext(), "Beginning of buffer is past end of buffer."); +} + +} // namespace sdp +} // namespace bluetooth diff --git a/system/profile/sdp/sdp_common.h b/system/profile/sdp/sdp_common.h new file mode 100644 index 00000000000..4a07a6306d4 --- /dev/null +++ b/system/profile/sdp/sdp_common.h @@ -0,0 +1,95 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace bluetooth { +namespace sdp { + +enum class PduId : uint8_t { + RESERVED = 0x00, + ERROR = 0x01, + SERVICE_SEARCH_REQUEST = 0x02, + SERVICE_SEARCH_RESPONSE = 0x03, + SERVICE_ATTRIBUTE_REQUEST = 0x04, + SERVICE_ATTRIBUTE_RESPONSE = 0x05, + SERVICE_SEARCH_ATTRIBUTE_REQUEST = 0x06, + SERVICE_SEARCH_ATTRIBUTE_RESPONSE = 0x07, + MAX_VALUE = 0x07, +}; + +enum class AttributeId : uint16_t { + SERVICE_RECORD_HANDLE = 0x0000, + SERVICE_CLASS_ID_LIST = 0x0001, + SERVICE_RECORD_STATE = 0x0002, + SERVICE_ID = 0x0003, + PROTOCOL_DESCRIPTOR_LIST = 0x0004, + BROWSE_GROUP_LIST = 0x0005, + LANGUAGE_BASE_ATTRIBUTE_ID_LIST = 0x0006, + SERVICE_INFO_TIME_TO_LIVE = 0x0007, + SERVICE_AVAILABILITY = 0x0008, + PROFILE_DESCRIPTOR_LIST = 0x0009, + DOCUMENTATION_URL = 0x000A, + CLIENT_EXECUTABLE_URL = 0x000B, + ICON_URL = 0x000C, + ADDITIONAL_PROTOCOL_DESCRIPTOR_LIST = 0x000D, + + // The following attributes are only used in the SDP server service record. + // They are only valid if ServiceDiscoveryServerServiceClassID is in the + // ServiceClassIDList. See Bluetooth Core v5.0 Section 5.2. + VERSION_NUMBER_LIST = 0x0200, + SERVICE_DATABASE_STATE = 0x0201, +}; + +// The Attribute ID's of these attributes are calculated by adding the offset +// value for the attribute to the attribute ID base (contained in the +// LanguageBaseAttributeIDList attribute value). +enum AttributeIdOffset : uint16_t { + SERVICE_NAME = 0x0000, + SERVICE_DESCRIPTION = 0x0001, + PROVIDER_NAME = 0x0002, +}; + +// Constant that define the different types of data element. +enum class DataElementType : uint8_t { + NIL = 0x00, + UNSIGNED_INT = 0x01, + SIGNED_INT = 0x02, + UUID = 0x03, + STRING = 0x04, + BOOLEAN = 0x05, + DATA_ELEMENT_SEQUENCE = 0x06, + DATA_ELEMENT_ALTERNATIVE = 0x07, + URL = 0x08, + MAX_VALUE = 0x08, +}; + +// Constant that define the different sizes of data element. +enum class DataElementSize : uint8_t { + BYTE1 = 0x0, // Exception: If the data element is NIL then size is 0 bytes + BYTE2 = 0x1, + BYTE4 = 0x2, + BYTE8 = 0x3, + BYTE16 = 0x4, + // The size types below represent that the first X bits of the value + // represents the size of the remaining data. + ADDITIONAL_8BIT = 0x5, + ADDITIONAL_16BIT = 0x6, + ADDITIONAL_32BIT = 0x7, +}; + +} // namespace sdp +} // namespace bluetooth diff --git a/system/profile/sdp/sdp_logging_helper.h b/system/profile/sdp/sdp_logging_helper.h new file mode 100644 index 00000000000..e9473298685 --- /dev/null +++ b/system/profile/sdp/sdp_logging_helper.h @@ -0,0 +1,123 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <iomanip> +#include <iostream> +#include <sstream> +#include <string> +#include <type_traits> + +#include "bt_trace.h" +#include "sdp_common.h" + +namespace bluetooth { +namespace sdp { + +#ifndef CASE_RETURN_TEXT +#define CASE_RETURN_TEXT(code) \ + case code: \ + return #code +#endif + +inline std::string PduIdText(const PduId& id) { + switch (id) { + CASE_RETURN_TEXT(PduId::RESERVED); + CASE_RETURN_TEXT(PduId::ERROR); + CASE_RETURN_TEXT(PduId::SERVICE_SEARCH_REQUEST); + CASE_RETURN_TEXT(PduId::SERVICE_SEARCH_RESPONSE); + CASE_RETURN_TEXT(PduId::SERVICE_ATTRIBUTE_REQUEST); + CASE_RETURN_TEXT(PduId::SERVICE_ATTRIBUTE_RESPONSE); + CASE_RETURN_TEXT(PduId::SERVICE_SEARCH_ATTRIBUTE_REQUEST); + CASE_RETURN_TEXT(PduId::SERVICE_SEARCH_ATTRIBUTE_RESPONSE); + default: + return "Unknown PduId: " + loghex((uint8_t)id); + } +} + +inline std::ostream& operator<<(std::ostream& os, const PduId& id) { + return os << PduIdText(id); +} + +inline std::string AttributeIdText(const AttributeId& id) { + switch (id) { + CASE_RETURN_TEXT(AttributeId::SERVICE_RECORD_HANDLE); + CASE_RETURN_TEXT(AttributeId::SERVICE_CLASS_ID_LIST); + CASE_RETURN_TEXT(AttributeId::SERVICE_RECORD_STATE); + CASE_RETURN_TEXT(AttributeId::SERVICE_ID); + CASE_RETURN_TEXT(AttributeId::PROTOCOL_DESCRIPTOR_LIST); + CASE_RETURN_TEXT(AttributeId::BROWSE_GROUP_LIST); + CASE_RETURN_TEXT(AttributeId::LANGUAGE_BASE_ATTRIBUTE_ID_LIST); + CASE_RETURN_TEXT(AttributeId::SERVICE_INFO_TIME_TO_LIVE); + CASE_RETURN_TEXT(AttributeId::SERVICE_AVAILABILITY); + CASE_RETURN_TEXT(AttributeId::PROFILE_DESCRIPTOR_LIST); + CASE_RETURN_TEXT(AttributeId::DOCUMENTATION_URL); + CASE_RETURN_TEXT(AttributeId::CLIENT_EXECUTABLE_URL); + CASE_RETURN_TEXT(AttributeId::ICON_URL); + CASE_RETURN_TEXT(AttributeId::ADDITIONAL_PROTOCOL_DESCRIPTOR_LIST); + CASE_RETURN_TEXT(AttributeId::VERSION_NUMBER_LIST); + CASE_RETURN_TEXT(AttributeId::SERVICE_DATABASE_STATE); + default: + return "Unknown AttributeId: " + loghex((uint16_t)id); + } +} + +inline std::ostream& operator<<(std::ostream& os, const AttributeId& id) { + return os << AttributeIdText(id); +} + +inline std::string DataElementTypeText(const DataElementType& type) { + switch (type) { + CASE_RETURN_TEXT(DataElementType::NIL); + CASE_RETURN_TEXT(DataElementType::UNSIGNED_INT); + CASE_RETURN_TEXT(DataElementType::SIGNED_INT); + CASE_RETURN_TEXT(DataElementType::UUID); + CASE_RETURN_TEXT(DataElementType::STRING); + CASE_RETURN_TEXT(DataElementType::BOOLEAN); + CASE_RETURN_TEXT(DataElementType::DATA_ELEMENT_SEQUENCE); + CASE_RETURN_TEXT(DataElementType::DATA_ELEMENT_ALTERNATIVE); + CASE_RETURN_TEXT(DataElementType::URL); + default: + return "Unknown DataElementType: " + loghex((uint8_t)type); + } +} + +inline std::ostream& operator<<(std::ostream& os, const DataElementType& type) { + return os << DataElementTypeText(type); +} + +inline std::string DataElementSizeText(const DataElementSize& size) { + switch (size) { + CASE_RETURN_TEXT(DataElementSize::BYTE1); + CASE_RETURN_TEXT(DataElementSize::BYTE2); + CASE_RETURN_TEXT(DataElementSize::BYTE4); + CASE_RETURN_TEXT(DataElementSize::BYTE8); + CASE_RETURN_TEXT(DataElementSize::BYTE16); + CASE_RETURN_TEXT(DataElementSize::ADDITIONAL_8BIT); + CASE_RETURN_TEXT(DataElementSize::ADDITIONAL_16BIT); + CASE_RETURN_TEXT(DataElementSize::ADDITIONAL_32BIT); + default: + return "Unknown DataElementSize: " + loghex((uint8_t)size); + } +} + +inline std::ostream& operator<<(std::ostream& os, const DataElementSize& size) { + return os << DataElementSizeText(size); +} + +} // namespace sdp +} // namespace bluetooth -- GitLab