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/runtime.h"
16 :
17 : #include <map>
18 :
19 : #include "base/bind.h"
20 : #include "base/bits.h"
21 : #include "base/command_line.h"
22 : #include "base/environment.h"
23 : #include "base/memory/scoped_ptr.h"
24 : #include "base/strings/string_number_conversions.h"
25 : #include "base/strings/utf_string_conversions.h"
26 : #include "gtest/gtest.h"
27 : #include "syzygy/agent/asan/unittest_util.h"
28 :
29 : namespace agent {
30 : namespace asan {
31 :
32 : namespace {
33 :
34 : using agent::asan::AsanErrorInfo;
35 :
36 : // Derived classes to expose protected members for unit-testing.
37 : class TestBlockHeapManager : public heap_managers::BlockHeapManager {
38 : public:
39 : using heap_managers::BlockHeapManager::enable_page_protections_;
40 : };
41 :
42 : class TestAsanRuntime : public AsanRuntime {
43 : public:
44 : using AsanRuntime::GenerateRandomFeatureSet;
45 : using AsanRuntime::PropagateParams;
46 : using AsanRuntime::enable_kasko_;
47 : using AsanRuntime::enabled_features_;
48 : using AsanRuntime::heap_manager_;
49 : };
50 :
51 : class AsanRuntimeTest : public testing::TestWithAsanLogger {
52 : public:
53 : typedef testing::TestWithAsanLogger Super;
54 :
55 E : AsanRuntimeTest() : current_command_line_(base::CommandLine::NO_PROGRAM) {}
56 :
57 E : void SetUp() override {
58 E : Super::SetUp();
59 :
60 E : env_.reset(base::Environment::Create());
61 E : ASSERT_TRUE(env_.get() != NULL);
62 E : env_->UnSetVar(::common::kSyzyAsanOptionsEnvVar);
63 :
64 : // Setup the "global" state.
65 E : common::StackCapture::Init();
66 E : StackCaptureCache::Init();
67 E : }
68 :
69 E : void TearDown() override {
70 : // Clear the environment so other tests aren't affected.
71 E : env_->UnSetVar(::common::kSyzyAsanOptionsEnvVar);
72 :
73 E : Super::TearDown();
74 E : }
75 :
76 : // The test runtime instance.
77 : TestAsanRuntime asan_runtime_;
78 :
79 : // The value of the command-line that we want to test.
80 : base::CommandLine current_command_line_;
81 :
82 : // The process environment.
83 : scoped_ptr<base::Environment> env_;
84 : };
85 :
86 : bool callback_called = false;
87 : AsanErrorInfo callback_error_info = {};
88 :
89 : // A simple callback that change the value of a boolean to indicate that it has
90 : // been called.
91 E : void TestCallback(AsanErrorInfo* error_info) {
92 E : callback_called = true;
93 E : callback_error_info = *error_info;
94 E : }
95 :
96 : } // namespace
97 :
98 E : TEST_F(AsanRuntimeTest, SetUpAndTearDown) {
99 : ASSERT_NO_FATAL_FAILURE(
100 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
101 : // Make sure the singleton pointer matches the runtime we created.
102 : ASSERT_EQ(reinterpret_cast<AsanRuntime*>(&asan_runtime_),
103 E : AsanRuntime::runtime());
104 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
105 E : }
106 :
107 E : TEST_F(AsanRuntimeTest, ThreadIdCache) {
108 : ASSERT_NO_FATAL_FAILURE(
109 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
110 :
111 E : EXPECT_FALSE(asan_runtime_.ThreadIdIsValid(1234));
112 E : EXPECT_FALSE(asan_runtime_.ThreadIdIsValid(5678));
113 E : asan_runtime_.AddThreadId(1234);
114 E : EXPECT_TRUE(asan_runtime_.ThreadIdIsValid(1234));
115 E : EXPECT_FALSE(asan_runtime_.ThreadIdIsValid(5678));
116 E : asan_runtime_.AddThreadId(5678);
117 E : EXPECT_TRUE(asan_runtime_.ThreadIdIsValid(1234));
118 E : EXPECT_TRUE(asan_runtime_.ThreadIdIsValid(5678));
119 :
120 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
121 E : }
122 :
123 E : TEST_F(AsanRuntimeTest, OnError) {
124 : ASSERT_NO_FATAL_FAILURE(
125 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
126 :
127 : // Disable the heap checking as this really slows down the unittests.
128 E : asan_runtime_.params().check_heap_on_failure = false;
129 E : asan_runtime_.SetErrorCallBack(base::Bind(&TestCallback));
130 E : callback_called = false;
131 E : AsanErrorInfo bad_access_info = {};
132 E : RtlCaptureContext(&bad_access_info.context);
133 E : asan_runtime_.OnError(&bad_access_info);
134 E : ASSERT_TRUE(callback_called);
135 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
136 E : ::common::AsanParameters params = asan_runtime_.params();
137 : EXPECT_EQ(0u, ::memcmp(¶ms, &(callback_error_info.asan_parameters),
138 E : sizeof(::common::AsanParameters)));
139 E : }
140 :
141 E : TEST_F(AsanRuntimeTest, SetCompressionReportingPeriod) {
142 : ASSERT_EQ(StackCaptureCache::GetDefaultCompressionReportingPeriod(),
143 E : StackCaptureCache::compression_reporting_period());
144 :
145 : size_t new_period =
146 E : StackCaptureCache::GetDefaultCompressionReportingPeriod() + 1024;
147 E : std::string new_period_str = base::UintToString(new_period);
148 : current_command_line_.AppendSwitchASCII(
149 E : ::common::kParamReportingPeriod, new_period_str);
150 :
151 : ASSERT_NO_FATAL_FAILURE(
152 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
153 : // Ensure that the compression reporting period has been modified.
154 E : EXPECT_EQ(new_period, StackCaptureCache::compression_reporting_period());
155 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
156 E : }
157 :
158 E : TEST_F(AsanRuntimeTest, SetBottomFramesToSkip) {
159 E : size_t frames_to_skip = common::StackCapture::bottom_frames_to_skip() + 1;
160 E : std::string new_frames_to_skip_str = base::UintToString(frames_to_skip);
161 : current_command_line_.AppendSwitchASCII(
162 E : ::common::kParamBottomFramesToSkip, new_frames_to_skip_str);
163 :
164 : ASSERT_NO_FATAL_FAILURE(
165 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
166 E : EXPECT_EQ(frames_to_skip, common::StackCapture::bottom_frames_to_skip());
167 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
168 E : }
169 :
170 E : TEST_F(AsanRuntimeTest, SetDisableBreakpad) {
171 E : current_command_line_.AppendSwitch(::common::kParamDisableBreakpadReporting);
172 :
173 : ASSERT_NO_FATAL_FAILURE(
174 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
175 E : EXPECT_TRUE(asan_runtime_.params().disable_breakpad_reporting);
176 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
177 E : }
178 :
179 E : TEST_F(AsanRuntimeTest, SetExitOnFailure) {
180 E : current_command_line_.AppendSwitch(::common::kParamExitOnFailure);
181 :
182 : ASSERT_NO_FATAL_FAILURE(
183 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
184 E : EXPECT_TRUE(asan_runtime_.params().exit_on_failure);
185 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
186 E : }
187 :
188 E : TEST_F(AsanRuntimeTest, ExitOnFailure) {
189 : // This test always fails under a debugger, due to strangeness in how
190 : // gtest death tests work.
191 E : if (base::debug::BeingDebugged()) {
192 i : LOG(WARNING) << "Skipping this test under debugger.";
193 i : return;
194 : }
195 :
196 E : current_command_line_.AppendSwitch(::common::kParamExitOnFailure);
197 :
198 : ASSERT_NO_FATAL_FAILURE(
199 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
200 :
201 E : EXPECT_TRUE(asan_runtime_.params().exit_on_failure);
202 E : AsanErrorInfo bad_access_info = {};
203 E : RtlCaptureContext(&bad_access_info.context);
204 :
205 : // We need to delete the files and directory created by this unittest because
206 : // the EXPECT_EXIT macro will clone the process and this new process will exit
207 : // after the call to OnError, without calling the destructor of this class
208 : // (who takes care of deleting the temporary files/directories).
209 E : DeleteTempFileAndDirectory();
210 :
211 : // Disable the heap checking as this really slows down the unittests.
212 E : asan_runtime_.params().check_heap_on_failure = false;
213 : EXPECT_EXIT(asan_runtime_.OnError(&bad_access_info),
214 E : ::testing::ExitedWithCode(EXIT_FAILURE), "");
215 :
216 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
217 E : }
218 :
219 E : TEST_F(AsanRuntimeTest, IgnoredStackIds) {
220 E : std::string ignored_stack_ids = "0x1;0X7E577E57;0xCAFEBABE;0xffffffff";
221 : current_command_line_.AppendSwitchASCII(
222 E : ::common::kParamIgnoredStackIds, ignored_stack_ids);
223 :
224 : ASSERT_NO_FATAL_FAILURE(
225 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
226 :
227 : EXPECT_THAT(asan_runtime_.params().ignored_stack_ids_set,
228 E : testing::ElementsAre(0x1, 0x7E577E57, 0xCAFEBABE, 0xFFFFFFFF));
229 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
230 E : }
231 :
232 E : TEST_F(AsanRuntimeTest, HeapIdIsValid) {
233 : ASSERT_NO_FATAL_FAILURE(
234 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
235 :
236 E : EXPECT_FALSE(asan_runtime_.HeapIdIsValid(0xDEADBEEF));
237 E : EXPECT_TRUE(asan_runtime_.HeapIdIsValid(asan_runtime_.GetProcessHeap()));
238 :
239 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
240 E : }
241 :
242 E : TEST_F(AsanRuntimeTest, GetHeapType) {
243 : ASSERT_NO_FATAL_FAILURE(
244 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
245 :
246 E : HeapManagerInterface::HeapId heap_id = asan_runtime_.GetProcessHeap();
247 E : EXPECT_EQ(kWinHeap, asan_runtime_.GetHeapType(heap_id));
248 :
249 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
250 E : }
251 :
252 E : TEST_F(AsanRuntimeTest, GenerateRandomFeatureSet) {
253 : ASSERT_NO_FATAL_FAILURE(
254 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
255 :
256 E : const size_t kIterations = 10000;
257 :
258 E : std::map<size_t, size_t> feature_group_frequency;
259 : const size_t kFeatureMaskBitCount =
260 E : static_cast<size_t>(base::bits::Log2Floor(ASAN_FEATURE_MAX));
261 E : std::vector<size_t> feature_activation_count(kFeatureMaskBitCount, 0);
262 :
263 E : for (size_t i = 0; i < kIterations; ++i) {
264 E : AsanFeatureSet feature_group = asan_runtime_.GenerateRandomFeatureSet();
265 E : ASSERT_LT(feature_group, ASAN_FEATURE_MAX);
266 : if (feature_group_frequency.find(feature_group) !=
267 E : feature_group_frequency.end()) {
268 E : feature_group_frequency[feature_group]++;
269 E : } else {
270 E : feature_group_frequency[feature_group] = 0;
271 : }
272 : TestBlockHeapManager* test_block_heap_manager =
273 E : static_cast<TestBlockHeapManager*>(asan_runtime_.heap_manager_.get());
274 E : if (test_block_heap_manager->enable_page_protections_) {
275 : feature_activation_count[base::bits::Log2Floor(
276 E : ASAN_FEATURE_ENABLE_PAGE_PROTECTIONS)]++;
277 : }
278 E : if (asan_runtime_.params().enable_large_block_heap) {
279 : feature_activation_count[base::bits::Log2Floor(
280 E : ASAN_FEATURE_ENABLE_LARGE_BLOCK_HEAP)]++;
281 : }
282 E : if (asan_runtime_.enable_kasko_) {
283 : feature_activation_count[base::bits::Log2Floor(
284 E : ASAN_FEATURE_ENABLE_KASKO)]++;
285 : }
286 E : }
287 :
288 E : size_t number_of_disabled_features = 0;
289 E : for (size_t i = 0; i < sizeof(kAsanDisabledFeatureMask); ++i) {
290 E : if ((kAsanDisabledFeatureMask & (1 << i)) == 0)
291 E : number_of_disabled_features++;
292 E : }
293 :
294 : // This could theoretically fail, but that would imply an extremely bad
295 : // implementation of the underlying random number generator. We expect a
296 : // standard deviation of 1 / 8 * sqrt(10000 * 7) = 33. A 10% margin is
297 : // 1000 / 33 = 30 standard deviations. For |z| > 30, the p-value is < 0.00001
298 : // and can be considered as insignificant.
299 : const size_t kExpectedCount =
300 E : kIterations / (ASAN_FEATURE_MAX >> number_of_disabled_features);
301 E : const size_t kErrorMargin = kExpectedCount / 10;
302 E : for (const auto& iter : feature_group_frequency) {
303 E : EXPECT_LT(kExpectedCount - kErrorMargin, iter.second);
304 E : EXPECT_GT(kExpectedCount + kErrorMargin, iter.second);
305 E : }
306 :
307 : // Every feature has a 50% chance of being turned on, so we expect a standard
308 : // deviation of sqrt(10000 * 0.5 * (1 - 0.5)) = 50. So with a 10% margin we're
309 : // at 20 standard deviations. For |z| > 30, the p-value is < 0.0001 and can
310 : // be considered as insignificant.
311 E : const size_t kExpectedFeatureFrequency = kIterations / 2;
312 : const size_t kExpectedFeatureFrequencyErrorMargin =
313 E : kExpectedFeatureFrequency / 10;
314 E : for (size_t i = 0; i < feature_activation_count.size(); ++i) {
315 E : if ((kAsanDisabledFeatureMask & (1 << i)) != 0) {
316 : EXPECT_LT(
317 : kExpectedFeatureFrequency - kExpectedFeatureFrequencyErrorMargin,
318 E : feature_activation_count[i]);
319 : EXPECT_GT(
320 : kExpectedFeatureFrequency + kExpectedFeatureFrequencyErrorMargin,
321 E : feature_activation_count[i]);
322 E : } else {
323 E : EXPECT_EQ(0U, feature_activation_count[i]);
324 : }
325 E : }
326 :
327 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
328 E : }
329 :
330 E : TEST_F(AsanRuntimeTest, OnErrorSaveEnabledFeatureList) {
331 E : asan_runtime_.params().feature_randomization = true;
332 : ASSERT_NO_FATAL_FAILURE(
333 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
334 :
335 : // Disable the heap checking as this really slows down the unittests.
336 E : asan_runtime_.params().check_heap_on_failure = false;
337 E : asan_runtime_.SetErrorCallBack(base::Bind(&TestCallback));
338 E : callback_called = false;
339 E : callback_error_info.feature_set = ASAN_FEATURE_MAX;
340 E : AsanErrorInfo bad_access_info = {};
341 E : RtlCaptureContext(&bad_access_info.context);
342 : AsanFeatureSet expected_feature_set = static_cast<AsanFeatureSet>(
343 E : ASAN_FEATURE_ENABLE_KASKO | ASAN_FEATURE_ENABLE_LARGE_BLOCK_HEAP);
344 E : asan_runtime_.enabled_features_ = expected_feature_set;
345 E : asan_runtime_.OnError(&bad_access_info);
346 E : EXPECT_TRUE(callback_called);
347 E : EXPECT_EQ(expected_feature_set, callback_error_info.feature_set);
348 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
349 E : }
350 :
351 : } // namespace asan
352 : } // namespace agent
|