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::input_image_path_;
44 : using AsanInstrumenter::input_pdb_path_;
45 : using AsanInstrumenter::instrumentation_rate_;
46 : using AsanInstrumenter::kAgentDllAsan;
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::InstrumentImpl;
56 :
57 E : TestAsanInstrumenter() {
58 : // Call the GetPERelinker function to initialize it.
59 E : EXPECT_TRUE(GetPERelinker() != NULL);
60 E : }
61 : };
62 :
63 : class AsanInstrumenterTest : public testing::PELibUnitTest {
64 : public:
65 : typedef testing::PELibUnitTest Super;
66 :
67 E : AsanInstrumenterTest()
68 : : cmd_line_(base::FilePath(L"instrument.exe")) {
69 E : }
70 :
71 E : virtual void SetUp() OVERRIDE {
72 E : testing::Test::SetUp();
73 :
74 : // Several of the tests generate progress and (deliberate) error messages
75 : // that would otherwise clutter the unittest output.
76 E : logging::SetMinLogLevel(logging::LOG_FATAL);
77 :
78 : // Setup the IO streams.
79 E : CreateTemporaryDir(&temp_dir_);
80 E : stdin_path_ = temp_dir_.Append(L"NUL");
81 E : stdout_path_ = temp_dir_.Append(L"stdout.txt");
82 E : stderr_path_ = temp_dir_.Append(L"stderr.txt");
83 E : InitStreams(stdin_path_, stdout_path_, stderr_path_);
84 :
85 : // Initialize the (potential) input and output path values.
86 E : abs_input_image_path_ = testing::GetExeRelativePath(testing::kTestDllName);
87 E : input_image_path_ = testing::GetRelativePath(abs_input_image_path_);
88 E : abs_input_pdb_path_ = testing::GetExeRelativePath(testing::kTestDllPdbName);
89 E : input_pdb_path_ = testing::GetRelativePath(abs_input_pdb_path_);
90 E : output_image_path_ = temp_dir_.Append(input_image_path_.BaseName());
91 E : output_pdb_path_ = temp_dir_.Append(input_pdb_path_.BaseName());
92 E : test_dll_filter_path_ = temp_dir_.Append(L"test_dll_filter.json");
93 E : dummy_filter_path_ = temp_dir_.Append(L"dummy_filter.json");
94 E : }
95 :
96 E : void SetUpValidCommandLine() {
97 E : cmd_line_.AppendSwitchPath("input-image", input_image_path_);
98 E : cmd_line_.AppendSwitchPath("output-image", output_image_path_);
99 E : }
100 :
101 : protected:
102 E : void MakeFilters() {
103 : // Create a valid test_dll filter. Just so it's not empty we mark the NT
104 : // headers as non-instrumentable.
105 E : pe::ImageFilter filter;
106 E : ASSERT_TRUE(filter.Init(abs_input_image_path_));
107 : filter.filter.Mark(pe::ImageFilter::RelativeAddressFilter::Range(
108 E : core::RelativeAddress(0), 4096));
109 E : ASSERT_TRUE(filter.SaveToJSON(false, test_dll_filter_path_));
110 :
111 : // Muck up the time date stamp and create an invalid filter.
112 E : filter.signature.module_time_date_stamp ^= 0x0F00BA55;
113 E : ASSERT_TRUE(filter.SaveToJSON(true, dummy_filter_path_));
114 E : }
115 :
116 : base::FilePath temp_dir_;
117 :
118 : // @name The redirected streams paths.
119 : // @{
120 : base::FilePath stdin_path_;
121 : base::FilePath stdout_path_;
122 : base::FilePath stderr_path_;
123 : // @}
124 :
125 : // @name Command-line and parameters.
126 : // @{
127 : CommandLine cmd_line_;
128 : base::FilePath input_image_path_;
129 : base::FilePath input_pdb_path_;
130 : base::FilePath output_image_path_;
131 : base::FilePath output_pdb_path_;
132 : base::FilePath test_dll_filter_path_;
133 : base::FilePath dummy_filter_path_;
134 : // @}
135 :
136 : // @name Expected final values of input parameters.
137 : // @{
138 : base::FilePath abs_input_image_path_;
139 : base::FilePath abs_input_pdb_path_;
140 : // @}
141 :
142 : // The fake instrumenter we delegate to.
143 : TestAsanInstrumenter instrumenter_;
144 : };
145 :
146 : } // namespace
147 :
148 E : TEST_F(AsanInstrumenterTest, ParseMinimalAsan) {
149 E : SetUpValidCommandLine();
150 :
151 E : EXPECT_TRUE(instrumenter_.ParseCommandLine(&cmd_line_));
152 :
153 E : EXPECT_EQ(abs_input_image_path_, instrumenter_.input_image_path_);
154 E : EXPECT_EQ(output_image_path_, instrumenter_.output_image_path_);
155 : EXPECT_EQ(std::string(TestAsanInstrumenter::kAgentDllAsan),
156 E : instrumenter_.agent_dll_);
157 E : EXPECT_FALSE(instrumenter_.allow_overwrite_);
158 E : EXPECT_FALSE(instrumenter_.no_augment_pdb_);
159 E : EXPECT_FALSE(instrumenter_.no_strip_strings_);
160 E : EXPECT_FALSE(instrumenter_.debug_friendly_);
161 E : EXPECT_TRUE(instrumenter_.use_interceptors_);
162 E : EXPECT_TRUE(instrumenter_.use_liveness_analysis_);
163 E : EXPECT_TRUE(instrumenter_.remove_redundant_checks_);
164 E : EXPECT_EQ(1.0, instrumenter_.instrumentation_rate_);
165 E : EXPECT_FALSE(instrumenter_.asan_rtl_options_);
166 E : }
167 :
168 E : TEST_F(AsanInstrumenterTest, ParseFullAsan) {
169 E : SetUpValidCommandLine();
170 E : cmd_line_.AppendSwitchPath("filter", test_dll_filter_path_);
171 E : cmd_line_.AppendSwitchASCII("agent", "foo.dll");
172 E : cmd_line_.AppendSwitch("debug-friendly");
173 E : cmd_line_.AppendSwitchPath("input-pdb", input_pdb_path_);
174 E : cmd_line_.AppendSwitch("no-augment-pdb");
175 E : cmd_line_.AppendSwitch("no-interceptors");
176 E : cmd_line_.AppendSwitch("no-strip-strings");
177 E : cmd_line_.AppendSwitchPath("output-pdb", output_pdb_path_);
178 E : cmd_line_.AppendSwitch("overwrite");
179 E : cmd_line_.AppendSwitch("no-liveness-analysis");
180 E : cmd_line_.AppendSwitch("no-redundancy-analysis");
181 E : cmd_line_.AppendSwitchASCII("instrumentation-rate", "0.5");
182 : cmd_line_.AppendSwitchASCII("asan-rtl-options",
183 E : "--quarantine_size=1024 --quarantine_block_size=512 --ignored");
184 :
185 E : EXPECT_TRUE(instrumenter_.ParseCommandLine(&cmd_line_));
186 :
187 E : EXPECT_EQ(abs_input_image_path_, instrumenter_.input_image_path_);
188 E : EXPECT_EQ(output_image_path_, instrumenter_.output_image_path_);
189 E : EXPECT_EQ(abs_input_pdb_path_, instrumenter_.input_pdb_path_);
190 E : EXPECT_EQ(output_pdb_path_, instrumenter_.output_pdb_path_);
191 E : EXPECT_EQ(test_dll_filter_path_, instrumenter_.filter_path_);
192 E : EXPECT_EQ(std::string("foo.dll"), instrumenter_.agent_dll_);
193 E : EXPECT_TRUE(instrumenter_.allow_overwrite_);
194 E : EXPECT_TRUE(instrumenter_.no_augment_pdb_);
195 E : EXPECT_TRUE(instrumenter_.no_strip_strings_);
196 E : EXPECT_TRUE(instrumenter_.debug_friendly_);
197 E : EXPECT_FALSE(instrumenter_.use_interceptors_);
198 E : EXPECT_FALSE(instrumenter_.use_liveness_analysis_);
199 E : EXPECT_FALSE(instrumenter_.remove_redundant_checks_);
200 E : EXPECT_EQ(0.5, instrumenter_.instrumentation_rate_);
201 E : EXPECT_TRUE(instrumenter_.asan_rtl_options_);
202 :
203 : // We check that the requested RTL options were parsed, and that others are
204 : // left to their defaults. We don't check all the parameters as other
205 : // unittests check the behaviour of the parser.
206 E : EXPECT_EQ(1024u, instrumenter_.asan_params_.quarantine_size);
207 E : EXPECT_EQ(512u, instrumenter_.asan_params_.quarantine_block_size);
208 : EXPECT_EQ(common::kDefaultMaxNumFrames,
209 E : instrumenter_.asan_params_.max_num_frames);
210 E : }
211 :
212 E : TEST_F(AsanInstrumenterTest, InstrumentImpl) {
213 E : SetUpValidCommandLine();
214 :
215 E : EXPECT_TRUE(instrumenter_.ParseCommandLine(&cmd_line_));
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_.CreateRelinker());
231 E : EXPECT_FALSE(instrumenter_.InstrumentImpl());
232 E : }
233 :
234 E : TEST_F(AsanInstrumenterTest, SucceedsWithValidFilter) {
235 E : cmd_line_.AppendSwitchPath("input-image", input_image_path_);
236 E : cmd_line_.AppendSwitchPath("output-image", output_image_path_);
237 E : cmd_line_.AppendSwitchPath("filter", test_dll_filter_path_);
238 :
239 E : MakeFilters();
240 E : EXPECT_TRUE(instrumenter_.ParseCommandLine(&cmd_line_));
241 E : EXPECT_TRUE(instrumenter_.CreateRelinker());
242 E : EXPECT_TRUE(instrumenter_.InstrumentImpl());
243 E : }
244 :
245 E : TEST_F(AsanInstrumenterTest, FailsWithInvalidInstrumentationRate) {
246 E : cmd_line_.AppendSwitchPath("input-image", input_image_path_);
247 E : cmd_line_.AppendSwitchPath("output-image", output_image_path_);
248 E : cmd_line_.AppendSwitchASCII("instrumentation-rate", "forty.three");
249 :
250 E : EXPECT_FALSE(instrumenter_.ParseCommandLine(&cmd_line_));
251 E : }
252 :
253 E : TEST_F(AsanInstrumenterTest, FailsWithInvalidAsanRtlOptions) {
254 E : cmd_line_.AppendSwitchPath("input-image", input_image_path_);
255 E : cmd_line_.AppendSwitchPath("output-image", output_image_path_);
256 E : cmd_line_.AppendSwitchASCII("asan-rtl-options", "--quarantine_size=foobar");
257 :
258 E : EXPECT_FALSE(instrumenter_.ParseCommandLine(&cmd_line_));
259 E : }
260 :
261 E : TEST_F(AsanInstrumenterTest, AllocationFilterConfigFileEmpty) {
262 E : SetUpValidCommandLine();
263 : base::FilePath filter_file = testing::GetSrcRelativePath(
264 E : kGoodAllocationFilterFileEmpty);
265 E : cmd_line_.AppendSwitchPath("allocation-filter-config-file", filter_file);
266 E : EXPECT_TRUE(instrumenter_.ParseCommandLine(&cmd_line_));
267 : // No transform should be initialized for an empty filter file.
268 E : EXPECT_EQ(nullptr, instrumenter_.af_transform_.get());
269 E : }
270 :
271 E : TEST_F(AsanInstrumenterTest, AllocationFilterConfigFile) {
272 E : SetUpValidCommandLine();
273 : base::FilePath filter_file = testing::GetSrcRelativePath(
274 E : kGoodAllocationFilterFile);
275 E : cmd_line_.AppendSwitchPath("allocation-filter-config-file", filter_file);
276 E : EXPECT_TRUE(instrumenter_.ParseCommandLine(&cmd_line_));
277 E : EXPECT_NE(nullptr, instrumenter_.af_transform_.get());
278 E : }
279 :
280 E : TEST_F(AsanInstrumenterTest, AllocationFilterConfigFileNotSpecified) {
281 E : SetUpValidCommandLine();
282 E : EXPECT_TRUE(instrumenter_.ParseCommandLine(&cmd_line_));
283 E : EXPECT_TRUE(instrumenter_.allocation_filter_config_file_path_.empty());
284 E : EXPECT_EQ(nullptr, instrumenter_.af_transform_.get());
285 E : }
286 :
287 E : TEST_F(AsanInstrumenterTest, FailsWithAllocationFilterConfigFileInvalidPath) {
288 E : SetUpValidCommandLine();
289 E : base::FilePath invalid_path = temp_dir_.Append(L"path-does-not-exist.json");
290 E : cmd_line_.AppendSwitchPath("allocation-filter-config-file", invalid_path);
291 : // Should fail if the AllocationFilter configuration file path is invalid.
292 E : EXPECT_FALSE(instrumenter_.ParseCommandLine(&cmd_line_));
293 E : }
294 :
295 : } // namespace instrumenters
296 : } // namespace instrument
|