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/unittest_util.h"
26 :
27 : namespace agent {
28 : namespace asan {
29 :
30 : namespace {
31 :
32 : using agent::asan::AsanErrorInfo;
33 : using agent::asan::HeapProxy;
34 :
35 : // A derived class to expose protected members for unit-testing.
36 : class TestAsanRuntime : public AsanRuntime {
37 : public:
38 : using AsanRuntime::AsanFlags;
39 : using AsanRuntime::kBottomFramesToSkip;
40 : using AsanRuntime::kCompressionReportingPeriod;
41 : using AsanRuntime::kExitOnFailure;
42 : using AsanRuntime::kIgnoredStackIds;
43 : using AsanRuntime::kQuarantineSize;
44 : using AsanRuntime::kSyzyAsanEnvVar;
45 : using AsanRuntime::PropagateFlagsValues;
46 : using AsanRuntime::flags;
47 : using AsanRuntime::set_flags;
48 : };
49 :
50 : class AsanRuntimeTest : public testing::TestWithAsanLogger {
51 : public:
52 E : AsanRuntimeTest() : current_command_line_(CommandLine::NO_PROGRAM) {
53 E : }
54 :
55 E : void SetUp() OVERRIDE {
56 E : testing::TestWithAsanLogger::SetUp();
57 E : scoped_ptr<base::Environment> env(base::Environment::Create());
58 E : ASSERT_TRUE(env.get() != NULL);
59 E : env->UnSetVar(TestAsanRuntime::kSyzyAsanEnvVar);
60 :
61 : // Setup the "global" state.
62 E : HeapProxy::Init();
63 E : StackCapture::Init();
64 E : StackCaptureCache::Init();
65 E : }
66 :
67 : // The test runtime instance.
68 : TestAsanRuntime asan_runtime_;
69 :
70 : // The value of the command-line that we want to test.
71 : CommandLine current_command_line_;
72 : };
73 :
74 : bool callback_called = false;
75 :
76 : // A simple callback that change the value of a boolean to indicate that it has
77 : // been called.
78 E : void TestCallback(CONTEXT* context, AsanErrorInfo* error_info) {
79 E : callback_called = true;
80 E : }
81 :
82 : } // namespace
83 :
84 E : TEST_F(AsanRuntimeTest, SetUpAndTearDown) {
85 : ASSERT_NO_FATAL_FAILURE(
86 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
87 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
88 E : }
89 :
90 E : TEST_F(AsanRuntimeTest, OnError) {
91 : ASSERT_NO_FATAL_FAILURE(
92 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
93 E : asan_runtime_.SetErrorCallBack(base::Bind(&TestCallback));
94 E : callback_called = false;
95 : CONTEXT context;
96 E : RtlCaptureContext(&context);
97 E : StackCapture stack;
98 E : stack.InitFromStack();
99 E : AsanErrorInfo bad_access_info = {};
100 E : asan_runtime_.OnError(&context, &bad_access_info);
101 E : ASSERT_TRUE(callback_called);
102 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
103 E : }
104 :
105 E : TEST_F(AsanRuntimeTest, SetDefaultQuarantineMaxSize) {
106 : // Initialize the flags with the original command line.
107 : ASSERT_NO_FATAL_FAILURE(
108 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
109 :
110 : // Double the max size of the quarantine.
111 : unsigned int quarantine_max_size =
112 E : HeapProxy::default_quarantine_max_size() * 2;
113 : // Increments the quarantine max size if it was set to 0.
114 E : if (quarantine_max_size == 0)
115 i : quarantine_max_size++;
116 E : DCHECK_GT(quarantine_max_size, 0U);
117 E : std::string quarantine_max_size_str = base::UintToString(quarantine_max_size);
118 : current_command_line_.AppendSwitchASCII(TestAsanRuntime::kQuarantineSize,
119 E : quarantine_max_size_str);
120 :
121 : // Tear down the runtime and restart it with a different command line.
122 E : ASSERT_NO_FATAL_FAILURE(asan_runtime_.TearDown());
123 : ASSERT_NO_FATAL_FAILURE(
124 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
125 :
126 : // Ensure that the quarantine max size has been modified.
127 E : EXPECT_EQ(quarantine_max_size, HeapProxy::default_quarantine_max_size());
128 :
129 : // Ensure that the heap proxies use the new quarantine max size.
130 E : HeapProxy heap_proxy(asan_runtime_.stack_cache(), asan_runtime_.logger());
131 E : ASSERT_TRUE(heap_proxy.Create(0, 0, 0));
132 E : EXPECT_EQ(quarantine_max_size, heap_proxy.quarantine_max_size());
133 E : ASSERT_TRUE(heap_proxy.Destroy());
134 E : }
135 :
136 E : TEST_F(AsanRuntimeTest, SetCompressionReportingPeriod) {
137 : ASSERT_EQ(StackCaptureCache::GetDefaultCompressionReportingPeriod(),
138 E : StackCaptureCache::compression_reporting_period());
139 :
140 : size_t new_period =
141 E : StackCaptureCache::GetDefaultCompressionReportingPeriod() + 1024;
142 E : std::string new_period_str = base::UintToString(new_period);
143 : current_command_line_.AppendSwitchASCII(
144 E : TestAsanRuntime::kCompressionReportingPeriod, new_period_str);
145 :
146 : ASSERT_NO_FATAL_FAILURE(
147 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
148 :
149 : // Ensure that the compression reporting period has been modified.
150 E : EXPECT_EQ(new_period, StackCaptureCache::compression_reporting_period());
151 E : }
152 :
153 E : TEST_F(AsanRuntimeTest, SetBottomFramesToSkip) {
154 E : size_t frames_to_skip = StackCapture::bottom_frames_to_skip() + 1;
155 E : std::string new_frames_to_skip_str = base::UintToString(frames_to_skip);
156 : current_command_line_.AppendSwitchASCII(
157 E : TestAsanRuntime::kBottomFramesToSkip, new_frames_to_skip_str);
158 :
159 : ASSERT_NO_FATAL_FAILURE(
160 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
161 :
162 E : EXPECT_EQ(frames_to_skip, StackCapture::bottom_frames_to_skip());
163 E : }
164 :
165 E : TEST_F(AsanRuntimeTest, SetExitOnFailure) {
166 E : current_command_line_.AppendSwitch(TestAsanRuntime::kExitOnFailure);
167 :
168 : ASSERT_NO_FATAL_FAILURE(
169 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
170 E : EXPECT_TRUE(asan_runtime_.flags()->exit_on_failure);
171 E : }
172 :
173 E : TEST_F(AsanRuntimeTest, ExitOnFailure) {
174 E : current_command_line_.AppendSwitch(TestAsanRuntime::kExitOnFailure);
175 :
176 : ASSERT_NO_FATAL_FAILURE(
177 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
178 :
179 E : EXPECT_TRUE(asan_runtime_.flags()->exit_on_failure);
180 E : CONTEXT context = {};
181 E : RtlCaptureContext(&context);
182 :
183 E : AsanErrorInfo bad_access_info = {};
184 : // We need to delete the files and directory created by this unittest because
185 : // the EXPECT_EXIT macro will clone the process and this new process will exit
186 : // after the call to OnError, without calling the destructor of this class
187 : // (who takes care of deleting the temporary files/directories).
188 E : DeleteTempFileAndDirectory();
189 : EXPECT_EXIT(asan_runtime_.OnError(&context, &bad_access_info),
190 E : ::testing::ExitedWithCode(EXIT_FAILURE), "");
191 E : }
192 :
193 E : TEST_F(AsanRuntimeTest, IgnoredStackIds) {
194 E : std::string ignored_stack_ids = "0x1;0X7E577E57;0xCAFEBABE;0xffffffff";
195 : current_command_line_.AppendSwitchASCII(
196 E : TestAsanRuntime::kIgnoredStackIds, ignored_stack_ids);
197 :
198 : ASSERT_NO_FATAL_FAILURE(
199 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
200 :
201 : EXPECT_TRUE(asan_runtime_.flags()->ignored_stack_ids.find(0x1) !=
202 E : asan_runtime_.flags()->ignored_stack_ids.end());
203 : EXPECT_TRUE(asan_runtime_.flags()->ignored_stack_ids.find(0x7E577E57) !=
204 E : asan_runtime_.flags()->ignored_stack_ids.end());
205 : EXPECT_TRUE(asan_runtime_.flags()->ignored_stack_ids.find(0xCAFEBABE) !=
206 E : asan_runtime_.flags()->ignored_stack_ids.end());
207 : EXPECT_TRUE(asan_runtime_.flags()->ignored_stack_ids.find(0xFFFFFFFF) !=
208 E : asan_runtime_.flags()->ignored_stack_ids.end());
209 E : }
210 :
211 E : TEST_F(AsanRuntimeTest, SetFlags) {
212 : ASSERT_NO_FATAL_FAILURE(
213 E : asan_runtime_.SetUp(current_command_line_.GetCommandLineString()));
214 :
215 E : TestAsanRuntime::AsanFlags flags;
216 E : flags.quarantine_size = HeapProxy::default_quarantine_max_size() - 1;
217 E : ASSERT_LT(0U, flags.quarantine_size);
218 : flags.reporting_period =
219 E : StackCaptureCache::GetDefaultCompressionReportingPeriod() - 1;
220 E : ASSERT_LT(0U, flags.reporting_period);
221 : flags.bottom_frames_to_skip =
222 E : StackCapture::bottom_frames_to_skip() + 1;
223 E : ASSERT_LT(0U, flags.bottom_frames_to_skip);
224 : flags.max_num_frames =
225 E : asan_runtime_.stack_cache()->max_num_frames() - 1;
226 E : ASSERT_LT(0U, flags.max_num_frames);
227 E : asan_runtime_.set_flags(&flags);
228 E : asan_runtime_.PropagateFlagsValues();
229 :
230 E : ASSERT_EQ(flags.quarantine_size, HeapProxy::default_quarantine_max_size());
231 : ASSERT_EQ(flags.reporting_period,
232 E : StackCaptureCache::compression_reporting_period());
233 E : ASSERT_EQ(flags.bottom_frames_to_skip, StackCapture::bottom_frames_to_skip());
234 : ASSERT_EQ(flags.max_num_frames,
235 E : asan_runtime_.stack_cache()->max_num_frames());
236 E : }
237 :
238 : } // namespace asan
239 : } // namespace agent
|