libbinder: resizeOutVector size check
Like the other functions, but with a much higher limit since we can't
actually know the size of a vector that will be returned. Likely this
value could be scoped down incrementally.
Bug: 131868573
Test: binder_parcel_fuzzer
Change-Id: I8a73fd51bf25b66fb1398f9715891bbb5d6f0857
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index e4dfa52..9795348 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1466,6 +1466,29 @@
return nullptr;
}
+status_t Parcel::readOutVectorSizeWithCheck(size_t elmSize, int32_t* size) const {
+ if (status_t status = readInt32(size); status != OK) return status;
+ if (*size < 0) return OK; // may be null, client to handle
+
+ LOG_ALWAYS_FATAL_IF(elmSize > INT32_MAX, "Cannot have element as big as %zu", elmSize);
+
+ // approximation, can't know max element size (e.g. if it makes heap
+ // allocations)
+ static_assert(sizeof(int) == sizeof(int32_t), "Android is LP64");
+ int32_t allocationSize;
+ if (__builtin_smul_overflow(elmSize, *size, &allocationSize)) return NO_MEMORY;
+
+ // High limit of 1MB since something this big could never be returned. Could
+ // probably scope this down, but might impact very specific usecases.
+ constexpr int32_t kMaxAllocationSize = 1 * 1000 * 1000;
+
+ if (allocationSize >= kMaxAllocationSize) {
+ return NO_MEMORY;
+ }
+
+ return OK;
+}
+
template<class T>
status_t Parcel::readAligned(T *pArg) const {
static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 5aaaa0c..02052ad 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -561,6 +561,8 @@
status_t flattenBinder(const sp<IBinder>& binder);
status_t unflattenBinder(sp<IBinder>* out) const;
+ status_t readOutVectorSizeWithCheck(size_t elmSize, int32_t* size) const;
+
template<class T>
status_t readAligned(T *pArg) const;
@@ -1315,7 +1317,7 @@
template<typename T>
status_t Parcel::resizeOutVector(std::vector<T>* val) const {
int32_t size;
- status_t err = readInt32(&size);
+ status_t err = readOutVectorSizeWithCheck(sizeof(T), &size);
if (err != NO_ERROR) {
return err;
}
@@ -1330,7 +1332,7 @@
template<typename T>
status_t Parcel::resizeOutVector(std::optional<std::vector<T>>* val) const {
int32_t size;
- status_t err = readInt32(&size);
+ status_t err = readOutVectorSizeWithCheck(sizeof(T), &size);
if (err != NO_ERROR) {
return err;
}
@@ -1346,7 +1348,7 @@
template<typename T>
status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const {
int32_t size;
- status_t err = readInt32(&size);
+ status_t err = readOutVectorSizeWithCheck(sizeof(T), &size);
if (err != NO_ERROR) {
return err;
}
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index cbb0a18..5f2c17c 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -66,6 +66,10 @@
int32_t mValue = 0;
};
+struct BigStruct {
+ uint8_t data[1337];
+};
+
#define PARCEL_READ_WITH_STATUS(T, FUN) \
[] (const ::android::Parcel& p, uint8_t /*data*/) {\
FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\
@@ -231,8 +235,12 @@
FUZZ_LOG() << "read lite flattenable: " << status;
},
- // TODO(b/131868573): can force read of arbitrarily sized vector
- // TODO: resizeOutVector
+ PARCEL_READ_WITH_STATUS(std::vector<uint8_t>, resizeOutVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint8_t>>, resizeOutVector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint8_t>>, resizeOutVector),
+ PARCEL_READ_WITH_STATUS(std::vector<BigStruct>, resizeOutVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<BigStruct>>, resizeOutVector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<BigStruct>>, resizeOutVector),
PARCEL_READ_NO_STATUS(int32_t, readExceptionCode),
[] (const android::Parcel& p, uint8_t /*len*/) {