diff options
| -rw-r--r-- | libs/binder/include/binder/Parcel.h | 27 | ||||
| -rw-r--r-- | libs/binder/ndk/include_cpp/android/binder_parcel_utils.h | 190 | ||||
| -rw-r--r-- | libs/binder/rust/src/parcel/parcelable.rs | 4 | ||||
| -rw-r--r-- | libs/binder/tests/parcel_fuzzer/binder.cpp | 2 | ||||
| -rw-r--r-- | libs/binder/tests/parcel_fuzzer/binder_ndk.cpp | 38 |
5 files changed, 194 insertions, 67 deletions
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 8fb4a377d6..9670d7bba6 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -207,6 +207,23 @@ public: status_t writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val) __attribute__((deprecated("use std::optional version instead"))); status_t writeStrongBinderVector(const std::vector<sp<IBinder>>& val); + // Write an IInterface or a vector of IInterface's + template <typename T, + std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true> + status_t writeStrongBinder(const sp<T>& val) { + return writeStrongBinder(T::asBinder(val)); + } + template <typename T, + std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true> + status_t writeStrongBinderVector(const std::vector<sp<T>>& val) { + return writeData(val); + } + template <typename T, + std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true> + status_t writeStrongBinderVector(const std::optional<std::vector<sp<T>>>& 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> @@ -421,6 +438,16 @@ public: status_t readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const; status_t readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const __attribute__((deprecated("use std::optional version instead"))); status_t readStrongBinderVector(std::vector<sp<IBinder>>* val) const; + template <typename T, + std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true> + status_t readStrongBinderVector(std::vector<sp<T>>* val) const { + return readData(val); + } + template <typename T, + std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true> + status_t readStrongBinderVector(std::optional<std::vector<sp<T>>>* val) const { + return readData(val); + } status_t readByteVector(std::optional<std::vector<int8_t>>* val) const; status_t readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const __attribute__((deprecated("use std::optional version instead"))); diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h index 2b18a0a1e0..67623a6556 100644 --- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h @@ -27,15 +27,67 @@ #pragma once #include <android/binder_auto_utils.h> +#include <android/binder_interface_utils.h> #include <android/binder_internal_logging.h> #include <android/binder_parcel.h> #include <optional> #include <string> +#include <type_traits> #include <vector> namespace ndk { +namespace { +template <typename Test, template <typename...> class Ref> +struct is_specialization : std::false_type {}; + +template <template <typename...> class Ref, typename... Args> +struct is_specialization<Ref<Args...>, Ref> : std::true_type {}; + +template <typename Test, template <typename...> class Ref> +static inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value; + +// Get the first template type from a container, the T from MyClass<T, ...>. +template <typename T> +struct first_template_type { + using type = void; +}; + +template <template <typename...> class V, typename T, typename... Args> +struct first_template_type<V<T, Args...>> { + using type = T; +}; + +template <typename T> +using first_template_type_t = typename first_template_type<T>::type; + +// Tells if T represents NDK interface (shared_ptr<ICInterface-derived>) +template <typename T> +static inline constexpr bool is_interface_v = is_specialization_v<T, std::shared_ptr>&& + std::is_base_of_v<::ndk::ICInterface, first_template_type_t<T>>; + +// Tells if T represents NDK parcelable with readFromParcel/writeToParcel methods defined +template <typename T, typename = void> +struct is_parcelable : std::false_type {}; + +template <typename T> +struct is_parcelable< + T, std::void_t<decltype(std::declval<T>().readFromParcel(std::declval<const AParcel*>())), + decltype(std::declval<T>().writeToParcel(std::declval<AParcel*>()))>> + : std::true_type {}; + +template <typename T> +static inline constexpr bool is_parcelable_v = is_parcelable<T>::value; + +// Tells if T represents nullable NDK parcelable (optional<parcelable> or unique_ptr<parcelable>) +template <typename T> +static inline constexpr bool is_nullable_parcelable_v = is_parcelable_v<first_template_type_t<T>> && + (is_specialization_v<T, std::optional> || + is_specialization_v<T, std::unique_ptr>); + +} // namespace + /** * This retrieves and allocates a vector to size 'length' and returns the underlying buffer. */ @@ -429,11 +481,19 @@ static inline binder_status_t AParcel_readVector( */ template <typename P> static inline binder_status_t AParcel_writeParcelable(AParcel* parcel, const P& p) { - binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null - if (status != STATUS_OK) { - return status; + if constexpr (is_interface_v<P>) { + if (!p) { + return STATUS_UNEXPECTED_NULL; + } + return first_template_type_t<P>::writeToParcel(parcel, p); + } else { + static_assert(is_parcelable_v<P>); + binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null + if (status != STATUS_OK) { + return status; + } + return p.writeToParcel(parcel); } - return p.writeToParcel(parcel); } /** @@ -441,85 +501,81 @@ static inline binder_status_t AParcel_writeParcelable(AParcel* parcel, const P& */ template <typename P> static inline binder_status_t AParcel_readParcelable(const AParcel* parcel, P* p) { - int32_t null; - binder_status_t status = AParcel_readInt32(parcel, &null); - if (status != STATUS_OK) { - return status; - } - if (null == 0) { - return STATUS_UNEXPECTED_NULL; - } - return p->readFromParcel(parcel); -} - -/** - * Convenience API for writing a nullable parcelable. - */ -template <typename P> -static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel, - const std::optional<P>& p) { - if (p == std::nullopt) { - return AParcel_writeInt32(parcel, 0); // null - } - binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null - if (status != STATUS_OK) { + if constexpr (is_interface_v<P>) { + binder_status_t status = first_template_type_t<P>::readFromParcel(parcel, p); + if (status == STATUS_OK) { + if (!*p) { + return STATUS_UNEXPECTED_NULL; + } + } return status; + } else { + static_assert(is_parcelable_v<P>); + int32_t null; + binder_status_t status = AParcel_readInt32(parcel, &null); + if (status != STATUS_OK) { + return status; + } + if (null == 0) { + return STATUS_UNEXPECTED_NULL; + } + return p->readFromParcel(parcel); } - return p->writeToParcel(parcel); } /** * Convenience API for writing a nullable parcelable. */ template <typename P> -static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel, - const std::unique_ptr<P>& p) { - if (!p) { - return AParcel_writeInt32(parcel, 0); // null - } - binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null - if (status != STATUS_OK) { - return status; - } - return p->writeToParcel(parcel); -} - -/** - * Convenience API for reading a nullable parcelable. - */ -template <typename P> -static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel, - std::optional<P>* p) { - int32_t null; - binder_status_t status = AParcel_readInt32(parcel, &null); - if (status != STATUS_OK) { - return status; - } - if (null == 0) { - *p = std::nullopt; - return STATUS_OK; +static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel, const P& p) { + if constexpr (is_interface_v<P>) { + return first_template_type_t<P>::writeToParcel(parcel, p); + } else { + static_assert(is_nullable_parcelable_v<P>); + if (!p) { + return AParcel_writeInt32(parcel, 0); // null + } + binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null + if (status != STATUS_OK) { + return status; + } + return p->writeToParcel(parcel); } - *p = std::optional<P>(P{}); - return (*p)->readFromParcel(parcel); } /** * Convenience API for reading a nullable parcelable. */ template <typename P> -static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel, - std::unique_ptr<P>* p) { - int32_t null; - binder_status_t status = AParcel_readInt32(parcel, &null); - if (status != STATUS_OK) { - return status; - } - if (null == 0) { - p->reset(); - return STATUS_OK; +static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel, P* p) { + if constexpr (is_interface_v<P>) { + return first_template_type_t<P>::readFromParcel(parcel, p); + } else if constexpr (is_specialization_v<P, std::optional>) { + int32_t null; + binder_status_t status = AParcel_readInt32(parcel, &null); + if (status != STATUS_OK) { + return status; + } + if (null == 0) { + *p = std::nullopt; + return STATUS_OK; + } + *p = std::optional<first_template_type_t<P>>(first_template_type_t<P>{}); + return (*p)->readFromParcel(parcel); + } else { + static_assert(is_specialization_v<P, std::unique_ptr>); + int32_t null; + binder_status_t status = AParcel_readInt32(parcel, &null); + if (status != STATUS_OK) { + return status; + } + if (null == 0) { + p->reset(); + return STATUS_OK; + } + *p = std::make_unique<first_template_type_t<P>>(); + return (*p)->readFromParcel(parcel); } - *p = std::make_unique<P>(); - return (*p)->readFromParcel(parcel); } /** diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs index ec00e1dc86..db9d8b0011 100644 --- a/libs/binder/rust/src/parcel/parcelable.rs +++ b/libs/binder/rust/src/parcel/parcelable.rs @@ -702,6 +702,8 @@ impl<T: SerializeOption + FromIBinder + ?Sized> SerializeOption for Strong<T> { } } +impl<T: Serialize + FromIBinder + ?Sized> SerializeArray for Strong<T> {} + impl<T: FromIBinder + ?Sized> Deserialize for Strong<T> { fn deserialize(parcel: &Parcel) -> Result<Self> { let ibinder: SpIBinder = parcel.read()?; @@ -716,6 +718,8 @@ impl<T: FromIBinder + ?Sized> DeserializeOption for Strong<T> { } } +impl<T: FromIBinder + ?Sized> DeserializeArray for Strong<T> {} + // We need these to support Option<&T> for all T impl<T: Serialize + ?Sized> Serialize for &T { fn serialize(&self, parcel: &mut Parcel) -> Result<()> { diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index 155a25bbcd..32406e599c 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -192,6 +192,8 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { // only reading one binder type for now PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readStrongBinder), PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readNullableStrongBinder), + PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::os::IServiceManager>>, readStrongBinderVector), + PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::sp<android::os::IServiceManager>>>, readStrongBinderVector), PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector), PARCEL_READ_WITH_STATUS(::std::optional<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector), diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp index c0a762d1de..752fcbb34c 100644 --- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp @@ -25,6 +25,7 @@ // TODO(b/142061461): parent class class SomeParcelable { public: + binder_status_t writeToParcel(AParcel* /*parcel*/) { return STATUS_OK; } binder_status_t readFromParcel(const AParcel* parcel) { return AParcel_readInt32(parcel, &mValue); } @@ -33,6 +34,41 @@ private: int32_t mValue = 0; }; +class ISomeInterface : public ::ndk::ICInterface { +public: + ISomeInterface() = default; + virtual ~ISomeInterface() = default; + static binder_status_t readFromParcel(const AParcel* parcel, + std::shared_ptr<ISomeInterface>* instance); +}; + +static binder_status_t onTransact(AIBinder*, transaction_code_t, const AParcel*, AParcel*) { + return STATUS_UNKNOWN_TRANSACTION; +} + +static AIBinder_Class* g_class = ::ndk::ICInterface::defineClass("ISomeInterface", onTransact); + +class BpSomeInterface : public ::ndk::BpCInterface<ISomeInterface> { +public: + explicit BpSomeInterface(const ::ndk::SpAIBinder& binder) : BpCInterface(binder) {} + virtual ~BpSomeInterface() = default; +}; + +binder_status_t ISomeInterface::readFromParcel(const AParcel* parcel, + std::shared_ptr<ISomeInterface>* instance) { + ::ndk::SpAIBinder binder; + binder_status_t status = AParcel_readStrongBinder(parcel, binder.getR()); + if (status == STATUS_OK) { + if (AIBinder_associateClass(binder.get(), g_class)) { + *instance = std::static_pointer_cast<ISomeInterface>( + ::ndk::ICInterface::asInterface(binder.get())); + } else { + *instance = ::ndk::SharedRefBase::make<BpSomeInterface>(binder); + } + } + return status; +} + #define PARCEL_READ(T, FUN) \ [](const NdkParcelAdapter& p, uint8_t /*data*/) { \ FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \ @@ -100,6 +136,8 @@ std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{ PARCEL_READ(std::optional<std::vector<ndk::SpAIBinder>>, ndk::AParcel_readVector), PARCEL_READ(std::vector<ndk::ScopedFileDescriptor>, ndk::AParcel_readVector), PARCEL_READ(std::optional<std::vector<ndk::ScopedFileDescriptor>>, ndk::AParcel_readVector), + PARCEL_READ(std::vector<std::shared_ptr<ISomeInterface>>, ndk::AParcel_readVector), + PARCEL_READ(std::optional<std::vector<std::shared_ptr<ISomeInterface>>>, ndk::AParcel_readVector), PARCEL_READ(std::vector<int32_t>, ndk::AParcel_readVector), PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_readVector), PARCEL_READ(std::vector<uint32_t>, ndk::AParcel_readVector), |