[fidl] Enforce that the buffer to encode/decode/linearize is aligned
TEST: /boot/test/sys/fidl-test
Change-Id: If753ff134ce82c14e35a1ead320174cca51b4264
diff --git a/system/ulib/fidl/decoding.cpp b/system/ulib/fidl/decoding.cpp
index 656280e..e5fbc2d 100644
--- a/system/ulib/fidl/decoding.cpp
+++ b/system/ulib/fidl/decoding.cpp
@@ -218,6 +218,10 @@
set_error("Cannot decode null bytes");
return ZX_ERR_INVALID_ARGS;
}
+ if (!fidl::IsAligned(reinterpret_cast<uint8_t*>(bytes))) {
+ set_error("Bytes must be aligned to FIDL_ALIGNMENT");
+ return ZX_ERR_INVALID_ARGS;
+ }
if (handles == nullptr && num_handles != 0) {
set_error("Cannot provide non-zero handle count and null handle pointer");
return ZX_ERR_INVALID_ARGS;
diff --git a/system/ulib/fidl/encoding.cpp b/system/ulib/fidl/encoding.cpp
index 81a5ee2..b1c1c17 100644
--- a/system/ulib/fidl/encoding.cpp
+++ b/system/ulib/fidl/encoding.cpp
@@ -216,6 +216,10 @@
set_error("Cannot encode null bytes");
return ZX_ERR_INVALID_ARGS;
}
+ if (!fidl::IsAligned(reinterpret_cast<uint8_t*>(bytes))) {
+ set_error("Bytes must be aligned to FIDL_ALIGNMENT");
+ return ZX_ERR_INVALID_ARGS;
+ }
if (handles == nullptr && max_handles != 0) {
set_error("Cannot provide non-zero handle count and null handle pointer");
return ZX_ERR_INVALID_ARGS;
diff --git a/system/ulib/fidl/include/lib/fidl/internal.h b/system/ulib/fidl/include/lib/fidl/internal.h
index 036757c..2783b15 100644
--- a/system/ulib/fidl/include/lib/fidl/internal.h
+++ b/system/ulib/fidl/include/lib/fidl/internal.h
@@ -6,7 +6,7 @@
#define LIB_FIDL_INTERNAL_H_
#include <assert.h>
-#include <stdint.h>
+#include <cstdint>
#include <lib/fidl/coding.h>
#include <zircon/syscalls/object.h>
@@ -35,6 +35,13 @@
return (offset + alignment_mask) & ~alignment_mask;
}
+// Determine if the pointer is aligned to |FIDL_ALIGNMENT|.
+inline bool IsAligned(uint8_t* ptr) {
+ auto uintptr = reinterpret_cast<std::uintptr_t>(ptr);
+ constexpr uintptr_t kAlignment = FIDL_ALIGNMENT;
+ return uintptr % kAlignment == 0;
+}
+
// Add |size| to out-of-line |offset|, maintaining alignment. For example, a pointer to a struct
// that is 4 bytes still needs to advance the next out-of-line offset by 8 to maintain
// the aligned-to-FIDL_ALIGNMENT property.
diff --git a/system/ulib/fidl/linearizing.cpp b/system/ulib/fidl/linearizing.cpp
index 3713745..0ce1134 100644
--- a/system/ulib/fidl/linearizing.cpp
+++ b/system/ulib/fidl/linearizing.cpp
@@ -212,6 +212,10 @@
set_error("Cannot linearize with null destination buffer");
return ZX_ERR_INVALID_ARGS;
}
+ if (!fidl::IsAligned(buffer)) {
+ set_error("Destination buffer must be aligned to FIDL_ALIGNMENT");
+ return ZX_ERR_INVALID_ARGS;
+ }
size_t primary_size;
zx_status_t status;
diff --git a/system/utest/fidl/decoding_tests.cpp b/system/utest/fidl/decoding_tests.cpp
index d474234..2f0afb0 100644
--- a/system/utest/fidl/decoding_tests.cpp
+++ b/system/utest/fidl/decoding_tests.cpp
@@ -137,6 +137,60 @@
END_TEST;
}
+bool decode_single_present_handle_unaligned_error() {
+ BEGIN_TEST;
+
+ // Test a short, unaligned version of nonnullable message
+ // handle. All fidl message objects should be 8 byte aligned.
+ struct unaligned_nonnullable_handle_inline_data {
+ fidl_message_header_t header;
+ zx_handle_t handle;
+ };
+ struct unaligned_nonnullable_handle_message_layout {
+ unaligned_nonnullable_handle_inline_data inline_struct;
+ };
+
+ unaligned_nonnullable_handle_message_layout message = {};
+ message.inline_struct.handle = FIDL_HANDLE_PRESENT;
+
+ zx_handle_t handles[] = {
+ dummy_handle_0,
+ };
+
+ // Decoding the unaligned version of the struct should fail.
+ const char* error = nullptr;
+ auto status = fidl_decode(&nonnullable_handle_message_type, &message, sizeof(message), handles,
+ ArrayCount(handles), &error);
+
+ EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
+ EXPECT_NONNULL(error);
+
+ END_TEST;
+}
+
+bool decode_present_nonnullable_string_unaligned_error() {
+ BEGIN_TEST;
+
+ unbounded_nonnullable_string_message_layout message = {};
+ message.inline_struct.string = fidl_string_t{6, reinterpret_cast<char*>(FIDL_ALLOC_PRESENT)};
+ memcpy(message.data, "hello!", 6);
+
+ // Copy the message to unaligned storage one byte off from true alignment
+ unbounded_nonnullable_string_message_layout message_storage[2];
+ uint8_t* unaligned_ptr = reinterpret_cast<uint8_t*>(&message_storage[0]) + 1;
+ memcpy(unaligned_ptr, &message, sizeof(message));
+
+ const char* error = nullptr;
+ auto status = fidl_decode(&unbounded_nonnullable_string_message_type, unaligned_ptr,
+ sizeof(message), nullptr, 0, &error);
+
+ EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
+ EXPECT_NONNULL(error);
+ ASSERT_STR_STR(error, "must be aligned to FIDL_ALIGNMENT");
+
+ END_TEST;
+}
+
bool decode_single_present_handle() {
BEGIN_TEST;
@@ -180,37 +234,6 @@
END_TEST;
}
-bool decode_single_present_handle_unaligned_error() {
- BEGIN_TEST;
-
- // Test a short, unaligned version of nonnullable message
- // handle. All fidl message objects should be 8 byte aligned.
- struct unaligned_nonnullable_handle_inline_data {
- fidl_message_header_t header;
- zx_handle_t handle;
- };
- struct unaligned_nonnullable_handle_message_layout {
- unaligned_nonnullable_handle_inline_data inline_struct;
- };
-
- unaligned_nonnullable_handle_message_layout message = {};
- message.inline_struct.handle = FIDL_HANDLE_PRESENT;
-
- zx_handle_t handles[] = {
- dummy_handle_0,
- };
-
- // Decoding the unaligned version of the struct should fail.
- const char* error = nullptr;
- auto status = fidl_decode(&nonnullable_handle_message_type, &message, sizeof(message), handles,
- ArrayCount(handles), &error);
-
- EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
- EXPECT_NONNULL(error);
-
- END_TEST;
-}
-
bool decode_multiple_present_handles() {
BEGIN_TEST;
@@ -1839,10 +1862,14 @@
RUN_TEST(decode_null_decode_parameters)
END_TEST_CASE(null_parameters)
+BEGIN_TEST_CASE(unaligned)
+RUN_TEST(decode_single_present_handle_unaligned_error)
+RUN_TEST(decode_present_nonnullable_string_unaligned_error)
+END_TEST_CASE(unaligned)
+
BEGIN_TEST_CASE(handles)
RUN_TEST(decode_single_present_handle)
RUN_TEST(decode_too_many_handles_specified_error)
-RUN_TEST(decode_single_present_handle_unaligned_error)
RUN_TEST(decode_multiple_present_handles)
RUN_TEST(decode_single_absent_handle)
RUN_TEST(decode_multiple_absent_handles)
diff --git a/system/utest/fidl/encoding_tests.cpp b/system/utest/fidl/encoding_tests.cpp
index 3c15487..ac38e56 100644
--- a/system/utest/fidl/encoding_tests.cpp
+++ b/system/utest/fidl/encoding_tests.cpp
@@ -158,28 +158,6 @@
END_TEST;
}
-bool encode_single_present_handle() {
- BEGIN_TEST;
-
- nonnullable_handle_message_layout message = {};
- message.inline_struct.handle = dummy_handle_0;
-
- zx_handle_t handles[1] = {};
-
- const char* error = nullptr;
- uint32_t actual_handles = 0u;
- auto status = fidl_encode(&nonnullable_handle_message_type, &message, sizeof(message), handles,
- ArrayCount(handles), &actual_handles, &error);
-
- EXPECT_EQ(status, ZX_OK);
- EXPECT_NULL(error, error);
- EXPECT_EQ(actual_handles, 1u);
- EXPECT_EQ(handles[0], dummy_handle_0);
- EXPECT_EQ(message.inline_struct.handle, FIDL_HANDLE_PRESENT);
-
- END_TEST;
-}
-
bool encode_single_present_handle_unaligned_error() {
BEGIN_TEST;
@@ -210,6 +188,55 @@
END_TEST;
}
+bool encode_present_nonnullable_string_unaligned_error() {
+ BEGIN_TEST;
+
+ unbounded_nonnullable_string_message_layout message = {};
+ message.inline_struct.string = fidl_string_t{6, &message.data[0]};
+ memcpy(message.data, "hello!", 6);
+
+ // Copy the message to unaligned storage one byte off from true alignment
+ unbounded_nonnullable_string_message_layout message_storage[2];
+ uint8_t* unaligned_ptr = reinterpret_cast<uint8_t*>(&message_storage[0]) + 1;
+ memcpy(unaligned_ptr, &message, sizeof(message));
+ auto unaligned_msg = reinterpret_cast<unbounded_nonnullable_string_message_layout*>(
+ unaligned_ptr);
+ unaligned_msg->inline_struct.string.data = &unaligned_msg->data[0];
+
+ const char* error = nullptr;
+ uint32_t actual_handles = 0u;
+ auto status = fidl_encode(&unbounded_nonnullable_string_message_type, unaligned_msg,
+ sizeof(message), nullptr, 0, &actual_handles, &error);
+
+ EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
+ EXPECT_NONNULL(error);
+ ASSERT_STR_STR(error, "must be aligned to FIDL_ALIGNMENT");
+
+ END_TEST;
+}
+
+bool encode_single_present_handle() {
+ BEGIN_TEST;
+
+ nonnullable_handle_message_layout message = {};
+ message.inline_struct.handle = dummy_handle_0;
+
+ zx_handle_t handles[1] = {};
+
+ const char* error = nullptr;
+ uint32_t actual_handles = 0u;
+ auto status = fidl_encode(&nonnullable_handle_message_type, &message, sizeof(message), handles,
+ ArrayCount(handles), &actual_handles, &error);
+
+ EXPECT_EQ(status, ZX_OK);
+ EXPECT_NULL(error, error);
+ EXPECT_EQ(actual_handles, 1u);
+ EXPECT_EQ(handles[0], dummy_handle_0);
+ EXPECT_EQ(message.inline_struct.handle, FIDL_HANDLE_PRESENT);
+
+ END_TEST;
+}
+
bool encode_multiple_present_handles() {
BEGIN_TEST;
@@ -1741,9 +1768,13 @@
RUN_TEST(encode_null_encode_parameters)
END_TEST_CASE(null_parameters)
+BEGIN_TEST_CASE(unaligned)
+RUN_TEST(encode_single_present_handle_unaligned_error)
+RUN_TEST(encode_present_nonnullable_string_unaligned_error)
+END_TEST_CASE(unaligned)
+
BEGIN_TEST_CASE(handles)
RUN_TEST(encode_single_present_handle)
-RUN_TEST(encode_single_present_handle_unaligned_error)
RUN_TEST(encode_multiple_present_handles)
RUN_TEST(encode_single_absent_handle)
RUN_TEST(encode_multiple_absent_handles)
diff --git a/system/utest/fidl/linearizing_tests.cpp b/system/utest/fidl/linearizing_tests.cpp
index c2d865b..c5eee96 100644
--- a/system/utest/fidl/linearizing_tests.cpp
+++ b/system/utest/fidl/linearizing_tests.cpp
@@ -55,6 +55,34 @@
END_TEST;
}
+bool linearize_present_nonnullable_string_unaligned_error() {
+ BEGIN_TEST;
+
+ unbounded_nonnullable_string_inline_data message = {};
+ constexpr const char* kStr = "hello!";
+ constexpr size_t kLength = 6;
+
+ char some_other_string[kLength] = {0};
+ message.string = fidl_string_t{kLength, some_other_string};
+ memcpy(some_other_string, kStr, kLength);
+
+ const char* error = nullptr;
+ zx_status_t status;
+ uint32_t actual_num_bytes = 0;
+
+ // Pass in an unaligned storage
+ constexpr uint32_t buf_size = 32u + FIDL_ALIGN(kLength);
+ std::unique_ptr<uint8_t[]> buf(new uint8_t[buf_size * 2]);
+ uint8_t* unaligned_ptr = buf.get() + 1;
+ status = fidl_linearize(&unbounded_nonnullable_string_message_type,
+ &message, unaligned_ptr, buf_size, &actual_num_bytes, &error);
+ EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
+ EXPECT_NONNULL(error);
+ ASSERT_STR_STR(error, "must be aligned to FIDL_ALIGNMENT");
+
+ END_TEST;
+}
+
bool linearize_present_nonnullable_longer_string() {
BEGIN_TEST;
@@ -640,6 +668,10 @@
RUN_TEST(linearize_present_nonnullable_longer_string)
END_TEST_CASE(strings)
+BEGIN_TEST_CASE(unaligned)
+RUN_TEST(linearize_present_nonnullable_string_unaligned_error)
+END_TEST_CASE(unaligned)
+
BEGIN_TEST_CASE(vectors)
RUN_TEST(linearize_vector_of_uint32)
RUN_TEST(linearize_vector_of_string)