diff options
| -rw-r--r-- | libs/binder/include/binder/Parcel.h | 99 | ||||
| -rw-r--r-- | libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h | 12 | ||||
| -rw-r--r-- | libs/binder/tests/parcel_fuzzer/binder.cpp | 26 |
3 files changed, 130 insertions, 7 deletions
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 9670d7bba6..8dbdc1d115 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -16,6 +16,7 @@ #pragma once +#include <array> #include <map> // for legacy reasons #include <string> #include <type_traits> @@ -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> @@ -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; @@ -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; @@ -922,7 +951,9 @@ 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 /* constexpr */ { // could define this, but raise as error. + } 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>); } return writeData(*c); @@ -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. @@ -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; @@ -1065,12 +1112,15 @@ private: c->reset(); return OK; } - } else /* constexpr */ { // could define this, but raise as error. + } else /* constexpr */ { // could define this, but raise as error. static_assert(dependent_false_v<CT>); } // 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; @@ -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); @@ -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: diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h index aa3b97806b..972eca79ff 100644 --- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h @@ -46,6 +46,18 @@ class AParcelableHolder { AParcelableHolder() = delete; explicit AParcelableHolder(parcelable_stability_t stability) : mParcel(AParcel_create()), mStability(stability) {} + +#if __ANDROID_API__ >= 31 + AParcelableHolder(const AParcelableHolder& other) + : mParcel(AParcel_create()), mStability(other.mStability) { + // AParcelableHolder has been introduced in 31. + if (__builtin_available(android 31, *)) { + AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0, + AParcel_getDataSize(other.mParcel.get())); + } + } +#endif + AParcelableHolder(AParcelableHolder&& other) = default; virtual ~AParcelableHolder() = default; diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index 32406e599c..077d915ca1 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -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; |