summaryrefslogtreecommitdiff
path: root/mojo/core/options_validation_unittest.cc
blob: b4b02dc7351d526a061c18fcd8230f5db5fe5102 (plain)
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
// Copyright 2014 The Chromium Authors. All rights reserved.
// 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 "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;
  }
}

// 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(
      {
        UserOptionsReader<TestOptions> reader(
            reinterpret_cast<const TestOptions*>(1));
      },
      kMemoryCheckFailedRegex);
  // 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.
  EXPECT_DEATH_IF_SUPPORTED(
      {
        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);
      },
      kMemoryCheckFailedRegex);
}

}  // namespace
}  // namespace core
}  // namespace mojo