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