1 : // Copyright 2012 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/agent/asan/asan_runtime.h"
16 :
17 : #include "base/bind.h"
18 : #include "base/command_line.h"
19 : #include "base/environment.h"
20 : #include "base/string_number_conversions.h"
21 : #include "base/utf_string_conversions.h"
22 : #include "base/memory/scoped_ptr.h"
23 : #include "gtest/gtest.h"
24 : #include "syzygy/agent/asan/asan_heap.h"
25 : #include "syzygy/agent/asan/asan_logger.h"
26 : #include "syzygy/agent/asan/unittest_util.h"
27 :
28 : namespace agent {
29 : namespace asan {
30 :
31 : namespace {
32 :
33 : using agent::asan::AsanErrorInfo;
34 : using agent::asan::HeapProxy;
35 :
36 : // A derived class to expose protected members for unit-testing.
37 : class TestAsanRuntime : public AsanRuntime {
38 : public:
39 : using AsanRuntime::AsanFlags;
40 : using AsanRuntime::kBottomFramesToSkip;
41 : using AsanRuntime::kCompressionReportingPeriod;
42 : using AsanRuntime::kExitOnFailure;
43 : using AsanRuntime::kIgnoredStackIds;
44 : using AsanRuntime::kQuarantineSize;
45 : using AsanRuntime::kTrailerPaddingSize;
46 : using AsanRuntime::PropagateFlagsValues;
47 : using AsanRuntime::flags;
48 : using AsanRuntime::set_flags;
49 : };
50 :
51 : class TestHeapProxy : public HeapProxy {
52 : public:
53 : using HeapProxy::kDefaultQuarantineMaxSize;
54 : };
55 :
56 : class AsanRuntimeTest : public testing::TestWithAsanLogger {
57 : public:
58 : typedef testing::TestWithAsanLogger Super;
59 :
60 : AsanRuntimeTest()
61 : : current_command_line_(CommandLine::NO_PROGRAM),
62 E : stack_cache_(&logger_) {
63 E : }
64 :
65 E : void SetUp() OVERRIDE {
66 E : Super::SetUp();
67 :
68 E : env_.reset(base::Environment::Create());
69 E : ASSERT_TRUE(env_.get() != NULL);
70 E : env_->UnSetVar(TestAsanRuntime::kSyzygyAsanOptionsEnvVar);
71 E : env_->UnSetVar(TestAsanRuntime::kSyzygyAsanCoinTossEnvVar);
72 :
73 : // Setup the "global" state.
74 E : StackCapture::Init();
75 E : StackCaptureCache::Init();
76 E : HeapProxy::Init(&stack_cache_);
77 E : }
78 :
79 E : void TearDown() OVERRIDE {
80 : // Clear the environment so other tests aren't affected.
81 E : env_->UnSetVar(TestAsanRuntime::kSyzygyAsanOptionsEnvVar);
82 E : env_->UnSetVar(TestAsanRuntime::kSyzygyAsanCoinTossEnvVar);
83 :
84 E : Super::TearDown();
85 E : }
86 :
87 : AsanLogger logger_;
88 : StackCaptureCache stack_cache_;
89 :
90 : // The test runtime instance.
91 : TestAsanRuntime asan_runtime_;
92 :
93 : // The value of the command-line that we want to test.
94 : CommandLine current_command_line_;
95 :
96 : // The process environment.
97 : scoped_ptr<base::Environment> env_;
98 : };
99 :
100 : bool callback_called = false;
101 :
102 : // A simple callback that change the value of a boolean to indicate that it has
103 : // been called.
104 E : void TestCallback(AsanErrorInfo* error_info) {
105 E : callback_called = true;
106 E : }
107 :
108 : } // namespace
109 :
110 E : TEST_F(AsanRuntimeTest, SetUpAndTearDown) {
111 : ASSERT_NO_FATAL_FAILURE(
112 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
113 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
114 E : }
115 :
116 E : TEST_F(AsanRuntimeTest, OnError) {
117 : ASSERT_NO_FATAL_FAILURE(
118 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
119 E : asan_runtime_.SetErrorCallBack(base::Bind(&TestCallback));
120 E : callback_called = false;
121 E : AsanErrorInfo bad_access_info = {};
122 E : RtlCaptureContext(&bad_access_info.context);
123 E : asan_runtime_.OnError(&bad_access_info);
124 E : ASSERT_TRUE(callback_called);
125 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
126 E : }
127 :
128 E : TEST_F(AsanRuntimeTest, SetDefaultQuarantineMaxSize) {
129 : // Initialize the flags with the original command line.
130 : ASSERT_NO_FATAL_FAILURE(
131 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
132 :
133 : // Double the max size of the quarantine.
134 : unsigned int quarantine_max_size =
135 E : HeapProxy::default_quarantine_max_size() * 2;
136 : // Increments the quarantine max size if it was set to 0.
137 E : if (quarantine_max_size == 0)
138 i : quarantine_max_size++;
139 E : DCHECK_GT(quarantine_max_size, 0U);
140 E : std::string quarantine_max_size_str = base::UintToString(quarantine_max_size);
141 : current_command_line_.AppendSwitchASCII(TestAsanRuntime::kQuarantineSize,
142 E : quarantine_max_size_str);
143 :
144 : // Tear down the runtime and restart it with a different command line.
145 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
146 : ASSERT_NO_FATAL_FAILURE(
147 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
148 :
149 : // Ensure that the quarantine max size has been modified.
150 E : EXPECT_EQ(quarantine_max_size, HeapProxy::default_quarantine_max_size());
151 :
152 : // Ensure that the heap proxies use the new quarantine max size.
153 E : HeapProxy heap_proxy;
154 E : ASSERT_TRUE(heap_proxy.Create(0, 0, 0));
155 E : EXPECT_EQ(quarantine_max_size, heap_proxy.quarantine_max_size());
156 E : ASSERT_TRUE(heap_proxy.Destroy());
157 E : }
158 :
159 E : TEST_F(AsanRuntimeTest, SetCompressionReportingPeriod) {
160 : ASSERT_EQ(StackCaptureCache::GetDefaultCompressionReportingPeriod(),
161 E : StackCaptureCache::compression_reporting_period());
162 :
163 : size_t new_period =
164 E : StackCaptureCache::GetDefaultCompressionReportingPeriod() + 1024;
165 E : std::string new_period_str = base::UintToString(new_period);
166 : current_command_line_.AppendSwitchASCII(
167 E : TestAsanRuntime::kCompressionReportingPeriod, new_period_str);
168 :
169 : ASSERT_NO_FATAL_FAILURE(
170 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
171 :
172 : // Ensure that the compression reporting period has been modified.
173 E : EXPECT_EQ(new_period, StackCaptureCache::compression_reporting_period());
174 E : }
175 :
176 E : TEST_F(AsanRuntimeTest, SetBottomFramesToSkip) {
177 E : size_t frames_to_skip = StackCapture::bottom_frames_to_skip() + 1;
178 E : std::string new_frames_to_skip_str = base::UintToString(frames_to_skip);
179 : current_command_line_.AppendSwitchASCII(
180 E : TestAsanRuntime::kBottomFramesToSkip, new_frames_to_skip_str);
181 :
182 : ASSERT_NO_FATAL_FAILURE(
183 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
184 :
185 E : EXPECT_EQ(frames_to_skip, StackCapture::bottom_frames_to_skip());
186 E : }
187 :
188 E : TEST_F(AsanRuntimeTest, SetTrailerPaddingSize) {
189 E : size_t trailer_padding_size = HeapProxy::trailer_padding_size() + 1;
190 : std::string new_trailer_padding_size_str =
191 E : base::UintToString(trailer_padding_size);
192 : current_command_line_.AppendSwitchASCII(
193 E : TestAsanRuntime::kTrailerPaddingSize, new_trailer_padding_size_str);
194 :
195 : ASSERT_NO_FATAL_FAILURE(
196 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
197 :
198 E : EXPECT_EQ(trailer_padding_size, HeapProxy::trailer_padding_size());
199 E : }
200 :
201 E : TEST_F(AsanRuntimeTest, SetExitOnFailure) {
202 E : current_command_line_.AppendSwitch(TestAsanRuntime::kExitOnFailure);
203 :
204 : ASSERT_NO_FATAL_FAILURE(
205 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
206 E : EXPECT_TRUE(asan_runtime_.flags()->exit_on_failure);
207 E : }
208 :
209 E : TEST_F(AsanRuntimeTest, ExitOnFailure) {
210 E : current_command_line_.AppendSwitch(TestAsanRuntime::kExitOnFailure);
211 :
212 : ASSERT_NO_FATAL_FAILURE(
213 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
214 :
215 E : EXPECT_TRUE(asan_runtime_.flags()->exit_on_failure);
216 E : AsanErrorInfo bad_access_info = {};
217 E : RtlCaptureContext(&bad_access_info.context);
218 :
219 : // We need to delete the files and directory created by this unittest because
220 : // the EXPECT_EXIT macro will clone the process and this new process will exit
221 : // after the call to OnError, without calling the destructor of this class
222 : // (who takes care of deleting the temporary files/directories).
223 E : DeleteTempFileAndDirectory();
224 : EXPECT_EXIT(asan_runtime_.OnError(&bad_access_info),
225 E : ::testing::ExitedWithCode(EXIT_FAILURE), "");
226 E : }
227 :
228 E : TEST_F(AsanRuntimeTest, IgnoredStackIds) {
229 E : std::string ignored_stack_ids = "0x1;0X7E577E57;0xCAFEBABE;0xffffffff";
230 : current_command_line_.AppendSwitchASCII(
231 E : TestAsanRuntime::kIgnoredStackIds, ignored_stack_ids);
232 :
233 : ASSERT_NO_FATAL_FAILURE(
234 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
235 :
236 : EXPECT_TRUE(asan_runtime_.flags()->ignored_stack_ids.find(0x1) !=
237 E : asan_runtime_.flags()->ignored_stack_ids.end());
238 : EXPECT_TRUE(asan_runtime_.flags()->ignored_stack_ids.find(0x7E577E57) !=
239 E : asan_runtime_.flags()->ignored_stack_ids.end());
240 : EXPECT_TRUE(asan_runtime_.flags()->ignored_stack_ids.find(0xCAFEBABE) !=
241 E : asan_runtime_.flags()->ignored_stack_ids.end());
242 : EXPECT_TRUE(asan_runtime_.flags()->ignored_stack_ids.find(0xFFFFFFFF) !=
243 E : asan_runtime_.flags()->ignored_stack_ids.end());
244 E : }
245 :
246 E : TEST_F(AsanRuntimeTest, SetFlags) {
247 : ASSERT_NO_FATAL_FAILURE(
248 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
249 :
250 E : TestAsanRuntime::AsanFlags flags;
251 E : flags.quarantine_size = HeapProxy::default_quarantine_max_size() - 1;
252 E : ASSERT_LT(0U, flags.quarantine_size);
253 : flags.reporting_period =
254 E : StackCaptureCache::GetDefaultCompressionReportingPeriod() - 1;
255 E : ASSERT_LT(0U, flags.reporting_period);
256 : flags.bottom_frames_to_skip =
257 E : StackCapture::bottom_frames_to_skip() + 1;
258 E : ASSERT_LT(0U, flags.bottom_frames_to_skip);
259 : flags.max_num_frames =
260 E : asan_runtime_.stack_cache()->max_num_frames() - 1;
261 E : ASSERT_LT(0U, flags.max_num_frames);
262 E : asan_runtime_.set_flags(&flags);
263 E : asan_runtime_.PropagateFlagsValues();
264 :
265 E : ASSERT_EQ(flags.quarantine_size, HeapProxy::default_quarantine_max_size());
266 : ASSERT_EQ(flags.reporting_period,
267 E : StackCaptureCache::compression_reporting_period());
268 E : ASSERT_EQ(flags.bottom_frames_to_skip, StackCapture::bottom_frames_to_skip());
269 : ASSERT_EQ(flags.max_num_frames,
270 E : asan_runtime_.stack_cache()->max_num_frames());
271 E : }
272 :
273 E : TEST_F(AsanRuntimeTest, NotOptedInWithNoCoinToss) {
274 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.SetUp(std::wstring()));
275 E : ASSERT_FALSE(asan_runtime_.flags()->opted_in);
276 E : }
277 :
278 E : TEST_F(AsanRuntimeTest, NotOptedInWithInvalidCoinToss) {
279 : env_->SetVar(TestAsanRuntime::kSyzygyAsanCoinTossEnvVar,
280 E : "This is not a numeric value.");
281 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.SetUp(std::wstring()));
282 E : ASSERT_FALSE(asan_runtime_.flags()->opted_in);
283 E : }
284 :
285 E : TEST_F(AsanRuntimeTest, OptedInWithCoinToss) {
286 E : env_->SetVar(TestAsanRuntime::kSyzygyAsanCoinTossEnvVar, "0xF");
287 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.SetUp(std::wstring()));
288 E : ASSERT_TRUE(asan_runtime_.flags()->opted_in);
289 : ASSERT_NE(TestHeapProxy::kDefaultQuarantineMaxSize,
290 E : asan_runtime_.flags()->quarantine_size);
291 E : ASSERT_NE(0u, asan_runtime_.flags()->trailer_padding_size);
292 E : }
293 :
294 : } // namespace asan
295 : } // namespace agent
|