[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)