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

Commit e26e5ca5 authored by Jooyung Han's avatar Jooyung Han
Browse files

libbinder: Parcel APIs for fixed-size arrays

readFixedArray/writeFixedArray for std::array<T,N> are added to Parcel
class. These recursively calls readData/writeData when arrays are
nested.

Arrays of primitive values are serialized as contiguous values just like
std::vector<T>. For example, std::array<uint8_t, N> is serialized in
single-byte strides.

Bug: 207087196
Test: binder_parcel_fuzzer
Change-Id: I333c386cbe281d44ce15b45a343d03cdbbdccb6d
parent 3183b5c2
Loading
Loading
Loading
Loading
+92 −7
Original line number Diff line number Diff line
@@ -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,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>);
        }
@@ -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;
@@ -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;
@@ -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:

+26 −0
Original line number Diff line number Diff line
@@ -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;