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