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