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
mojo / core / options_validation_unittest.cc [blame]
// 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