1 : // Copyright 2015 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_util.h"
16 :
17 : #include <windows.h> // NOLINT
18 : #include <psapi.h>
19 :
20 : #include "base/command_line.h"
21 : #include "base/files/file_path.h"
22 : #include "base/win/pe_image.h"
23 : #include "syzygy/agent/asan/rtl_impl.h"
24 : #include "syzygy/agent/asan/runtime.h"
25 : #include "syzygy/common/asan_parameters.h"
26 : #include "syzygy/common/com_utils.h"
27 : #include "syzygy/common/logging.h"
28 :
29 : // The linker satisfies this symbol. This gets us a pointer to our own module
30 : // when we're loaded.
31 : extern "C" IMAGE_DOS_HEADER __ImageBase;
32 :
33 : namespace agent {
34 : namespace asan {
35 :
36 : namespace {
37 :
38 : // Returns the name of this module.
39 E : bool GetSelfPath(base::FilePath* self_path) {
40 E : DCHECK_NE(static_cast<base::FilePath*>(nullptr), self_path);
41 :
42 E : HMODULE self = reinterpret_cast<HMODULE>(&__ImageBase);
43 :
44 E : std::vector<wchar_t> name(1024, 0);
45 E : while (true) {
46 E : size_t n = ::GetModuleFileNameW(self, name.data(), name.size());
47 E : if (n == 0) {
48 i : DWORD error = ::GetLastError();
49 i : LOG(ERROR) << "GetModuleFileNameW failed: "
50 : << ::common::LogWe(error) << ".";
51 i : return false;
52 : }
53 :
54 : // If we read the whole thing we're done.
55 E : if (n < name.size())
56 E : break;
57 :
58 : // Otherwise resize the buffer and try again.
59 i : name.resize(2 * name.size(), 0);
60 i : }
61 :
62 E : *self_path = base::FilePath(name.data());
63 E : return true;
64 E : }
65 :
66 : // Used as user data by EnumImportChunksCallback. This is used to look for a
67 : // module matching |basename|. Success or failure is returned via |match|.
68 : struct EnumImportChunksCookie {
69 : const std::string* basename;
70 : bool match;
71 : };
72 :
73 : // Examines the imported |module|. If it matches the |basename| specified in
74 : // |cookie|, then aborts the search and indicates success via the |match|
75 : // parameter in |cookie|.
76 : bool EnumImportChunksCallback(const base::win::PEImage& image,
77 : LPCSTR module,
78 : PIMAGE_THUNK_DATA name_table,
79 : PIMAGE_THUNK_DATA iat,
80 E : PVOID cookie) {
81 E : DCHECK_NE(static_cast<LPCSTR>(nullptr), module);
82 E : DCHECK_NE(static_cast<PVOID>(nullptr), cookie);
83 :
84 : EnumImportChunksCookie* eicc =
85 E : reinterpret_cast<EnumImportChunksCookie*>(cookie);
86 E : if (::_stricmp(eicc->basename->c_str(), module) == 0) {
87 : // Indicate that the module was found.
88 E : eicc->match = true;
89 : // Stop the enumeration as we're done.
90 E : return false;
91 : }
92 :
93 : // Continue the iteration.
94 E : return true;
95 E : }
96 :
97 : // Inspects the given module for embedded Asan parameters. If they are found
98 : // sets a pointer to them in |asan_params|. Returns true on success, false
99 : // otherwise.
100 : bool InspectModuleForEmbeddedAsanParameters(
101 : const std::string& self_basename,
102 : HMODULE module,
103 E : const ::common::AsanParameters** asan_params) {
104 E : DCHECK_NE(static_cast<HMODULE>(nullptr), module);
105 E : DCHECK_NE(static_cast<::common::AsanParameters**>(nullptr), asan_params);
106 :
107 E : *asan_params = nullptr;
108 :
109 E : base::win::PEImage pe_image(module);
110 E : EnumImportChunksCookie eicc = { &self_basename, false };
111 E : pe_image.EnumImportChunks(&EnumImportChunksCallback, &eicc);
112 :
113 : // If there was no matching import then we can skip this module.
114 E : if (!eicc.match)
115 E : return true;
116 :
117 : // Look for the magic section containing the runtime parameters. If found
118 : // then set the pointer to the parameters.
119 : PIMAGE_SECTION_HEADER section = pe_image.GetImageSectionHeaderByName(
120 E : ::common::kAsanParametersSectionName);
121 E : if (section != nullptr) {
122 E : const uint8* image_base = reinterpret_cast<const uint8*>(module);
123 : *asan_params = reinterpret_cast<const ::common::AsanParameters*>(
124 E : image_base + section->VirtualAddress);
125 : }
126 :
127 E : return true;
128 E : }
129 :
130 : // |asan_params| will be populated with a pointer to any found Asan parameters,
131 : // and will be set to nullptr if none are found.
132 : bool LookForEmbeddedAsanParameters(
133 E : const ::common::AsanParameters** asan_params) {
134 E : DCHECK_NE(static_cast<::common::AsanParameters**>(nullptr), asan_params);
135 E : *asan_params = nullptr;
136 :
137 : // Get the path of this module.
138 E : base::FilePath self_path;
139 E : if (!GetSelfPath(&self_path))
140 i : return false;
141 :
142 : // Get the base name of this module. We'll be looking for modules that import
143 : // it.
144 E : std::string self_basename = self_path.BaseName().AsUTF8Unsafe();
145 :
146 : // Determine how much space we need for the module list.
147 E : HANDLE process = ::GetCurrentProcess();
148 E : DWORD bytes_needed = 0;
149 E : if (!::EnumProcessModules(process, nullptr, 0, &bytes_needed)) {
150 i : DWORD error = ::GetLastError();
151 i : LOG(ERROR) << "EnumProcessModules failed: "
152 : << ::common::LogWe(error) << ".";
153 i : return false;
154 : }
155 :
156 : // Get the list of module handles.
157 E : std::vector<HMODULE> modules(bytes_needed / sizeof(HMODULE));
158 : if (!::EnumProcessModules(process, modules.data(), bytes_needed,
159 E : &bytes_needed)) {
160 i : DWORD error = ::GetLastError();
161 i : LOG(ERROR) << "EnumProcessModules failed: "
162 : << ::common::LogWe(error) << ".";
163 i : return false;
164 : }
165 :
166 : // Inspect each module to see if it contains Asan runtime parameters. The
167 : // first ones found will be used.
168 E : for (size_t i = 0; i < modules.size(); ++i) {
169 : if (!InspectModuleForEmbeddedAsanParameters(
170 E : self_basename, modules[i], asan_params)) {
171 i : return false;
172 : }
173 :
174 : // If this module contained parameters then we've finished our search.
175 E : if (*asan_params != nullptr)
176 E : return true;
177 E : }
178 :
179 E : return true;
180 E : }
181 :
182 : } // namespace
183 :
184 E : bool SetUpAsanRuntime(AsanRuntime** asan_runtime) {
185 E : DCHECK_NE(static_cast<AsanRuntime**>(nullptr), asan_runtime);
186 E : DCHECK_EQ(static_cast<AsanRuntime*>(nullptr), *asan_runtime);
187 :
188 : // Look for any parameters that have been embedded in instrumented modules.
189 E : const ::common::AsanParameters* asan_params = nullptr;
190 E : if (!LookForEmbeddedAsanParameters(&asan_params )) {
191 i : LOG(ERROR) << "Error while trying to find embedded Asan parameters.";
192 : }
193 :
194 E : scoped_ptr<AsanRuntime> runtime(new AsanRuntime());
195 E : if (runtime.get() == nullptr)
196 i : return false;
197 :
198 : // Inflate these and inject them into the runtime library. These will serve
199 : // as the baseline parameters that will then be potentially modified by any
200 : // parameters via the environment.
201 : if (asan_params != nullptr &&
202 : !::common::InflateAsanParameters(asan_params,
203 E : &runtime->params())) {
204 i : LOG(ERROR) << "Failed to inflate embedded Asan parameters.";
205 : }
206 :
207 : // Get the flags string from the environment.
208 E : std::wstring asan_flags_str;
209 E : if (!AsanRuntime::GetAsanFlagsEnvVar(&asan_flags_str)) {
210 i : LOG(ERROR) << "Error while trying to read Asan command line.";
211 : }
212 :
213 : // Setup the runtime library with the given options.
214 E : if (!runtime->SetUp(asan_flags_str))
215 i : return false;
216 E : agent::asan::SetUpRtl(runtime.get());
217 :
218 : // Transfer ownership to the caller.
219 E : *asan_runtime = runtime.release();
220 E : return true;
221 E : }
222 :
223 E : void TearDownAsanRuntime(AsanRuntime** asan_runtime) {
224 E : DCHECK_NE(static_cast<AsanRuntime**>(nullptr), asan_runtime);
225 E : if (asan_runtime == nullptr)
226 i : return;
227 E : agent::asan::TearDownRtl();
228 E : (*asan_runtime)->TearDown();
229 E : delete *asan_runtime;
230 E : *asan_runtime = nullptr;
231 E : }
232 :
233 : } // namespace asan
234 : } // namespace agent
|