1 : // Copyright 2013 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/instrument/instrumenters/asan_instrumenter.h"
16 :
17 : #include "base/command_line.h"
18 : #include "gtest/gtest.h"
19 : #include "syzygy/core/unittest_util.h"
20 : #include "syzygy/pe/image_filter.h"
21 : #include "syzygy/pe/unittest_util.h"
22 :
23 : namespace instrument {
24 : namespace instrumenters {
25 :
26 : namespace {
27 :
28 : static wchar_t kGoodAllocationFilterFileEmpty[] =
29 : L"syzygy/instrument/test_data/allocation-filter-good-minimal.json";
30 : static wchar_t kGoodAllocationFilterFile[] =
31 : L"syzygy/instrument/test_data/allocation-filter-good-full.json";
32 :
33 : class TestAsanInstrumenter : public AsanInstrumenter {
34 : public:
35 : using AsanInstrumenter::af_transform_;
36 : using AsanInstrumenter::agent_dll_;
37 : using AsanInstrumenter::allocation_filter_config_file_path_;
38 : using AsanInstrumenter::allow_overwrite_;
39 : using AsanInstrumenter::asan_params_;
40 : using AsanInstrumenter::asan_rtl_options_;
41 : using AsanInstrumenter::debug_friendly_;
42 : using AsanInstrumenter::filter_path_;
43 : using AsanInstrumenter::hot_patching_;
44 : using AsanInstrumenter::input_image_path_;
45 : using AsanInstrumenter::input_pdb_path_;
46 : using AsanInstrumenter::instrumentation_rate_;
47 : using AsanInstrumenter::no_augment_pdb_;
48 : using AsanInstrumenter::no_strip_strings_;
49 : using AsanInstrumenter::output_image_path_;
50 : using AsanInstrumenter::output_pdb_path_;
51 : using AsanInstrumenter::remove_redundant_checks_;
52 : using AsanInstrumenter::use_interceptors_;
53 : using AsanInstrumenter::use_liveness_analysis_;
54 : using InstrumenterWithAgent::CreateRelinker;
55 : using AsanInstrumenter::InstrumentPrepare;
56 : using AsanInstrumenter::InstrumentImpl;
57 : };
58 :
59 : class AsanInstrumenterTest : public testing::PELibUnitTest {
60 : public:
61 : typedef testing::PELibUnitTest Super;
62 :
63 E : AsanInstrumenterTest()
64 : : cmd_line_(base::FilePath(L"instrument.exe")) {
65 E : }
66 :
67 E : void SetUp() override {
68 E : testing::Test::SetUp();
69 :
70 : // Several of the tests generate progress and (deliberate) error messages
71 : // that would otherwise clutter the unittest output.
72 E : logging::SetMinLogLevel(logging::LOG_FATAL);
73 :
74 : // Setup the IO streams.
75 E : CreateTemporaryDir(&temp_dir_);
76 E : stdin_path_ = temp_dir_.Append(L"NUL");
77 E : stdout_path_ = temp_dir_.Append(L"stdout.txt");
78 E : stderr_path_ = temp_dir_.Append(L"stderr.txt");
79 E : InitStreams(stdin_path_, stdout_path_, stderr_path_);
80 :
81 : // Initialize the (potential) input and output path values.
82 E : abs_input_image_path_ = testing::GetExeRelativePath(testing::kTestDllName);
83 E : input_image_path_ = testing::GetRelativePath(abs_input_image_path_);
84 E : abs_input_pdb_path_ = testing::GetExeRelativePath(testing::kTestDllPdbName);
85 E : input_pdb_path_ = testing::GetRelativePath(abs_input_pdb_path_);
86 E : output_image_path_ = temp_dir_.Append(input_image_path_.BaseName());
87 E : output_pdb_path_ = temp_dir_.Append(input_pdb_path_.BaseName());
88 E : test_dll_filter_path_ = temp_dir_.Append(L"test_dll_filter.json");
89 E : dummy_filter_path_ = temp_dir_.Append(L"dummy_filter.json");
90 E : }
91 :
92 E : void SetUpValidCommandLine() {
93 E : cmd_line_.AppendSwitchPath("input-image", input_image_path_);
94 E : cmd_line_.AppendSwitchPath("output-image", output_image_path_);
95 E : }
96 :
97 : protected:
98 E : void MakeFilters() {
99 : // Create a valid test_dll filter. Just so it's not empty we mark the NT
100 : // headers as non-instrumentable.
101 E : pe::ImageFilter filter;
102 E : ASSERT_TRUE(filter.Init(abs_input_image_path_));
103 : filter.filter.Mark(pe::ImageFilter::RelativeAddressFilter::Range(
104 E : core::RelativeAddress(0), 4096));
105 E : ASSERT_TRUE(filter.SaveToJSON(false, test_dll_filter_path_));
106 :
107 : // Muck up the time date stamp and create an invalid filter.
108 E : filter.signature.module_time_date_stamp ^= 0x0F00BA55;
109 E : ASSERT_TRUE(filter.SaveToJSON(true, dummy_filter_path_));
110 E : }
111 :
112 : base::FilePath temp_dir_;
113 :
114 : // @name The redirected streams paths.
115 : // @{
116 : base::FilePath stdin_path_;
117 : base::FilePath stdout_path_;
118 : base::FilePath stderr_path_;
119 : // @}
120 :
121 : // @name Command-line and parameters.
122 : // @{
123 : base::CommandLine cmd_line_;
124 : base::FilePath input_image_path_;
125 : base::FilePath input_pdb_path_;
126 : base::FilePath output_image_path_;
127 : base::FilePath output_pdb_path_;
128 : base::FilePath test_dll_filter_path_;
129 : base::FilePath dummy_filter_path_;
130 : // @}
131 :
132 : // @name Expected final values of input parameters.
133 : // @{
134 : base::FilePath abs_input_image_path_;
135 : base::FilePath abs_input_pdb_path_;
136 : // @}
137 :
138 : // The fake instrumenter we delegate to.
139 : TestAsanInstrumenter instrumenter_;
140 : };
141 :
142 : } // namespace
143 :
144 E : TEST_F(AsanInstrumenterTest, ParseMinimalAsan) {
145 E : SetUpValidCommandLine();
146 :
147 E : EXPECT_TRUE(instrumenter_.ParseCommandLine(&cmd_line_));
148 :
149 E : EXPECT_EQ(abs_input_image_path_, instrumenter_.input_image_path_);
150 E : EXPECT_EQ(output_image_path_, instrumenter_.output_image_path_);
151 : EXPECT_EQ(std::string(instrument::transforms::AsanTransform::kSyzyAsanDll),
152 E : instrumenter_.agent_dll_);
153 E : EXPECT_FALSE(instrumenter_.allow_overwrite_);
154 E : EXPECT_FALSE(instrumenter_.no_augment_pdb_);
155 E : EXPECT_FALSE(instrumenter_.no_strip_strings_);
156 E : EXPECT_FALSE(instrumenter_.debug_friendly_);
157 E : EXPECT_TRUE(instrumenter_.use_interceptors_);
158 E : EXPECT_TRUE(instrumenter_.use_liveness_analysis_);
159 E : EXPECT_TRUE(instrumenter_.remove_redundant_checks_);
160 E : EXPECT_EQ(1.0, instrumenter_.instrumentation_rate_);
161 E : EXPECT_FALSE(instrumenter_.asan_rtl_options_);
162 E : EXPECT_FALSE(instrumenter_.hot_patching_);
163 E : }
164 :
165 E : TEST_F(AsanInstrumenterTest, ParseFullAsan) {
166 E : SetUpValidCommandLine();
167 E : cmd_line_.AppendSwitchPath("filter", test_dll_filter_path_);
168 E : cmd_line_.AppendSwitchASCII("agent", "foo.dll");
169 E : cmd_line_.AppendSwitch("debug-friendly");
170 E : cmd_line_.AppendSwitch("hot-patching");
171 E : cmd_line_.AppendSwitchPath("input-pdb", input_pdb_path_);
172 E : cmd_line_.AppendSwitch("no-augment-pdb");
173 E : cmd_line_.AppendSwitch("no-interceptors");
174 E : cmd_line_.AppendSwitch("no-strip-strings");
175 E : cmd_line_.AppendSwitchPath("output-pdb", output_pdb_path_);
176 E : cmd_line_.AppendSwitch("overwrite");
177 E : cmd_line_.AppendSwitch("no-liveness-analysis");
178 E : cmd_line_.AppendSwitch("no-redundancy-analysis");
179 E : cmd_line_.AppendSwitchASCII("instrumentation-rate", "0.5");
180 : cmd_line_.AppendSwitchASCII("asan-rtl-options",
181 E : "\"--quarantine_size=1024 --quarantine_block_size=512 --ignored\"");
182 :
183 E : EXPECT_TRUE(instrumenter_.ParseCommandLine(&cmd_line_));
184 :
185 E : EXPECT_EQ(abs_input_image_path_, instrumenter_.input_image_path_);
186 E : EXPECT_EQ(output_image_path_, instrumenter_.output_image_path_);
187 E : EXPECT_EQ(abs_input_pdb_path_, instrumenter_.input_pdb_path_);
188 E : EXPECT_EQ(output_pdb_path_, instrumenter_.output_pdb_path_);
189 E : EXPECT_EQ(test_dll_filter_path_, instrumenter_.filter_path_);
190 E : EXPECT_EQ(std::string("foo.dll"), instrumenter_.agent_dll_);
191 E : EXPECT_TRUE(instrumenter_.allow_overwrite_);
192 E : EXPECT_TRUE(instrumenter_.no_augment_pdb_);
193 E : EXPECT_TRUE(instrumenter_.no_strip_strings_);
194 E : EXPECT_TRUE(instrumenter_.debug_friendly_);
195 E : EXPECT_FALSE(instrumenter_.use_interceptors_);
196 E : EXPECT_FALSE(instrumenter_.use_liveness_analysis_);
197 E : EXPECT_FALSE(instrumenter_.remove_redundant_checks_);
198 E : EXPECT_EQ(0.5, instrumenter_.instrumentation_rate_);
199 E : EXPECT_TRUE(instrumenter_.asan_rtl_options_);
200 E : EXPECT_TRUE(instrumenter_.hot_patching_);
201 :
202 : // We check that the requested RTL options were parsed, and that others are
203 : // left to their defaults. We don't check all the parameters as other
204 : // unittests check the behaviour of the parser.
205 E : EXPECT_EQ(1024u, instrumenter_.asan_params_.quarantine_size);
206 E : EXPECT_EQ(512u, instrumenter_.asan_params_.quarantine_block_size);
207 : EXPECT_EQ(common::kDefaultMaxNumFrames,
208 E : instrumenter_.asan_params_.max_num_frames);
209 E : }
210 :
211 E : TEST_F(AsanInstrumenterTest, InstrumentImpl) {
212 E : SetUpValidCommandLine();
213 :
214 E : EXPECT_TRUE(instrumenter_.ParseCommandLine(&cmd_line_));
215 E : EXPECT_TRUE(instrumenter_.InstrumentPrepare());
216 E : EXPECT_TRUE(instrumenter_.CreateRelinker());
217 E : EXPECT_TRUE(instrumenter_.InstrumentImpl());
218 E : }
219 :
220 E : TEST_F(AsanInstrumenterTest, FailsWithInvalidFilter) {
221 E : cmd_line_.AppendSwitchPath("input-image", input_image_path_);
222 E : cmd_line_.AppendSwitchPath("output-image", output_image_path_);
223 E : cmd_line_.AppendSwitchPath("filter", dummy_filter_path_);
224 :
225 : // We don't expect the relinker to be called at all, as before we get that far
226 : // the filter will be identified as being for the wrong module.
227 :
228 E : MakeFilters();
229 E : EXPECT_TRUE(instrumenter_.ParseCommandLine(&cmd_line_));
230 E : EXPECT_TRUE(instrumenter_.InstrumentPrepare());
231 E : EXPECT_TRUE(instrumenter_.CreateRelinker());
232 E : EXPECT_FALSE(instrumenter_.InstrumentImpl());
233 E : }
234 :
235 E : TEST_F(AsanInstrumenterTest, SucceedsWithValidFilter) {
236 E : cmd_line_.AppendSwitchPath("input-image", input_image_path_);
237 E : cmd_line_.AppendSwitchPath("output-image", output_image_path_);
238 E : cmd_line_.AppendSwitchPath("filter", test_dll_filter_path_);
239 :
240 E : MakeFilters();
241 E : EXPECT_TRUE(instrumenter_.ParseCommandLine(&cmd_line_));
242 E : EXPECT_TRUE(instrumenter_.InstrumentPrepare());
243 E : EXPECT_TRUE(instrumenter_.CreateRelinker());
244 E : EXPECT_TRUE(instrumenter_.InstrumentImpl());
245 E : }
246 :
247 E : TEST_F(AsanInstrumenterTest, FailsWithInvalidInstrumentationRate) {
248 E : cmd_line_.AppendSwitchPath("input-image", input_image_path_);
249 E : cmd_line_.AppendSwitchPath("output-image", output_image_path_);
250 E : cmd_line_.AppendSwitchASCII("instrumentation-rate", "forty.three");
251 :
252 E : EXPECT_FALSE(instrumenter_.ParseCommandLine(&cmd_line_));
253 E : }
254 :
255 E : TEST_F(AsanInstrumenterTest, FailsWithInvalidAsanRtlOptions) {
256 E : cmd_line_.AppendSwitchPath("input-image", input_image_path_);
257 E : cmd_line_.AppendSwitchPath("output-image", output_image_path_);
258 E : cmd_line_.AppendSwitchASCII("asan-rtl-options", "--quarantine_size=foobar");
259 :
260 E : EXPECT_FALSE(instrumenter_.ParseCommandLine(&cmd_line_));
261 E : }
262 :
263 E : TEST_F(AsanInstrumenterTest, AllocationFilterConfigFileEmpty) {
264 E : SetUpValidCommandLine();
265 : base::FilePath filter_file = testing::GetSrcRelativePath(
266 E : kGoodAllocationFilterFileEmpty);
267 E : cmd_line_.AppendSwitchPath("allocation-filter-config-file", filter_file);
268 E : EXPECT_TRUE(instrumenter_.ParseCommandLine(&cmd_line_));
269 : // No transform should be initialized for an empty filter file.
270 E : EXPECT_EQ(nullptr, instrumenter_.af_transform_.get());
271 E : }
272 :
273 E : TEST_F(AsanInstrumenterTest, AllocationFilterConfigFile) {
274 E : SetUpValidCommandLine();
275 : base::FilePath filter_file = testing::GetSrcRelativePath(
276 E : kGoodAllocationFilterFile);
277 E : cmd_line_.AppendSwitchPath("allocation-filter-config-file", filter_file);
278 E : EXPECT_TRUE(instrumenter_.ParseCommandLine(&cmd_line_));
279 E : EXPECT_NE(nullptr, instrumenter_.af_transform_.get());
280 E : }
281 :
282 E : TEST_F(AsanInstrumenterTest, AllocationFilterConfigFileNotSpecified) {
283 E : SetUpValidCommandLine();
284 E : EXPECT_TRUE(instrumenter_.ParseCommandLine(&cmd_line_));
285 E : EXPECT_TRUE(instrumenter_.allocation_filter_config_file_path_.empty());
286 E : EXPECT_EQ(nullptr, instrumenter_.af_transform_.get());
287 E : }
288 :
289 E : TEST_F(AsanInstrumenterTest, FailsWithAllocationFilterConfigFileInvalidPath) {
290 E : SetUpValidCommandLine();
291 E : base::FilePath invalid_path = temp_dir_.Append(L"path-does-not-exist.json");
292 E : cmd_line_.AppendSwitchPath("allocation-filter-config-file", invalid_path);
293 : // Should fail if the AllocationFilter configuration file path is invalid.
294 E : EXPECT_FALSE(instrumenter_.ParseCommandLine(&cmd_line_));
295 E : }
296 :
297 E : TEST_F(AsanInstrumenterTest, HotPatchingChangesDefaultAgentDll) {
298 E : SetUpValidCommandLine();
299 E : cmd_line_.AppendSwitch("hot-patching");
300 :
301 E : EXPECT_TRUE(instrumenter_.ParseCommandLine(&cmd_line_));
302 :
303 : EXPECT_EQ(std::string(instrument::transforms::AsanTransform::kSyzyAsanHpDll),
304 E : instrumenter_.agent_dll_);
305 E : }
306 :
307 : } // namespace instrumenters
308 : } // namespace instrument
|