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