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 <windows.h> // NOLINT
16 : #include <psapi.h>
17 :
18 : #include "base/at_exit.h"
19 : #include "base/atomicops.h"
20 : #include "base/command_line.h"
21 : #include "base/logging.h"
22 : #include "base/files/file_path.h"
23 : #include "base/win/pe_image.h"
24 : #include "syzygy/agent/asan/asan_rtl_impl.h"
25 : #include "syzygy/agent/asan/asan_runtime.h"
26 : #include "syzygy/agent/common/agent.h"
27 : #include "syzygy/common/com_utils.h"
28 : #include "syzygy/common/logging.h"
29 :
30 : // The linker satisfies this symbol. This gets us a pointer to our own module
31 : // when we're loaded.
32 : extern "C" IMAGE_DOS_HEADER __ImageBase;
33 :
34 : namespace {
35 :
36 : using agent::asan::AsanRuntime;
37 :
38 : // Our AtExit manager required by base.
39 : base::AtExitManager* at_exit = NULL;
40 :
41 : // The asan runtime manager.
42 : AsanRuntime* asan_runtime = NULL;
43 :
44 E : void SetUpAtExitManager() {
45 E : DCHECK(at_exit == NULL);
46 E : at_exit = new base::AtExitManager();
47 E : CHECK(at_exit != NULL);
48 E : }
49 :
50 : void TearDownAtExitManager() {
51 : DCHECK(at_exit != NULL);
52 : delete at_exit;
53 : at_exit = NULL;
54 : }
55 :
56 : // Returns the name of this module.
57 E : bool GetSelfPath(base::FilePath* self_path) {
58 E : DCHECK_NE(reinterpret_cast<base::FilePath*>(NULL), self_path);
59 :
60 E : HMODULE self = reinterpret_cast<HMODULE>(&__ImageBase);
61 :
62 E : std::vector<wchar_t> name(1024, 0);
63 E : while (true) {
64 E : size_t n = ::GetModuleFileNameW(self, name.data(), name.size());
65 E : if (n == 0) {
66 i : DWORD error = ::GetLastError();
67 i : LOG(ERROR) << "GetModuleFileNameW failed: "
68 : << common::LogWe(error) << ".";
69 i : return false;
70 : }
71 :
72 : // If we read the whole thing we're done.
73 E : if (n < name.size())
74 E : break;
75 :
76 : // Otherwise resize the buffer and try again.
77 i : name.resize(2 * name.size(), 0);
78 i : }
79 :
80 E : *self_path = base::FilePath(name.data());
81 E : return true;
82 E : }
83 :
84 : // Used as user data by EnumImportChunksCallback. This is used to look for a
85 : // module matching |basename|. Success or failure is returned via |match|.
86 : struct EnumImportChunksCookie {
87 : const std::string* basename;
88 : bool match;
89 : };
90 :
91 : // Examines the imported |module|. If it matches the |basename| specified in
92 : // |cookie|, then aborts the search and indicates success via the |match|
93 : // parameter in |cookie|.
94 : bool EnumImportChunksCallback(const base::win::PEImage& image,
95 : LPCSTR module,
96 : PIMAGE_THUNK_DATA name_table,
97 : PIMAGE_THUNK_DATA iat,
98 E : PVOID cookie) {
99 E : DCHECK_NE(reinterpret_cast<LPCSTR>(NULL), module);
100 E : DCHECK_NE(reinterpret_cast<PVOID>(NULL), cookie);
101 :
102 : EnumImportChunksCookie* eicc =
103 E : reinterpret_cast<EnumImportChunksCookie*>(cookie);
104 E : if (::_stricmp(eicc->basename->c_str(), module) == 0) {
105 : // Indicate that the module was found.
106 E : eicc->match = true;
107 : // Stop the enumeration as we're done.
108 E : return false;
109 : }
110 :
111 : // Continue the iteration.
112 E : return true;
113 E : }
114 :
115 : // Inspects the given module for embedded Asan parameters. If they are found
116 : // sets a pointer to them in |asan_params|. Returns true on success, false
117 : // otherwise.
118 : bool InspectModuleForEmbeddedAsanParameters(
119 : const std::string& self_basename,
120 : HMODULE module,
121 E : const common::AsanParameters** asan_params) {
122 E : DCHECK_NE(reinterpret_cast<HMODULE>(NULL), module);
123 E : DCHECK_NE(reinterpret_cast<common::AsanParameters**>(NULL), asan_params);
124 :
125 E : *asan_params = NULL;
126 :
127 E : base::win::PEImage pe_image(module);
128 E : EnumImportChunksCookie eicc = { &self_basename, false };
129 E : pe_image.EnumImportChunks(&EnumImportChunksCallback, &eicc);
130 :
131 : // If there was no matching import then we can skip this module.
132 E : if (!eicc.match)
133 E : return true;
134 :
135 : // Look for the magic section containing the runtime parameters. If found
136 : // then set the pointer to the parameters.
137 : PIMAGE_SECTION_HEADER section = pe_image.GetImageSectionHeaderByName(
138 E : common::kAsanParametersSectionName);
139 E : if (section != NULL) {
140 E : const uint8* image_base = reinterpret_cast<const uint8*>(module);
141 : *asan_params = reinterpret_cast<const common::AsanParameters*>(
142 E : image_base + section->VirtualAddress);
143 : }
144 :
145 E : return true;
146 E : }
147 :
148 : // |asan_params| will be populated with a pointer to any found Asan parameters,
149 : // and will be set to NULL if none are found.
150 E : bool LookForEmbeddedAsanParameters(const common::AsanParameters** asan_params) {
151 E : DCHECK_NE(reinterpret_cast<common::AsanParameters**>(NULL), asan_params);
152 E : *asan_params = NULL;
153 :
154 : // Get the path of this module.
155 E : base::FilePath self_path;
156 E : if (!GetSelfPath(&self_path))
157 i : return false;
158 :
159 : // Get the base name of this module. We'll be looking for modules that import
160 : // it.
161 E : std::string self_basename = self_path.BaseName().AsUTF8Unsafe();
162 :
163 : // Determine how much space we need for the module list.
164 E : HANDLE process = ::GetCurrentProcess();
165 E : DWORD bytes_needed = 0;
166 E : if (!::EnumProcessModules(process, NULL, 0, &bytes_needed)) {
167 i : DWORD error = ::GetLastError();
168 i : LOG(ERROR) << "EnumProcessModules failed: "
169 : << ::common::LogWe(error) << ".";
170 i : return false;
171 : }
172 :
173 : // Get the list of module handles.
174 E : std::vector<HMODULE> modules(bytes_needed / sizeof(HMODULE));
175 : if (!::EnumProcessModules(process, modules.data(), bytes_needed,
176 E : &bytes_needed)) {
177 i : DWORD error = ::GetLastError();
178 i : LOG(ERROR) << "EnumProcessModules failed: "
179 : << ::common::LogWe(error) << ".";
180 i : return false;
181 : }
182 :
183 : // Inspect each module to see if it contains Asan runtime parameters. The
184 : // first ones found will be used.
185 E : for (size_t i = 0; i < modules.size(); ++i) {
186 : if (!InspectModuleForEmbeddedAsanParameters(
187 E : self_basename, modules[i], asan_params)) {
188 i : return false;
189 : }
190 :
191 : // If this module contained parameters then we've finished our search.
192 E : if (*asan_params != NULL)
193 E : return true;
194 E : }
195 :
196 E : return true;
197 E : }
198 :
199 E : void SetUpAsanRuntime() {
200 E : DCHECK(asan_runtime == NULL);
201 E : asan_runtime = new AsanRuntime();
202 E : CHECK(asan_runtime != NULL);
203 :
204 : // Look for any parameters that have been embedded in instrumented modules.
205 E : const common::AsanParameters* asan_params = NULL;
206 E : if (!LookForEmbeddedAsanParameters(&asan_params )) {
207 i : LOG(ERROR) << "Error while trying to find embedded Asan parameters.";
208 : }
209 :
210 : // Inflate these and inject them into the runtime library. These will serve
211 : // as the baseline parameters that will then be potentially modified by any
212 : // parameters via the environment.
213 : if (asan_params != NULL &&
214 E : !common::InflateAsanParameters(asan_params, &asan_runtime->params())) {
215 i : LOG(ERROR) << "Failed to inflate embedded Asan parameters.";
216 : }
217 :
218 : // Get the flags string from the environment.
219 E : std::wstring asan_flags_str;
220 E : if (!AsanRuntime::GetAsanFlagsEnvVar(&asan_flags_str)) {
221 i : LOG(ERROR) << "Error while trying to read Asan command line.";
222 : }
223 :
224 : // Setup the runtime library with the given options.
225 E : asan_runtime->SetUp(asan_flags_str);
226 E : agent::asan::SetUpRtl(asan_runtime);
227 E : }
228 :
229 E : void TearDownAsanRuntime() {
230 E : DCHECK(asan_runtime != NULL);
231 E : asan_runtime->TearDown();
232 E : delete asan_runtime;
233 E : asan_runtime = NULL;
234 E : }
235 :
236 : } // namespace
237 :
238 : extern "C" {
239 :
240 E : BOOL WINAPI DllMain(HMODULE instance, DWORD reason, LPVOID reserved) {
241 E : agent::common::InitializeCrt();
242 :
243 E : switch (reason) {
244 : case DLL_PROCESS_ATTACH: {
245 : // Create the At-Exit manager.
246 E : SetUpAtExitManager();
247 :
248 : // Disable logging. In the case of Chrome this is running in a sandboxed
249 : // process where logging to file doesn't help us any. In other cases the
250 : // log output will still go to console.
251 E : CommandLine::Init(0, NULL);
252 E : common::InitLoggingForDll(L"asan");
253 :
254 E : SetUpAsanRuntime();
255 E : break;
256 : }
257 :
258 : case DLL_THREAD_ATTACH: {
259 E : agent::asan::AsanRuntime* runtime = agent::asan::AsanRuntime::runtime();
260 E : DCHECK_NE(static_cast<agent::asan::AsanRuntime*>(nullptr), runtime);
261 E : runtime->AddThreadId(::GetCurrentThreadId());
262 E : break;
263 : }
264 :
265 : case DLL_THREAD_DETACH:
266 : // Nothing to do here.
267 E : break;
268 :
269 : case DLL_PROCESS_DETACH: {
270 E : CommandLine::Reset();
271 : // This should be the last thing called in the agent DLL before it
272 : // gets unloaded. Everything should otherwise have been initialized
273 : // and we're now just cleaning it up again.
274 E : agent::asan::TearDownRtl();
275 E : TearDownAsanRuntime();
276 E : break;
277 : }
278 :
279 : default:
280 i : NOTREACHED();
281 : break;
282 : }
283 :
284 E : return TRUE;
285 E : }
286 :
287 : } // extern "C"
|