Make hidl_vec/hidl_string safe for 32<->64 IPC.
Pointers going over IPC always need to be 64-bit.
This change introduces a hidl_pointer class that
wraps a pointer (without taking ownership), and
stuffs it in a union with a uint64_t to ensure we
always have 64-bits.
We can map size_t to either a fixed 32-bit or fixed
64-bit type. Since sending objects of more than
2^32 bytes seems a while out, we can save ourselves
4 bytes by allowing a maximum of 2^32 elements in
hidl_vec, and 2^32 characters in a string.
Bug: 32089785
Test: hidl_test, hidl_string 32->64->32
Change-Id: Ife77f7362013226aada51c7686e33494390824d7
diff --git a/include/hidl/HidlSupport.h b/include/hidl/HidlSupport.h
index 48cfd26..c11d680 100644
--- a/include/hidl/HidlSupport.h
+++ b/include/hidl/HidlSupport.h
@@ -33,6 +33,77 @@
namespace android {
namespace hardware {
+namespace details {
+
+// hidl_log_base is a base class that templatized
+// classes implemented in a header can inherit from,
+// to avoid creating dependencies on liblog.
+struct hidl_log_base {
+ void logAlwaysFatal(const char *message);
+};
+
+// HIDL client/server code should *NOT* use this class.
+//
+// hidl_pointer wraps a pointer without taking ownership,
+// and stores it in a union with a uint64_t. This ensures
+// that we always have enough space to store a pointer,
+// regardless of whether we're running in a 32-bit or 64-bit
+// process.
+template<typename T>
+struct hidl_pointer {
+ hidl_pointer()
+ : mPointer(nullptr) {
+ }
+ hidl_pointer(T* ptr)
+ : mPointer(ptr) {
+ }
+ hidl_pointer(const hidl_pointer<T>& other) {
+ mPointer = other.mPointer;
+ }
+ hidl_pointer(hidl_pointer<T>&& other) {
+ *this = std::move(other);
+ }
+
+ hidl_pointer &operator=(const hidl_pointer<T>& other) {
+ mPointer = other.mPointer;
+ return *this;
+ }
+ hidl_pointer &operator=(hidl_pointer<T>&& other) {
+ mPointer = other.mPointer;
+ other.mPointer = nullptr;
+ return *this;
+ }
+ hidl_pointer &operator=(T* ptr) {
+ mPointer = ptr;
+ return *this;
+ }
+
+ operator T*() const {
+ return mPointer;
+ }
+ explicit operator void*() const { // requires explicit cast to avoid ambiguity
+ return mPointer;
+ }
+ T& operator*() const {
+ return *mPointer;
+ }
+ T* operator->() const {
+ return mPointer;
+ }
+ T &operator[](size_t index) {
+ return mPointer[index];
+ }
+ const T &operator[](size_t index) const {
+ return mPointer[index];
+ }
+private:
+ union {
+ T* mPointer;
+ uint64_t _pad;
+ };
+};
+} // namespace details
+
struct hidl_string {
hidl_string();
~hidl_string();
@@ -76,8 +147,8 @@
static const size_t kOffsetOfBuffer;
private:
- const char *mBuffer;
- size_t mSize; // NOT including the terminating '\0'.
+ details::hidl_pointer<const char> mBuffer;
+ uint32_t mSize; // NOT including the terminating '\0'.
bool mOwnsBuffer; // if true then mBuffer is a mutable char *
// copy from data with size. Assume that my memory is freed
@@ -106,7 +177,7 @@
////////////////////////////////////////////////////////////////////////////////
template<typename T>
-struct hidl_vec {
+struct hidl_vec : private details::hidl_log_base {
hidl_vec()
: mBuffer(NULL),
mSize(0),
@@ -152,7 +223,10 @@
delete [] mBuffer;
}
mBuffer = data;
- mSize = size;
+ if (size > UINT32_MAX) {
+ logAlwaysFatal("external vector size exceeds 2^32 elements.");
+ }
+ mSize = static_cast<uint32_t>(size);
mOwnsBuffer = shouldOwn;
}
@@ -225,9 +299,12 @@
}
void resize(size_t size) {
+ if (size > UINT32_MAX) {
+ logAlwaysFatal("hidl_vec can't hold more than 2^32 elements.");
+ }
T *newBuffer = new T[size];
- for (size_t i = 0; i < std::min(size, mSize); ++i) {
+ for (size_t i = 0; i < std::min(static_cast<uint32_t>(size), mSize); ++i) {
newBuffer[i] = mBuffer[i];
}
@@ -236,15 +313,15 @@
}
mBuffer = newBuffer;
- mSize = size;
+ mSize = static_cast<uint32_t>(size);
mOwnsBuffer = true;
}
// offsetof(hidl_string, mBuffer) exposed since mBuffer is private.
static const size_t kOffsetOfBuffer;
private:
- T *mBuffer;
- size_t mSize;
+ details::hidl_pointer<T> mBuffer;
+ uint32_t mSize;
bool mOwnsBuffer;
// copy from an array-like object, assuming my resources are freed.