forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathelf_imports_unittest.cc
178 lines (152 loc) · 6.58 KB
/
elf_imports_unittest.cc
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// 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 <stdint.h>
#include <windows.h>
#include <algorithm>
#include <vector>
#include "base/base_paths.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/files/memory_mapped_file.h"
#include "base/path_service.h"
#include "base/strings/pattern.h"
#include "base/strings/string_util.h"
#include "base/test/launcher/test_launcher.h"
#include "base/win/pe_image.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
class ELFImportsTest : public testing::Test {
protected:
static bool ImportsCallback(const base::win::PEImage &image,
LPCSTR module,
PIMAGE_THUNK_DATA name_table,
PIMAGE_THUNK_DATA iat,
PVOID cookie) {
std::vector<std::string>* import_list =
reinterpret_cast<std::vector<std::string>*>(cookie);
import_list->push_back(module);
return true;
}
void GetImports(const base::FilePath& module_path,
std::vector<std::string>* imports) {
ASSERT_TRUE(imports != NULL);
base::MemoryMappedFile module_mmap;
ASSERT_TRUE(module_mmap.Initialize(module_path));
base::win::PEImageAsData pe_image_data(
reinterpret_cast<HMODULE>(const_cast<uint8_t*>(module_mmap.data())));
pe_image_data.EnumImportChunks(ELFImportsTest::ImportsCallback, imports);
}
};
// Run this test only in Release builds.
//
// This test makes sure that chrome_elf.dll has only certain types of imports.
// However, it directly and indirectly depends on base, which has lots more
// imports than are allowed here.
//
// In release builds, the offending imports are all stripped since this
// depends on a relatively small portion of base. In GYP, this works in debug
// builds as well because static libraries are used for the sandbox and base
// targets and the files that use e.g. user32.dll happen to not get brought
// into the build in the first place (due to the way static libraries are
// linked where only the required .o files are included). But we don't bother
// differentiating GYP and GN builds for this purpose.
//
// If you break this test, you may have changed base or the Windows sandbox
// such that more system imports are required to link.
#if defined(NDEBUG) && !defined(COMPONENT_BUILD)
TEST_F(ELFImportsTest, ChromeElfSanityCheck) {
base::FilePath dll;
ASSERT_TRUE(PathService::Get(base::DIR_EXE, &dll));
dll = dll.Append(L"chrome_elf.dll");
std::vector<std::string> elf_imports;
GetImports(dll, &elf_imports);
// Check that ELF has imports.
ASSERT_LT(0u, elf_imports.size()) << "Ensure the chrome_elf_unittests "
"target was built, instead of chrome_elf_unittests.exe";
static const char* const kValidFilePatterns[] = {
"KERNEL32.dll",
"RPCRT4.dll",
#if defined(SYZYASAN)
"syzyasan_rtl.dll",
#endif
#if defined(ADDRESS_SANITIZER) && defined(COMPONENT_BUILD)
"clang_rt.asan_dynamic-i386.dll",
#endif
"ADVAPI32.dll",
// On 64 bit the Version API's like VerQueryValue come from VERSION.dll.
// It depends on kernel32, advapi32 and api-ms-win-crt*.dll. This should
// be ok.
"VERSION.dll",
};
// Make sure all of ELF's imports are in the valid imports list.
for (const std::string& import : elf_imports) {
bool match = false;
for (const char* kValidFilePattern : kValidFilePatterns) {
if (base::MatchPattern(import, kValidFilePattern)) {
match = true;
break;
}
}
ASSERT_TRUE(match) << "Illegal import in chrome_elf.dll: " << import;
}
}
TEST_F(ELFImportsTest, ChromeElfLoadSanityTest) {
// chrome_elf will try to launch crashpad_handler by reinvoking the current
// binary with --type=crashpad-handler if not already running that way. To
// avoid that, we relaunch and run the real test body manually, adding that
// command line argument, as we're only trying to confirm that user32.dll
// doesn't get loaded by import table when chrome_elf.dll does.
base::CommandLine new_test =
base::CommandLine(base::CommandLine::ForCurrentProcess()->GetProgram());
new_test.AppendSwitchASCII(
base::kGTestFilterFlag,
"ELFImportsTest.DISABLED_ChromeElfLoadSanityTestImpl");
new_test.AppendSwitchASCII("type", "crashpad-handler");
new_test.AppendSwitch("gtest_also_run_disabled_tests");
new_test.AppendSwitch("single-process-tests");
std::string output;
ASSERT_TRUE(base::GetAppOutput(new_test, &output));
std::string crash_string =
"OK ] ELFImportsTest.DISABLED_ChromeElfLoadSanityTestImpl";
if (output.find(crash_string) == std::string::npos) {
GTEST_FAIL() << "Couldn't find\n" << crash_string << "\n in output\n "
<< output;
}
}
// Note: This test is not actually disabled, it's just tagged disabled so that
// the real run (above, in ChromeElfLoadSanityTest) can run it with an argument
// added to the command line.
TEST_F(ELFImportsTest, DISABLED_ChromeElfLoadSanityTestImpl) {
base::FilePath dll;
ASSERT_TRUE(PathService::Get(base::DIR_EXE, &dll));
dll = dll.Append(L"chrome_elf.dll");
// We don't expect user32 to be loaded in chrome_elf_unittests. If this test
// case fails, then it means that a dependency on user32 has crept into the
// chrome_elf_unittests executable, which needs to be removed.
// NOTE: it may be a secondary dependency of another system DLL. If so,
// try adding a "/DELAYLOAD:<blah>.dll" to the build.gn file.
ASSERT_EQ(nullptr, ::GetModuleHandle(L"user32.dll"));
HMODULE chrome_elf_module_handle = ::LoadLibrary(dll.value().c_str());
EXPECT_TRUE(chrome_elf_module_handle != nullptr);
// Loading chrome_elf.dll should not load user32.dll
EXPECT_EQ(nullptr, ::GetModuleHandle(L"user32.dll"));
EXPECT_TRUE(!!::FreeLibrary(chrome_elf_module_handle));
}
#endif // NDEBUG && !COMPONENT_BUILD
TEST_F(ELFImportsTest, ChromeExeSanityCheck) {
std::vector<std::string> exe_imports;
base::FilePath exe;
ASSERT_TRUE(PathService::Get(base::DIR_EXE, &exe));
exe = exe.Append(L"chrome.exe");
GetImports(exe, &exe_imports);
// Check that chrome.exe has imports.
ASSERT_LT(0u, exe_imports.size()) << "Ensure the chrome_elf_unittests "
"target was built, instead of chrome_elf_unittests.exe";
// Chrome.exe's first import must be ELF.
EXPECT_EQ("chrome_elf.dll", exe_imports[0]) <<
"Illegal import order in chrome.exe (ensure the chrome_elf_unittest "
"target was built, instead of just chrome_elf_unittests.exe)";
}
} // namespace