1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/core/options_validation.h"
#include <stddef.h>
#include <stdint.h>
#include "base/compiler_specific.h"
#include "mojo/public/c/system/macros.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace core {
namespace {
// Declare a test options struct just as we do in actual public headers.
using TestOptionsFlags = uint32_t;
static_assert(MOJO_ALIGNOF(int64_t) <= 8, "int64_t has weird alignment");
struct MOJO_ALIGNAS(8) TestOptions {
uint32_t struct_size;
TestOptionsFlags flags;
uint32_t member1;
uint32_t member2;
};
static_assert(sizeof(TestOptions) == 16, "TestOptions has wrong size");
const uint32_t kSizeOfTestOptions = static_cast<uint32_t>(sizeof(TestOptions));
TEST(OptionsValidationTest, Valid) {
{
const TestOptions kOptions = {kSizeOfTestOptions};
UserOptionsReader<TestOptions> reader(&kOptions);
EXPECT_TRUE(reader.is_valid());
EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, flags, reader));
EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member1, reader));
EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member2, reader));
}
{
const TestOptions kOptions = {static_cast<uint32_t>(
offsetof(TestOptions, struct_size) + sizeof(uint32_t))};
UserOptionsReader<TestOptions> reader(&kOptions);
EXPECT_TRUE(reader.is_valid());
EXPECT_FALSE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, flags, reader));
EXPECT_FALSE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member1, reader));
EXPECT_FALSE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member2, reader));
}
{
const TestOptions kOptions = {
static_cast<uint32_t>(offsetof(TestOptions, flags) + sizeof(uint32_t))};
UserOptionsReader<TestOptions> reader(&kOptions);
EXPECT_TRUE(reader.is_valid());
EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, flags, reader));
EXPECT_FALSE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member1, reader));
EXPECT_FALSE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member2, reader));
}
{
MOJO_ALIGNAS(8) char buf[sizeof(TestOptions) + 100] = {};
TestOptions* options = reinterpret_cast<TestOptions*>(buf);
options->struct_size = kSizeOfTestOptions + 1;
UserOptionsReader<TestOptions> reader(options);
EXPECT_TRUE(reader.is_valid());
EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, flags, reader));
EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member1, reader));
EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member2, reader));
}
{
MOJO_ALIGNAS(8) char buf[sizeof(TestOptions) + 100] = {};
TestOptions* options = reinterpret_cast<TestOptions*>(buf);
options->struct_size = kSizeOfTestOptions + 4;
UserOptionsReader<TestOptions> reader(options);
EXPECT_TRUE(reader.is_valid());
EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, flags, reader));
EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member1, reader));
EXPECT_TRUE(OPTIONS_STRUCT_HAS_MEMBER(TestOptions, member2, reader));
}
}
TEST(OptionsValidationTest, Invalid) {
// Size too small:
for (size_t i = 0; i < sizeof(uint32_t); i++) {
TestOptions options = {static_cast<uint32_t>(i)};
UserOptionsReader<TestOptions> reader(&options);
EXPECT_FALSE(reader.is_valid()) << i;
}
}
// Creating unaligned points is undefined in C++, so even manufacturing these
// situations would trip UBSan. Suppress the sanitizer in these tests, so it
// does not interfere with situation being tested.
NO_SANITIZE("undefined")
void TestUnalignedPointer1() {
UserOptionsReader<TestOptions> reader(
reinterpret_cast<const TestOptions*>(1));
}
NO_SANITIZE("undefined")
void TestUnalignedPointer2() {
// Note: The current implementation checks the size only after checking the
// alignment versus that required for the |uint32_t| size, so it won't die in
// the expected way if you pass, e.g., 4. So we have to manufacture a valid
// pointer at an offset of alignment 4.
uint32_t buffer[100] = {};
TestOptions* options = (reinterpret_cast<uintptr_t>(buffer) % 8 == 0)
? reinterpret_cast<TestOptions*>(&buffer[1])
: reinterpret_cast<TestOptions*>(&buffer[0]);
options->struct_size = static_cast<uint32_t>(sizeof(TestOptions));
UserOptionsReader<TestOptions> reader(options);
}
// These test invalid arguments that should cause death if we're being paranoid
// about checking arguments (which we would want to do if, e.g., we were in a
// true "kernel" situation, but we might not want to do otherwise for
// performance reasons). Probably blatant errors like passing in null pointers
// (for required pointer arguments) will still cause death, but perhaps not
// predictably.
TEST(OptionsValidationTest, InvalidDeath) {
#if defined(OFFICIAL_BUILD)
const char kMemoryCheckFailedRegex[] = "";
#else
const char kMemoryCheckFailedRegex[] = "Check failed";
#endif
// Null:
EXPECT_DEATH_IF_SUPPORTED(
{ UserOptionsReader<TestOptions> reader((nullptr)); },
kMemoryCheckFailedRegex);
// Unaligned:
EXPECT_DEATH_IF_SUPPORTED(TestUnalignedPointer1(), kMemoryCheckFailedRegex);
EXPECT_DEATH_IF_SUPPORTED(TestUnalignedPointer2(), kMemoryCheckFailedRegex);
}
} // namespace
} // namespace core
} // namespace mojo
|