diff options
| -rw-r--r-- | libs/binder/include/binder/Parcel.h | 99 | ||||
| -rw-r--r-- | libs/binder/tests/parcel_fuzzer/binder.cpp | 26 | 
2 files changed, 118 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/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; |