libbinder_ndk: nullable primitive arrays
Now consistent w/ String[].
Bug: 111445392
Test: atest android.binder.cts
Change-Id: Ie671e5b08a41d4b49d9afa83d2836b6ca35847a3
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 4136698..b1a2955 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -36,28 +36,46 @@
using ::android::os::ParcelFileDescriptor;
template <typename T>
-using ContiguousArrayAllocator = T* (*)(void* arrayData, size_t length);
+using ContiguousArrayAllocator = bool (*)(void* arrayData, int32_t length, T** outBuffer);
template <typename T>
-using ArrayAllocator = bool (*)(void* arrayData, size_t length);
+using ArrayAllocator = bool (*)(void* arrayData, int32_t length);
template <typename T>
using ArrayGetter = T (*)(const void* arrayData, size_t index);
template <typename T>
using ArraySetter = void (*)(void* arrayData, size_t index, T value);
-template <typename T>
-binder_status_t WriteArray(AParcel* parcel, const T* array, size_t length) {
- if (length > std::numeric_limits<int32_t>::max()) return STATUS_BAD_VALUE;
+binder_status_t WriteAndValidateArraySize(AParcel* parcel, bool isNullArray, int32_t length) {
+ // only -1 can be used to represent a null array
+ if (length < -1) return STATUS_BAD_VALUE;
+
+ if (!isNullArray && length < 0) {
+ LOG(ERROR) << __func__ << ": null array must be used with length == -1.";
+ return STATUS_BAD_VALUE;
+ }
+ if (isNullArray && length > 0) {
+ LOG(ERROR) << __func__ << ": null buffer cannot be for size " << length << " array.";
+ return STATUS_BAD_VALUE;
+ }
Parcel* rawParcel = parcel->get();
status_t status = rawParcel->writeInt32(static_cast<int32_t>(length));
if (status != STATUS_OK) return PruneStatusT(status);
+ return STATUS_OK;
+}
+
+template <typename T>
+binder_status_t WriteArray(AParcel* parcel, const T* array, int32_t length) {
+ binder_status_t status = WriteAndValidateArraySize(parcel, array == nullptr, length);
+ if (status != STATUS_OK) return status;
+ if (length <= 0) return STATUS_OK;
+
int32_t size = 0;
if (__builtin_smul_overflow(sizeof(T), length, &size)) return STATUS_NO_MEMORY;
- void* const data = rawParcel->writeInplace(size);
+ void* const data = parcel->get()->writeInplace(size);
if (data == nullptr) return STATUS_NO_MEMORY;
memcpy(data, array, size);
@@ -67,17 +85,16 @@
// Each element in a char16_t array is converted to an int32_t (not packed).
template <>
-binder_status_t WriteArray<char16_t>(AParcel* parcel, const char16_t* array, size_t length) {
- if (length > std::numeric_limits<int32_t>::max()) return STATUS_BAD_VALUE;
-
- Parcel* rawParcel = parcel->get();
-
- status_t status = rawParcel->writeInt32(static_cast<int32_t>(length));
- if (status != STATUS_OK) return PruneStatusT(status);
+binder_status_t WriteArray<char16_t>(AParcel* parcel, const char16_t* array, int32_t length) {
+ binder_status_t status = WriteAndValidateArraySize(parcel, array == nullptr, length);
+ if (status != STATUS_OK) return status;
+ if (length <= 0) return STATUS_OK;
int32_t size = 0;
if (__builtin_smul_overflow(sizeof(char16_t), length, &size)) return STATUS_NO_MEMORY;
+ Parcel* rawParcel = parcel->get();
+
for (int32_t i = 0; i < length; i++) {
status = rawParcel->writeChar(array[i]);
@@ -96,10 +113,12 @@
status_t status = rawParcel->readInt32(&length);
if (status != STATUS_OK) return PruneStatusT(status);
- if (length < 0) return STATUS_UNEXPECTED_NULL;
+ if (length < -1) return STATUS_BAD_VALUE;
- T* array = allocator(arrayData, length);
- if (length == 0) return STATUS_OK;
+ T* array;
+ if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY;
+
+ if (length <= 0) return STATUS_OK;
if (array == nullptr) return STATUS_NO_MEMORY;
int32_t size = 0;
@@ -123,10 +142,12 @@
status_t status = rawParcel->readInt32(&length);
if (status != STATUS_OK) return PruneStatusT(status);
- if (length < 0) return STATUS_UNEXPECTED_NULL;
+ if (length < -1) return STATUS_BAD_VALUE;
- char16_t* array = allocator(arrayData, length);
- if (length == 0) return STATUS_OK;
+ char16_t* array;
+ if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY;
+
+ if (length <= 0) return STATUS_OK;
if (array == nullptr) return STATUS_NO_MEMORY;
int32_t size = 0;
@@ -142,15 +163,16 @@
}
template <typename T>
-binder_status_t WriteArray(AParcel* parcel, const void* arrayData, size_t length,
+binder_status_t WriteArray(AParcel* parcel, const void* arrayData, int32_t length,
ArrayGetter<T> getter, status_t (Parcel::*write)(T)) {
- if (length > std::numeric_limits<int32_t>::max()) return STATUS_BAD_VALUE;
+ // we have no clue if arrayData represents a null object or not, we can only infer from length
+ bool arrayIsNull = length < 0;
+ binder_status_t status = WriteAndValidateArraySize(parcel, arrayIsNull, length);
+ if (status != STATUS_OK) return status;
+ if (length <= 0) return STATUS_OK;
Parcel* rawParcel = parcel->get();
- status_t status = rawParcel->writeInt32(static_cast<int32_t>(length));
- if (status != STATUS_OK) return PruneStatusT(status);
-
for (size_t i = 0; i < length; i++) {
status = (rawParcel->*write)(getter(arrayData, i));
@@ -169,10 +191,12 @@
status_t status = rawParcel->readInt32(&length);
if (status != STATUS_OK) return PruneStatusT(status);
- if (length < 0) return STATUS_UNEXPECTED_NULL;
+ if (length < -1) return STATUS_BAD_VALUE;
if (!allocator(arrayData, length)) return STATUS_NO_MEMORY;
+ if (length <= 0) return STATUS_OK;
+
for (size_t i = 0; i < length; i++) {
T readTarget;
status = (rawParcel->*read)(&readTarget);
@@ -317,20 +341,11 @@
binder_status_t AParcel_writeStringArray(AParcel* parcel, const void* arrayData, int32_t length,
AParcel_stringArrayElementGetter getter) {
- Parcel* rawParcel = parcel->get();
-
- if (length < 0) {
- if (length != -1) {
- LOG(WARNING) << __func__ << ": null array must be used with length == -1.";
- return STATUS_BAD_VALUE;
- }
-
- status_t status = rawParcel->writeInt32(-1);
- return PruneStatusT(status);
- }
-
- status_t status = rawParcel->writeInt32(static_cast<int32_t>(length));
- if (status != STATUS_OK) return PruneStatusT(status);
+ // we have no clue if arrayData represents a null object or not, we can only infer from length
+ bool arrayIsNull = length < 0;
+ binder_status_t status = WriteAndValidateArraySize(parcel, arrayIsNull, length);
+ if (status != STATUS_OK) return status;
+ if (length <= 0) return STATUS_OK;
for (size_t i = 0; i < length; i++) {
size_t length = 0;
@@ -482,42 +497,42 @@
return PruneStatusT(status);
}
-binder_status_t AParcel_writeInt32Array(AParcel* parcel, const int32_t* arrayData, size_t length) {
+binder_status_t AParcel_writeInt32Array(AParcel* parcel, const int32_t* arrayData, int32_t length) {
return WriteArray<int32_t>(parcel, arrayData, length);
}
binder_status_t AParcel_writeUint32Array(AParcel* parcel, const uint32_t* arrayData,
- size_t length) {
+ int32_t length) {
return WriteArray<uint32_t>(parcel, arrayData, length);
}
-binder_status_t AParcel_writeInt64Array(AParcel* parcel, const int64_t* arrayData, size_t length) {
+binder_status_t AParcel_writeInt64Array(AParcel* parcel, const int64_t* arrayData, int32_t length) {
return WriteArray<int64_t>(parcel, arrayData, length);
}
binder_status_t AParcel_writeUint64Array(AParcel* parcel, const uint64_t* arrayData,
- size_t length) {
+ int32_t length) {
return WriteArray<uint64_t>(parcel, arrayData, length);
}
-binder_status_t AParcel_writeFloatArray(AParcel* parcel, const float* arrayData, size_t length) {
+binder_status_t AParcel_writeFloatArray(AParcel* parcel, const float* arrayData, int32_t length) {
return WriteArray<float>(parcel, arrayData, length);
}
-binder_status_t AParcel_writeDoubleArray(AParcel* parcel, const double* arrayData, size_t length) {
+binder_status_t AParcel_writeDoubleArray(AParcel* parcel, const double* arrayData, int32_t length) {
return WriteArray<double>(parcel, arrayData, length);
}
-binder_status_t AParcel_writeBoolArray(AParcel* parcel, const void* arrayData, size_t length,
+binder_status_t AParcel_writeBoolArray(AParcel* parcel, const void* arrayData, int32_t length,
AParcel_boolArrayGetter getter) {
return WriteArray<bool>(parcel, arrayData, length, getter, &Parcel::writeBool);
}
-binder_status_t AParcel_writeCharArray(AParcel* parcel, const char16_t* arrayData, size_t length) {
+binder_status_t AParcel_writeCharArray(AParcel* parcel, const char16_t* arrayData, int32_t length) {
return WriteArray<char16_t>(parcel, arrayData, length);
}
-binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* arrayData, size_t length) {
+binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* arrayData, int32_t length) {
return WriteArray<int8_t>(parcel, arrayData, length);
}