1 : // Copyright 2014 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/common/asan_parameters.h"
16 :
17 : #include <windows.h>
18 :
19 : #include <algorithm>
20 :
21 : #include "base/command_line.h"
22 : #include "base/logging.h"
23 : #include "base/strings/string_number_conversions.h"
24 : #include "base/strings/string_tokenizer.h"
25 : #include "base/strings/string_util.h"
26 : #include "base/strings/stringprintf.h"
27 :
28 : namespace common {
29 :
30 : namespace {
31 :
32 : // Trinary return values used to indicate if a flag was updated or not.
33 : enum FlagResult {
34 : kFlagNotPresent,
35 : kFlagSet,
36 : kFlagError
37 : };
38 :
39 : // Templated utility class for parsing parameter values from a command-line.
40 : // @tparam Converter Defines the types and parsing.
41 : template<typename Parser>
42 : struct UpdateValueFromCommandLine {
43 : // Try to update the value of a variable from a command-line.
44 : // @param cmd_line The command line who might contain a given parameter.
45 : // @param param_name The parameter that we want to read.
46 : // @param value Will receive the value of the parameter if it's present.
47 : // @returns kFlagNotPresent if the flag was not present and left at its
48 : // default; kFlagSet if the flag was present, valid and modified; or
49 : // kFlagError if the flag was present but invalid. Logs an error on
50 : // failure, and an info message on successful parsing.
51 : static FlagResult Do(const CommandLine& cmd_line,
52 : const std::string& param_name,
53 E : typename Parser::ValueType* value) {
54 E : DCHECK_NE(reinterpret_cast<Parser::ValueType*>(NULL), value);
55 :
56 E : if (!cmd_line.HasSwitch(param_name))
57 E : return kFlagNotPresent;
58 :
59 E : std::string value_str = cmd_line.GetSwitchValueASCII(param_name);
60 E : Parser::IntermediateType new_value = 0;
61 E : if (!Parser::Convert(value_str, &new_value)) {
62 E : LOG(ERROR) << "Failed to parse \"" << param_name << "\" value of \""
63 : << value_str << "\".";
64 E : return kFlagError;
65 : }
66 :
67 E : *value = new_value;
68 E : VLOG(1) << "Set \"" << param_name << "\" to " << *value << ".";
69 E : return kFlagSet;
70 E : }
71 : };
72 :
73 : // Parses a uint32 value from a string provided on the command-line.
74 : struct Uint32Parser {
75 : typedef size_t IntermediateType;
76 : typedef uint32 ValueType;
77 E : static bool Convert(const std::string& value_str, IntermediateType* value) {
78 E : return base::StringToSizeT(value_str, value);
79 E : }
80 : };
81 : typedef UpdateValueFromCommandLine<Uint32Parser> UpdateUint32FromCommandLine;
82 :
83 : // Parses a float value from a string provided on the command-line.
84 : struct FloatParser {
85 : typedef double IntermediateType;
86 : typedef float ValueType;
87 E : static bool Convert(const std::string& value_str, IntermediateType* value) {
88 E : return base::StringToDouble(value_str, value);
89 E : }
90 : };
91 : typedef UpdateValueFromCommandLine<FloatParser> UpdateFloatFromCommandLine;
92 :
93 : // Try to update the value of an array of ignored stack ids from a command-line.
94 : // We expect the values to be in hexadecimal format and separated by a
95 : // semi-colon.
96 : // @param cmd_line The command line to parse.
97 : // @param param_name The parameter that we want to read.
98 : // @param values Will receive the set of parsed values.
99 : // @returns true on success, false otherwise.
100 : bool ReadIgnoredStackIdsFromCommandLine(const CommandLine& cmd_line,
101 : const std::string& param_name,
102 E : std::set<AsanStackId>* values) {
103 E : DCHECK(values != NULL);
104 E : if (!cmd_line.HasSwitch(param_name))
105 E : return true;
106 :
107 E : std::string value_str = cmd_line.GetSwitchValueASCII(param_name);
108 E : base::StringTokenizer string_tokenizer(value_str, ";");
109 E : while (string_tokenizer.GetNext()) {
110 E : int64 new_value = 0;
111 E : if (!base::HexStringToInt64(string_tokenizer.token(), &new_value)) {
112 E : LOG(ERROR) << "Failed to parse \"" << param_name << "\" value of \""
113 : << string_tokenizer.token() << "\".";
114 E : return false;
115 : }
116 :
117 E : VLOG(1) << "Parsed \"" << param_name << "\" value of "
118 : << base::StringPrintf("0x%016llX", new_value) << ".";
119 E : values->insert(static_cast<AsanStackId>(new_value));
120 E : }
121 :
122 E : return true;
123 E : }
124 :
125 : } // namespace
126 :
127 : // SYZYgy Asan Runtime Options.
128 : const char kAsanParametersSectionName[] = ".syzyaro";
129 : const uint32 kAsanParametersSectionCharacteristics =
130 : IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
131 :
132 : // Default values of HeapProxy parameters
133 : const uint32 kDefaultQuarantineSize = 16 * 1024 * 1024;
134 : const uint32 kDefaultQuarantineBlockSize = 4 * 1024 * 1024;
135 : const uint32 kDefaultTrailerPaddingSize = 0;
136 : const float kDefaultAllocationGuardRate = 1.0;
137 :
138 : // Default values of StackCaptureCache parameters.
139 : const uint32 kDefaultReportingPeriod = 0;
140 : const uint32 kDefaultBottomFramesToSkip = 0;
141 :
142 : // Default values of StackCapture parameters.
143 : // From http://msdn.microsoft.com/en-us/library/bb204633.aspx,
144 : // The maximum number of frames which CaptureStackBackTrace can be asked
145 : // to traverse must be less than 63, so this can't be any larger than 62.
146 : const uint32 kDefaultMaxNumFrames = 62;
147 :
148 : // Default values of AsanRuntime parameters.
149 : const bool kDefaultExitOnFailure = false;
150 : const bool kDefaultCheckHeapOnFailure = true;
151 : const bool kDefaultDisableBreakpadReporting = false;
152 :
153 : // Default values of AsanLogger parameters.
154 : const bool kDefaultMiniDumpOnFailure = false;
155 : const bool kDefaultLogAsText = true;
156 :
157 : // Default values of ZebraBlockHeap parameters.
158 : const uint32 kDefaultZebraBlockHeapSize = 16 * 1024 * 1024;
159 : const float kDefaultZebraBlockHeapQuarantineRatio = 0.25f;
160 :
161 : // Default values of the BlockHeapManager parameters.
162 : const bool kDefaultEnableCtMalloc = true;
163 : const bool kDefaultEnableRateTargetedHeaps = true;
164 : const bool kDefaultEnableZebraBlockHeap = false;
165 : const bool kDefaultEnableAllocationFilter = false;
166 :
167 : // Default values of LargeBlockHeap parameters.
168 : extern const bool kDefaultEnableLargeBlockHeap = true;
169 : // We want to maintain an allocation overhead of around 45%, which has been
170 : // our historic average for Chrome. Overhead in this heap is 2 pages, so want
171 : // 2 / 0.45 = 4.44 < 5 page minimum.
172 : extern const size_t kDefaultLargeAllocationThreshold = 5 * 4096;
173 :
174 : const char kSyzyAsanOptionsEnvVar[] = "SYZYGY_ASAN_OPTIONS";
175 :
176 : // String names of HeapProxy parameters.
177 : const char kParamQuarantineSize[] = "quarantine_size";
178 : const char kParamQuarantineBlockSize[] = "quarantine_block_size";
179 : const char kParamTrailerPaddingSize[] = "trailer_padding_size";
180 : extern const char kParamAllocationGuardRate[] = "allocation_guard_rate";
181 :
182 : // String names of StackCaptureCache parameters.
183 : const char kParamReportingPeriod[] = "compression_reporting_period";
184 : const char kParamBottomFramesToSkip[] = "bottom_frames_to_skip";
185 :
186 : // String names of StackCapture parameters.
187 : const char kParamMaxNumFrames[] = "max_num_frames";
188 :
189 : // String names of AsanRuntime parameters.
190 : const char kParamIgnoredStackIds[] = "ignored_stack_ids";
191 : const char kParamExitOnFailure[] = "exit_on_failure";
192 : const char kParamNoCheckHeapOnFailure[] = "no_check_heap_on_failure";
193 : const char kParamDisableBreakpadReporting[] = "disable_breakpad";
194 :
195 : // String names of AsanLogger parameters.
196 : const char kParamMiniDumpOnFailure[] = "minidump_on_failure";
197 : const char kParamNoLogAsText[] = "no_log_as_text";
198 :
199 : // String names of ZebraBlockHeap parameters.
200 : const char kParamZebraBlockHeapSize[] = "zebra_block_heap_size";
201 : const char kParamZebraBlockHeapQuarantineRatio[] =
202 : "zebra_block_heap_quarantine_ratio";
203 :
204 : // String names of BlockHeapManager parameters.
205 : const char kParamDisableCtMalloc[] = "disable_ctmalloc";
206 : const char kParamDisableRateTargetedHeaps[] = "disable_rate_targeted_heaps";
207 : const char kParamEnableZebraBlockHeap[] = "enable_zebra_block_heap";
208 : const char kParamEnableAllocationFilter[] = "enable_allocation_filter";
209 :
210 : // String names of LargeBlockHeap parameters.
211 : const char kParamDisableLargeBlockHeap[] = "disable_large_block_heap";
212 : const char kParamLargeAllocationThreshold[] = "large_allocation_threshold";
213 :
214 E : InflatedAsanParameters::InflatedAsanParameters() {
215 : // Clear the AsanParameters portion of ourselves.
216 E : ::memset(this, 0, sizeof(AsanParameters));
217 E : }
218 :
219 : FlatAsanParameters::FlatAsanParameters(
220 E : const InflatedAsanParameters& asan_parameters) {
221 E : bool have_ignored_stack_ids = !asan_parameters.ignored_stack_ids_set.empty();
222 :
223 E : size_t struct_size = sizeof(AsanParameters);
224 E : size_t ignored_stack_ids_size = 0;
225 E : if (have_ignored_stack_ids) {
226 : ignored_stack_ids_size = sizeof(AsanStackId) *
227 E : (asan_parameters.ignored_stack_ids_set.size() + 1);
228 : }
229 E : size_t data_size = struct_size + ignored_stack_ids_size;
230 :
231 E : data_.resize(data_size, 0);
232 E : ::memcpy(data_.data(), &asan_parameters, struct_size);
233 :
234 : // Get typed pointers to the underlying data.
235 E : AsanParameters* params = reinterpret_cast<AsanParameters*>(data_.data());
236 E : AsanStackId* ignored_stack_ids = NULL;
237 E : if (have_ignored_stack_ids) {
238 : ignored_stack_ids = reinterpret_cast<AsanStackId*>(
239 E : data_.data() + struct_size);
240 : }
241 :
242 : // Patch things up.
243 E : params->size = data_size;
244 E : params->ignored_stack_ids = ignored_stack_ids;
245 :
246 E : if (have_ignored_stack_ids) {
247 : // Fill in the ignored stack IDs.
248 : std::set<AsanStackId>::const_iterator id_it =
249 E : asan_parameters.ignored_stack_ids_set.begin();
250 E : for (; id_it != asan_parameters.ignored_stack_ids_set.end(); ++id_it)
251 E : *(ignored_stack_ids++) = *id_it;
252 E : *(ignored_stack_ids++) = 0; // Terminating NULL.
253 : DCHECK_EQ(data_.data() + data_size,
254 E : reinterpret_cast<uint8*>(ignored_stack_ids));
255 : }
256 E : }
257 :
258 E : void SetDefaultAsanParameters(AsanParameters* asan_parameters) {
259 E : DCHECK_NE(reinterpret_cast<AsanParameters*>(NULL), asan_parameters);
260 :
261 E : ::memset(asan_parameters, 0, sizeof(AsanParameters));
262 :
263 E : asan_parameters->size = sizeof(AsanParameters);
264 E : asan_parameters->version = kAsanParametersVersion;
265 E : asan_parameters->quarantine_size = kDefaultQuarantineSize;
266 E : asan_parameters->reporting_period = kDefaultReportingPeriod;
267 E : asan_parameters->bottom_frames_to_skip = kDefaultBottomFramesToSkip;
268 E : asan_parameters->max_num_frames = kDefaultMaxNumFrames;
269 E : asan_parameters->trailer_padding_size = kDefaultTrailerPaddingSize;
270 E : asan_parameters->ignored_stack_ids = NULL;
271 E : asan_parameters->quarantine_block_size = kDefaultQuarantineBlockSize;
272 E : asan_parameters->minidump_on_failure = kDefaultMiniDumpOnFailure;
273 E : asan_parameters->exit_on_failure = kDefaultExitOnFailure;
274 E : asan_parameters->check_heap_on_failure = kDefaultCheckHeapOnFailure;
275 E : asan_parameters->log_as_text = kDefaultLogAsText;
276 : asan_parameters->disable_breakpad_reporting =
277 E : kDefaultDisableBreakpadReporting;
278 E : asan_parameters->allocation_guard_rate = kDefaultAllocationGuardRate;
279 E : asan_parameters->zebra_block_heap_size = kDefaultZebraBlockHeapSize;
280 : asan_parameters->zebra_block_heap_quarantine_ratio =
281 E : kDefaultZebraBlockHeapQuarantineRatio;
282 E : asan_parameters->enable_ctmalloc = kDefaultEnableCtMalloc;
283 E : asan_parameters->enable_rate_targeted_heaps = kDefaultEnableRateTargetedHeaps;
284 E : asan_parameters->enable_zebra_block_heap = kDefaultEnableZebraBlockHeap;
285 E : asan_parameters->enable_large_block_heap = kDefaultEnableLargeBlockHeap;
286 E : asan_parameters->enable_allocation_filter = kDefaultEnableAllocationFilter;
287 : asan_parameters->large_allocation_threshold =
288 E : kDefaultLargeAllocationThreshold;
289 E : }
290 :
291 : bool InflateAsanParameters(const AsanParameters* pod_params,
292 E : InflatedAsanParameters* inflated_params) {
293 : // This must be kept up to date with AsanParameters as it evolves.
294 : static const size_t kSizeOfAsanParametersByVersion[] =
295 : { 40, 44, 48, 52, 52, 52, 56, 56, 56 };
296 : COMPILE_ASSERT(arraysize(kSizeOfAsanParametersByVersion) ==
297 : kAsanParametersVersion + 1,
298 : kSizeOfAsanParametersByVersion_out_of_date);
299 :
300 E : SetDefaultAsanParameters(inflated_params);
301 :
302 E : const uint8* data = reinterpret_cast<const uint8*>(pod_params);
303 E : const uint8* data_end = data + pod_params->size;
304 :
305 : // This is the size of known POD data in the version of the structure
306 : // being inflated.
307 : size_t min_pod_size = kSizeOfAsanParametersByVersion[
308 E : std::min(kAsanParametersVersion, pod_params->version)];
309 E : const uint8* min_pod_end = data + min_pod_size;
310 :
311 : // If we have stack IDs, ensure the pointer is to a valid location.
312 E : if (pod_params->ignored_stack_ids != NULL) {
313 : const uint8* ignored_stack_ids = reinterpret_cast<const uint8*>(
314 E : pod_params->ignored_stack_ids);
315 E : if (ignored_stack_ids < min_pod_end || ignored_stack_ids >= data_end) {
316 E : LOG(ERROR) << "Invalid ignored_stack_ids pointer.";
317 E : return false;
318 : }
319 : }
320 :
321 : // Only copy as many parameters as the structure contains, or that our version
322 : // of the runtime understands.
323 E : DCHECK_LE(min_pod_size, sizeof(AsanParameters));
324 E : ::memcpy(inflated_params, pod_params, min_pod_size);
325 :
326 : // Patch up the params to reflect our runtime version.
327 E : inflated_params->size = sizeof(AsanParameters);
328 E : inflated_params->version = kAsanParametersVersion;
329 E : (static_cast<AsanParameters*>(inflated_params))->ignored_stack_ids = NULL;
330 :
331 : // Populate the ignored stack ids.
332 E : const AsanStackId* stack_id = pod_params->ignored_stack_ids;
333 E : if (stack_id == NULL)
334 E : return true;
335 E : while (*stack_id != NULL) {
336 E : if (reinterpret_cast<const uint8*>(stack_id) > data_end) {
337 E : LOG(ERROR) << "AsanParameters::ignored_stack_ids list is not NULL "
338 : << "terminated.";
339 E : return false;
340 : }
341 E : inflated_params->ignored_stack_ids_set.insert(*stack_id);
342 E : ++stack_id;
343 E : }
344 :
345 E : return true;
346 E : }
347 :
348 : bool ParseAsanParameters(const base::StringPiece16& param_string,
349 E : InflatedAsanParameters* asan_parameters) {
350 E : DCHECK_NE(reinterpret_cast<InflatedAsanParameters*>(NULL), asan_parameters);
351 :
352 : // Prepends the flags with a dummy executable name to keep the CommandLine
353 : // parser happy.
354 E : std::wstring str(param_string.as_string());
355 E : str.insert(0, L" ");
356 E : str.insert(0, L"dummy.exe");
357 E : CommandLine cmd_line = CommandLine::FromString(str);
358 :
359 : // Parse the quarantine size flag.
360 : if (UpdateUint32FromCommandLine::Do(cmd_line, kParamQuarantineSize,
361 E : &asan_parameters->quarantine_size) == kFlagError) {
362 E : return false;
363 : }
364 :
365 : // Parse the quarantine block size.
366 : if (UpdateUint32FromCommandLine::Do(cmd_line, kParamQuarantineBlockSize,
367 E : &asan_parameters->quarantine_block_size) == kFlagError) {
368 i : return false;
369 : }
370 :
371 : // Parse the trailer padding size flag.
372 : if (UpdateUint32FromCommandLine::Do(cmd_line, kParamTrailerPaddingSize,
373 E : &asan_parameters->trailer_padding_size) == kFlagError) {
374 i : return false;
375 : }
376 :
377 : // Parse the allocation guard rate.
378 : if (UpdateFloatFromCommandLine::Do(cmd_line, kParamAllocationGuardRate,
379 E : &asan_parameters->allocation_guard_rate) == kFlagError) {
380 i : return false;
381 : }
382 :
383 : // Parse the reporting period flag.
384 : if (UpdateUint32FromCommandLine::Do(cmd_line, kParamReportingPeriod,
385 E : &asan_parameters->reporting_period) == kFlagError) {
386 i : return false;
387 : }
388 :
389 : // Parse the bottom frames to skip flag.
390 : if (UpdateUint32FromCommandLine::Do(cmd_line, kParamBottomFramesToSkip,
391 E : &asan_parameters->bottom_frames_to_skip) == kFlagError) {
392 i : return false;
393 : }
394 :
395 : // Parse the max number of frames flag.
396 : if (UpdateUint32FromCommandLine::Do(cmd_line, kParamMaxNumFrames,
397 E : &asan_parameters->max_num_frames) == kFlagError) {
398 i : return false;
399 : }
400 :
401 : // Parse the ignored stack ids.
402 : if (!ReadIgnoredStackIdsFromCommandLine(cmd_line, kParamIgnoredStackIds,
403 E : &asan_parameters->ignored_stack_ids_set)) {
404 E : return false;
405 : }
406 :
407 : // Parse the zebra block heap size flag.
408 : if (UpdateUint32FromCommandLine::Do(cmd_line, kParamZebraBlockHeapSize,
409 E : &asan_parameters->zebra_block_heap_size) == kFlagError) {
410 i : return false;
411 : }
412 :
413 : // Parse the zebra block heap quarantine ratio flag.
414 : if (UpdateFloatFromCommandLine::Do(cmd_line,
415 : kParamZebraBlockHeapQuarantineRatio,
416 E : &asan_parameters->zebra_block_heap_quarantine_ratio) == kFlagError) {
417 i : return false;
418 : }
419 :
420 : // Parse the large block heap allocation threshold.
421 : if (UpdateUint32FromCommandLine::Do(cmd_line,
422 : kParamLargeAllocationThreshold,
423 E : &asan_parameters->large_allocation_threshold) == kFlagError) {
424 i : return false;
425 : }
426 :
427 : // Parse the other (boolean) flags.
428 E : if (cmd_line.HasSwitch(kParamMiniDumpOnFailure))
429 E : asan_parameters->minidump_on_failure = true;
430 E : if (cmd_line.HasSwitch(kParamExitOnFailure))
431 E : asan_parameters->exit_on_failure = true;
432 E : if (cmd_line.HasSwitch(kParamNoLogAsText))
433 E : asan_parameters->log_as_text = false;
434 E : if (cmd_line.HasSwitch(kParamNoCheckHeapOnFailure))
435 E : asan_parameters->check_heap_on_failure = false;
436 E : if (cmd_line.HasSwitch(kParamDisableBreakpadReporting))
437 E : asan_parameters->disable_breakpad_reporting = true;
438 E : if (cmd_line.HasSwitch(kParamDisableCtMalloc))
439 E : asan_parameters->enable_ctmalloc = false;
440 E : if (cmd_line.HasSwitch(kParamEnableZebraBlockHeap))
441 E : asan_parameters->enable_zebra_block_heap = true;
442 E : if (cmd_line.HasSwitch(kParamDisableLargeBlockHeap))
443 E : asan_parameters->enable_large_block_heap = false;
444 E : if (cmd_line.HasSwitch(kParamDisableRateTargetedHeaps))
445 E : asan_parameters->enable_rate_targeted_heaps = false;
446 E : if (cmd_line.HasSwitch(kParamEnableAllocationFilter))
447 E : asan_parameters->enable_allocation_filter = true;
448 :
449 E : return true;
450 E : }
451 :
452 : } // namespace common
|