Loading libs/binder/include/binder/Parcel.h +92 −7 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #pragma once #include <array> #include <map> // for legacy reasons #include <string> #include <type_traits> Loading Loading @@ -224,6 +225,15 @@ public: return writeData(val); } template <typename T, size_t N> status_t writeFixedArray(const std::array<T, N>& val) { return writeData(val); } template <typename T, size_t N> status_t writeFixedArray(const std::optional<std::array<T, N>>& val) { return writeData(val); } // Write an Enum vector with underlying type int8_t. // Does not use padding; each byte is contiguous. template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> Loading Loading @@ -487,6 +497,15 @@ public: std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const __attribute__((deprecated("use std::optional version instead"))); status_t readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const; template <typename T, size_t N> status_t readFixedArray(std::array<T, N>* val) const { return readData(val); } template <typename T, size_t N> status_t readFixedArray(std::optional<std::array<T, N>>* val) const { return readData(val); } template<typename T> status_t read(Flattenable<T>& val) const; Loading Loading @@ -818,6 +837,16 @@ private: || is_specialization_v<T, std::unique_ptr> || is_specialization_v<T, std::shared_ptr>; // Tells if T is a fixed-size array. template <typename T> struct is_fixed_array : std::false_type {}; template <typename T, size_t N> struct is_fixed_array<std::array<T, N>> : std::true_type {}; template <typename T> static inline constexpr bool is_fixed_array_v = is_fixed_array<T>::value; // special int32 value to indicate NonNull or Null parcelables // This is fixed to be only 0 or 1 by contract, do not change. static constexpr int32_t kNonNullParcelableFlag = 1; Loading Loading @@ -922,6 +951,8 @@ private: if (!c) return writeData(static_cast<int32_t>(kNullVectorSize)); } else if constexpr (std::is_base_of_v<Parcelable, T>) { if (!c) return writeData(static_cast<int32_t>(kNullParcelableFlag)); } else if constexpr (is_fixed_array_v<T>) { if (!c) return writeData(static_cast<int32_t>(kNullVectorSize)); } else /* constexpr */ { // could define this, but raise as error. static_assert(dependent_false_v<CT>); } Loading Loading @@ -961,6 +992,23 @@ private: return OK; } template <typename T, size_t N> status_t writeData(const std::array<T, N>& val) { static_assert(N <= std::numeric_limits<int32_t>::max()); status_t status = writeData(static_cast<int32_t>(N)); if (status != OK) return status; if constexpr (is_pointer_equivalent_array_v<T>) { static_assert(N <= std::numeric_limits<size_t>::max() / sizeof(T)); return write(val.data(), val.size() * sizeof(T)); } else /* constexpr */ { for (const auto& t : val) { status = writeData(t); if (status != OK) return status; } return OK; } } // readData function overloads. // Implementation detail: Function overloading improves code readability over // template overloading, but prevents readData<T> from being used for those types. Loading Loading @@ -1053,9 +1101,8 @@ private: int32_t peek; status_t status = readData(&peek); if (status != OK) return status; if constexpr (is_specialization_v<T, std::vector> || std::is_same_v<T, String16> || std::is_same_v<T, std::string>) { if constexpr (is_specialization_v<T, std::vector> || is_fixed_array_v<T> || std::is_same_v<T, String16> || std::is_same_v<T, std::string>) { if (peek == kNullVectorSize) { c->reset(); return OK; Loading @@ -1070,7 +1117,10 @@ private: } // create a new object. if constexpr (is_specialization_v<CT, std::optional>) { c->emplace(); // Call default constructor explicitly // - Clang bug: https://bugs.llvm.org/show_bug.cgi?id=35748 // std::optional::emplace() doesn't work with nested types. c->emplace(T()); } else /* constexpr */ { T* const t = new (std::nothrow) T; // contents read from Parcel below. if (t == nullptr) return NO_MEMORY; Loading @@ -1079,7 +1129,7 @@ private: // rewind data ptr to reread (this is pretty quick), otherwise we could // pass an optional argument to readData to indicate a peeked value. setDataPosition(startPos); if constexpr (is_specialization_v<T, std::vector>) { if constexpr (is_specialization_v<T, std::vector> || is_fixed_array_v<T>) { return readData(&**c, READ_FLAG_SP_NULLABLE); // nullable sp<> allowed now } else { return readData(&**c); Loading Loading @@ -1142,6 +1192,41 @@ private: return OK; } template <typename T, size_t N> status_t readData(std::array<T, N>* val, ReadFlags readFlags = READ_FLAG_NONE) const { static_assert(N <= std::numeric_limits<int32_t>::max()); int32_t size; status_t status = readInt32(&size); if (status != OK) return status; if (size < 0) return UNEXPECTED_NULL; if (size != static_cast<int32_t>(N)) return BAD_VALUE; if constexpr (is_pointer_equivalent_array_v<T>) { auto data = reinterpret_cast<const T*>(readInplace(N * sizeof(T))); if (data == nullptr) return BAD_VALUE; memcpy(val->data(), data, N * sizeof(T)); } else if constexpr (is_specialization_v<T, sp>) { for (auto& t : *val) { if (readFlags & READ_FLAG_SP_NULLABLE) { status = readNullableStrongBinder(&t); // allow nullable } else { status = readStrongBinder(&t); } if (status != OK) return status; } } else if constexpr (is_fixed_array_v<T>) { // pass readFlags down to nested arrays for (auto& t : *val) { status = readData(&t, readFlags); if (status != OK) return status; } } else /* constexpr */ { for (auto& t : *val) { status = readData(&t); if (status != OK) return status; } } return OK; } //----------------------------------------------------------------------------- private: Loading libs/binder/tests/parcel_fuzzer/binder.cpp +26 −0 Original line number Diff line number Diff line Loading @@ -233,6 +233,32 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<std::string>>>, readUtf8VectorFromUtf16Vector), PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector), #define COMMA , PARCEL_READ_WITH_STATUS(std::array<uint8_t COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<uint8_t COMMA 3>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<char16_t COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<char16_t COMMA 3>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<std::string COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<std::string> COMMA 3>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<android::String16 COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<android::String16> COMMA 3>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<android::sp<android::IBinder> COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<android::sp<android::IBinder> COMMA 3>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<ExampleParcelable COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<ExampleParcelable> COMMA 3>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<ByteEnum COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<ByteEnum COMMA 3>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<IntEnum COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<IntEnum COMMA 3>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<LongEnum COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<LongEnum COMMA 3>>, readFixedArray), // nested arrays PARCEL_READ_WITH_STATUS(std::array<std::array<uint8_t COMMA 3> COMMA 4>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<std::array<uint8_t COMMA 3> COMMA 4>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<ExampleParcelable COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<std::array<std::optional<ExampleParcelable> COMMA 3> COMMA 4>>, readFixedArray), #undef COMMA [] (const android::Parcel& p, uint8_t /*len*/) { FUZZ_LOG() << "about to read flattenable"; ExampleFlattenable f; Loading Loading
libs/binder/include/binder/Parcel.h +92 −7 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #pragma once #include <array> #include <map> // for legacy reasons #include <string> #include <type_traits> Loading Loading @@ -224,6 +225,15 @@ public: return writeData(val); } template <typename T, size_t N> status_t writeFixedArray(const std::array<T, N>& val) { return writeData(val); } template <typename T, size_t N> status_t writeFixedArray(const std::optional<std::array<T, N>>& val) { return writeData(val); } // Write an Enum vector with underlying type int8_t. // Does not use padding; each byte is contiguous. template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> Loading Loading @@ -487,6 +497,15 @@ public: std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const __attribute__((deprecated("use std::optional version instead"))); status_t readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const; template <typename T, size_t N> status_t readFixedArray(std::array<T, N>* val) const { return readData(val); } template <typename T, size_t N> status_t readFixedArray(std::optional<std::array<T, N>>* val) const { return readData(val); } template<typename T> status_t read(Flattenable<T>& val) const; Loading Loading @@ -818,6 +837,16 @@ private: || is_specialization_v<T, std::unique_ptr> || is_specialization_v<T, std::shared_ptr>; // Tells if T is a fixed-size array. template <typename T> struct is_fixed_array : std::false_type {}; template <typename T, size_t N> struct is_fixed_array<std::array<T, N>> : std::true_type {}; template <typename T> static inline constexpr bool is_fixed_array_v = is_fixed_array<T>::value; // special int32 value to indicate NonNull or Null parcelables // This is fixed to be only 0 or 1 by contract, do not change. static constexpr int32_t kNonNullParcelableFlag = 1; Loading Loading @@ -922,6 +951,8 @@ private: if (!c) return writeData(static_cast<int32_t>(kNullVectorSize)); } else if constexpr (std::is_base_of_v<Parcelable, T>) { if (!c) return writeData(static_cast<int32_t>(kNullParcelableFlag)); } else if constexpr (is_fixed_array_v<T>) { if (!c) return writeData(static_cast<int32_t>(kNullVectorSize)); } else /* constexpr */ { // could define this, but raise as error. static_assert(dependent_false_v<CT>); } Loading Loading @@ -961,6 +992,23 @@ private: return OK; } template <typename T, size_t N> status_t writeData(const std::array<T, N>& val) { static_assert(N <= std::numeric_limits<int32_t>::max()); status_t status = writeData(static_cast<int32_t>(N)); if (status != OK) return status; if constexpr (is_pointer_equivalent_array_v<T>) { static_assert(N <= std::numeric_limits<size_t>::max() / sizeof(T)); return write(val.data(), val.size() * sizeof(T)); } else /* constexpr */ { for (const auto& t : val) { status = writeData(t); if (status != OK) return status; } return OK; } } // readData function overloads. // Implementation detail: Function overloading improves code readability over // template overloading, but prevents readData<T> from being used for those types. Loading Loading @@ -1053,9 +1101,8 @@ private: int32_t peek; status_t status = readData(&peek); if (status != OK) return status; if constexpr (is_specialization_v<T, std::vector> || std::is_same_v<T, String16> || std::is_same_v<T, std::string>) { if constexpr (is_specialization_v<T, std::vector> || is_fixed_array_v<T> || std::is_same_v<T, String16> || std::is_same_v<T, std::string>) { if (peek == kNullVectorSize) { c->reset(); return OK; Loading @@ -1070,7 +1117,10 @@ private: } // create a new object. if constexpr (is_specialization_v<CT, std::optional>) { c->emplace(); // Call default constructor explicitly // - Clang bug: https://bugs.llvm.org/show_bug.cgi?id=35748 // std::optional::emplace() doesn't work with nested types. c->emplace(T()); } else /* constexpr */ { T* const t = new (std::nothrow) T; // contents read from Parcel below. if (t == nullptr) return NO_MEMORY; Loading @@ -1079,7 +1129,7 @@ private: // rewind data ptr to reread (this is pretty quick), otherwise we could // pass an optional argument to readData to indicate a peeked value. setDataPosition(startPos); if constexpr (is_specialization_v<T, std::vector>) { if constexpr (is_specialization_v<T, std::vector> || is_fixed_array_v<T>) { return readData(&**c, READ_FLAG_SP_NULLABLE); // nullable sp<> allowed now } else { return readData(&**c); Loading Loading @@ -1142,6 +1192,41 @@ private: return OK; } template <typename T, size_t N> status_t readData(std::array<T, N>* val, ReadFlags readFlags = READ_FLAG_NONE) const { static_assert(N <= std::numeric_limits<int32_t>::max()); int32_t size; status_t status = readInt32(&size); if (status != OK) return status; if (size < 0) return UNEXPECTED_NULL; if (size != static_cast<int32_t>(N)) return BAD_VALUE; if constexpr (is_pointer_equivalent_array_v<T>) { auto data = reinterpret_cast<const T*>(readInplace(N * sizeof(T))); if (data == nullptr) return BAD_VALUE; memcpy(val->data(), data, N * sizeof(T)); } else if constexpr (is_specialization_v<T, sp>) { for (auto& t : *val) { if (readFlags & READ_FLAG_SP_NULLABLE) { status = readNullableStrongBinder(&t); // allow nullable } else { status = readStrongBinder(&t); } if (status != OK) return status; } } else if constexpr (is_fixed_array_v<T>) { // pass readFlags down to nested arrays for (auto& t : *val) { status = readData(&t, readFlags); if (status != OK) return status; } } else /* constexpr */ { for (auto& t : *val) { status = readData(&t); if (status != OK) return status; } } return OK; } //----------------------------------------------------------------------------- private: Loading
libs/binder/tests/parcel_fuzzer/binder.cpp +26 −0 Original line number Diff line number Diff line Loading @@ -233,6 +233,32 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<std::string>>>, readUtf8VectorFromUtf16Vector), PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector), #define COMMA , PARCEL_READ_WITH_STATUS(std::array<uint8_t COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<uint8_t COMMA 3>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<char16_t COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<char16_t COMMA 3>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<std::string COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<std::string> COMMA 3>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<android::String16 COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<android::String16> COMMA 3>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<android::sp<android::IBinder> COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<android::sp<android::IBinder> COMMA 3>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<ExampleParcelable COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<ExampleParcelable> COMMA 3>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<ByteEnum COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<ByteEnum COMMA 3>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<IntEnum COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<IntEnum COMMA 3>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<LongEnum COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<LongEnum COMMA 3>>, readFixedArray), // nested arrays PARCEL_READ_WITH_STATUS(std::array<std::array<uint8_t COMMA 3> COMMA 4>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<std::array<uint8_t COMMA 3> COMMA 4>>, readFixedArray), PARCEL_READ_WITH_STATUS(std::array<ExampleParcelable COMMA 3>, readFixedArray), PARCEL_READ_WITH_STATUS(std::optional<std::array<std::array<std::optional<ExampleParcelable> COMMA 3> COMMA 4>>, readFixedArray), #undef COMMA [] (const android::Parcel& p, uint8_t /*len*/) { FUZZ_LOG() << "about to read flattenable"; ExampleFlattenable f; Loading