1 : // Copyright 2013 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/instrument/instrumenters/asan_instrumenter.h"
16 :
17 : #include <algorithm>
18 :
19 : #include "base/logging.h"
20 : #include "base/files/file_util.h"
21 : #include "syzygy/application/application.h"
22 : #include "syzygy/instrument/transforms/allocation_filter_transform.h"
23 :
24 : namespace {
25 : using instrument::transforms::AllocationFilterTransform;
26 : }
27 :
28 : namespace instrument {
29 : namespace instrumenters {
30 :
31 : AsanInstrumenter::AsanInstrumenter()
32 : : use_interceptors_(true),
33 : remove_redundant_checks_(true),
34 : use_liveness_analysis_(true),
35 : instrumentation_rate_(1.0),
36 : asan_rtl_options_(false),
37 E : hot_patching_(false) {
38 E : }
39 :
40 E : bool AsanInstrumenter::ImageFormatIsSupported(ImageFormat image_format) {
41 : if (image_format == BlockGraph::PE_IMAGE ||
42 E : image_format == BlockGraph::COFF_IMAGE) {
43 E : return true;
44 : }
45 i : return false;
46 E : }
47 :
48 E : bool AsanInstrumenter::InstrumentPrepare() {
49 E : return true;
50 E : }
51 :
52 E : bool AsanInstrumenter::InstrumentImpl() {
53 : // Parse the filter if one was provided.
54 E : scoped_ptr<pe::ImageFilter> filter;
55 E : if (!filter_path_.empty()) {
56 E : filter.reset(new pe::ImageFilter());
57 E : if (!filter->LoadFromJSON(filter_path_)) {
58 i : LOG(ERROR) << "Failed to parse filter file: " << filter_path_.value();
59 i : return false;
60 : }
61 :
62 : // Ensure it is for the input module.
63 E : if (!filter->IsForModule(input_image_path_)) {
64 E : LOG(ERROR) << "Filter does not match the input module.";
65 E : return false;
66 : }
67 : }
68 :
69 E : asan_transform_.reset(new instrument::transforms::AsanTransform());
70 E : asan_transform_->set_instrument_dll_name(agent_dll_);
71 E : asan_transform_->set_use_interceptors(use_interceptors_);
72 E : asan_transform_->set_use_liveness_analysis(use_liveness_analysis_);
73 E : asan_transform_->set_remove_redundant_checks(remove_redundant_checks_);
74 E : asan_transform_->set_instrumentation_rate(instrumentation_rate_);
75 E : asan_transform_->set_hot_patching(hot_patching_);
76 :
77 : // Set up the filter if one was provided.
78 E : if (filter.get()) {
79 E : filter_.reset(filter.release());
80 E : asan_transform_->set_filter(&filter_->filter);
81 : }
82 :
83 : // Set overwrite source range flag in the Asan transform. The Asan
84 : // transformation will overwrite the source range of created instructions to
85 : // the source range of corresponding instrumented instructions.
86 E : asan_transform_->set_debug_friendly(debug_friendly_);
87 :
88 : // If RTL options were provided then pass them to the transform.
89 E : if (asan_rtl_options_)
90 E : asan_transform_->set_asan_parameters(&asan_params_);
91 :
92 E : if (!relinker_->AppendTransform(asan_transform_.get()))
93 i : return false;
94 :
95 : // Append the AllocationFilter transform if necessary.
96 E : if (af_transform_.get() != nullptr) {
97 i : if (!relinker_->AppendTransform(af_transform_.get()))
98 i : return false;
99 : }
100 :
101 E : return true;
102 E : }
103 :
104 : bool AsanInstrumenter::DoCommandLineParse(
105 E : const base::CommandLine* command_line) {
106 E : if (!Super::DoCommandLineParse(command_line))
107 i : return false;
108 :
109 : // Parse the additional command line arguments.
110 E : filter_path_ = command_line->GetSwitchValuePath("filter");
111 E : use_liveness_analysis_ = !command_line->HasSwitch("no-liveness-analysis");
112 E : remove_redundant_checks_ = !command_line->HasSwitch("no-redundancy-analysis");
113 E : use_interceptors_ = !command_line->HasSwitch("no-interceptors");
114 E : hot_patching_ = command_line->HasSwitch("hot-patching");
115 :
116 : // Parse the instrumentation rate if one has been provided.
117 : static const char kInstrumentationRate[] = "instrumentation-rate";
118 E : if (command_line->HasSwitch(kInstrumentationRate)) {
119 E : std::string s = command_line->GetSwitchValueASCII(kInstrumentationRate);
120 E : double d = 0;
121 E : if (!base::StringToDouble(s, &d)) {
122 E : LOG(ERROR) << "Failed to parse floating point value: " << s;
123 E : return false;
124 : }
125 : // Cap the rate to the range of valid values [0, 1].
126 E : instrumentation_rate_ = std::max(0.0, std::min(1.0, d));
127 E : }
128 :
129 : // Parse Asan RTL options if present.
130 : static const char kAsanRtlOptions[] = "asan-rtl-options";
131 E : asan_rtl_options_ = command_line->HasSwitch(kAsanRtlOptions);
132 E : if (asan_rtl_options_) {
133 E : std::wstring options = command_line->GetSwitchValueNative(kAsanRtlOptions);
134 : // The Asan RTL options string might be encapsulated in quotes, remove them
135 : // if it's the case.
136 E : if (!options.empty() && options[0] == L'\"') {
137 E : CHECK_EQ(L'\"', options.back()) << "If the asan-rtl-options string "
138 : << "starts with a quote it should also end with one.";
139 E : options.erase(options.begin());
140 E : if (!options.empty())
141 E : options.pop_back();
142 : }
143 E : common::SetDefaultAsanParameters(&asan_params_);
144 E : if (!common::ParseAsanParameters(options, &asan_params_))
145 E : return false;
146 E : }
147 :
148 : // Parse the allocation-filter flag.
149 : static const char kAsanAllocationFilter[] = "allocation-filter-config-file";
150 : allocation_filter_config_file_path_ = command_line->GetSwitchValuePath(
151 E : kAsanAllocationFilter);
152 :
153 : // Setup the AllocationFilter transform if a configuration file was specified.
154 E : if (!allocation_filter_config_file_path_.empty()) {
155 E : std::string json_string;
156 E : AllocationFilterTransform::FunctionNameOffsetMap target_calls;
157 : if (!AllocationFilterTransform::ReadFromJSON(
158 E : allocation_filter_config_file_path_, &target_calls)) {
159 E : LOG(ERROR) << "Failed to parse allocation-filter configuration file: "
160 : << allocation_filter_config_file_path_.value();
161 E : return false;
162 : }
163 :
164 E : if (!target_calls.empty()) {
165 : // Setup the allocation-filter transform.
166 E : af_transform_.reset(new AllocationFilterTransform(target_calls));
167 :
168 : // Set overwrite source range flag in the AllocationFilter transform.
169 : // It will overwrite the source range of created instructions to the
170 : // source range of corresponding instrumented instructions. The
171 : // AllocationFilter transform shares the Asan flag.
172 E : af_transform_->set_debug_friendly(debug_friendly_);
173 : }
174 E : }
175 :
176 : // Set default agent dll name if none provided. This has to be done here
177 : // because ParseCommandLine expects agent_dll_ to be filled.
178 E : if (agent_dll_.empty()) {
179 E : if (!hot_patching_) {
180 E : agent_dll_ = instrument::transforms::AsanTransform::kSyzyAsanDll;
181 E : } else {
182 E : agent_dll_ = instrument::transforms::AsanTransform::kSyzyAsanHpDll;
183 : }
184 : }
185 :
186 E : return true;
187 E : }
188 :
189 : } // namespace instrumenters
190 : } // namespace instrument
|