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>
16 :
17 : #include "base/at_exit.h"
18 : #include "base/bind.h"
19 : #include "base/command_line.h"
20 : #include "base/logging.h"
21 : #include "base/synchronization/lock.h"
22 : #include "syzygy/agent/asan/iat_patcher.h"
23 : #include "syzygy/agent/asan/memory_interceptors.h"
24 : #include "syzygy/agent/asan/rtl_impl.h"
25 : #include "syzygy/agent/asan/runtime.h"
26 : #include "syzygy/agent/asan/runtime_util.h"
27 : #include "syzygy/agent/common/agent.h"
28 : #include "syzygy/common/logging.h"
29 :
30 : namespace agent {
31 : namespace asan {
32 : namespace {
33 :
34 : struct AsanFeatureName {
35 : AsanFeature flag;
36 : const char* name;
37 : };
38 :
39 : static const AsanFeatureName kAsanFeatureNames[] = {
40 : {ASAN_FEATURE_ENABLE_PAGE_PROTECTIONS, "SyzyASANPageProtections"},
41 : {DEPRECATED_ASAN_FEATURE_ENABLE_CTMALLOC, nullptr},
42 : {ASAN_FEATURE_ENABLE_LARGE_BLOCK_HEAP, "SyzyASANLargeBlockHeap"},
43 : {DEPRECATED_ASAN_FEATURE_ENABLE_KASKO, nullptr},
44 : {ASAN_FEATURE_ENABLE_CRASHPAD, "SyzyASANCrashpad"},
45 : };
46 :
47 : // This lock guards against IAT patching on multiple threads concurrently.
48 E : base::Lock patch_lock;
49 :
50 : // The maximum number of patch attemps to tolerate.
51 : const size_t kPatchAttempsMax = 10;
52 : // Counts the number of patch attempts that have occurred. Under patch_lock.
53 : size_t patch_attempts = 0;
54 : // Set to true when patching has been successfully accomplished.
55 : bool patch_complete = false;
56 :
57 : // Our AtExit manager required by base.
58 : base::AtExitManager* at_exit = nullptr;
59 :
60 : // The asan runtime manager.
61 : AsanRuntime* asan_runtime = nullptr;
62 :
63 E : void SetUpAtExitManager() {
64 E : DCHECK_EQ(static_cast<base::AtExitManager*>(nullptr), at_exit);
65 E : at_exit = new base::AtExitManager();
66 E : CHECK_NE(static_cast<base::AtExitManager*>(nullptr), at_exit);
67 E : }
68 :
69 E : void TearDownAtExitManager() {
70 E : DCHECK_NE(static_cast<base::AtExitManager*>(nullptr), at_exit);
71 E : delete at_exit;
72 E : at_exit = nullptr;
73 E : }
74 :
75 E : MemoryAccessorMode SelectMemoryAccessorMode() {
76 : static uint64_t kOneGB = 1ull << 30;
77 :
78 : // If there is no runtime then use the noop probes.
79 E : if (asan_runtime == nullptr)
80 i : return MEMORY_ACCESSOR_MODE_NOOP;
81 :
82 : // Determine the amount of shadow memory allocated.
83 E : uint64_t gb = asan_runtime->shadow()->length();
84 E : gb <<= kShadowRatioLog;
85 E : gb /= kOneGB;
86 :
87 E : switch (gb) {
88 : case 2:
89 E : return MEMORY_ACCESSOR_MODE_2G;
90 : case 4:
91 E : return MEMORY_ACCESSOR_MODE_4G;
92 : // 1GB should never happen, and 3GB simply isn't properly supported.
93 : default:
94 i : return MEMORY_ACCESSOR_MODE_NOOP;
95 : }
96 E : }
97 :
98 E : MemoryAccessorMode OnRedirectStubEntry(const void* caller_address) {
99 : // This grabs the loader's lock, which could be a problem. If there are
100 : // multiple instrumented DLLs, or a single one executing on multiple threads,
101 : // there could be lock inversion here. The possibility seems remote, though.
102 : // Maybe locating the module associated with the caller_address can be done
103 : // with a VirtualQuery, with a fallback to the loader for an additional pair
104 : // of belt-and-suspenders...
105 E : const DWORD kFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
106 : GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
107 E : HMODULE calling_module = nullptr;
108 E : BOOL success = ::GetModuleHandleEx(
109 : kFlags, reinterpret_cast<LPCWSTR>(caller_address), &calling_module);
110 E : CHECK_EQ(TRUE, success);
111 :
112 : // TODO(chrisha): Implement logic for selecting the noop mode if the system
113 : // isn't up to par, if so configured by Finch, if the shadow memory
114 : // allocation failed, etc.
115 E : MemoryAccessorMode mode = SelectMemoryAccessorMode();
116 :
117 : // If a runtime has been successfully allocated but for whatever reason the
118 : // noop instrumentation has been selected, then cleanup the runtime
119 : // allocation.
120 E : if (mode == MEMORY_ACCESSOR_MODE_NOOP && asan_runtime != nullptr)
121 i : TearDownAsanRuntime(&asan_runtime);
122 :
123 : // Build the IAT patch map.
124 E : IATPatchMap patch_map;
125 E : for (size_t i = 0; i < kNumMemoryAccessorVariants; ++i) {
126 E : patch_map.insert(
127 : std::make_pair(kMemoryAccessorVariants[i].name,
128 : kMemoryAccessorVariants[i].accessors[mode]));
129 E : }
130 :
131 : // Grab the patching lock only while patching the caller's IAT. Assuming no
132 : // other parties are patching this IAT, this is sufficient to prevent
133 : // double-patching due to multiple threads invoking on instrumentation
134 : // concurrently idempotent.
135 E : base::AutoLock lock(patch_lock);
136 E : if (!patch_complete) {
137 E : ++patch_attempts;
138 E : auto result = PatchIATForModule(calling_module, patch_map);
139 : // If somebody is racing with us to patch our IAT we want to know about it.
140 E : CHECK_EQ(0u, result & PATCH_FAILED_RACY_WRITE);
141 :
142 : // Increment the counter on failure and potentially try again.
143 E : if (result != PATCH_SUCCEEDED) {
144 i : CHECK_LE(patch_attempts, kPatchAttempsMax);
145 i : } else {
146 E : patch_complete = true;
147 : }
148 : }
149 :
150 E : return mode;
151 E : }
152 :
153 : } // namespace
154 :
155 : extern "C" {
156 :
157 E : BOOL WINAPI DllMain(HMODULE instance, DWORD reason, LPVOID reserved) {
158 E : agent::common::InitializeCrt();
159 :
160 E : switch (reason) {
161 : case DLL_PROCESS_ATTACH: {
162 : // Create the At-Exit manager.
163 E : SetUpAtExitManager();
164 :
165 : // Disable logging. In the case of Chrome this is running in a sandboxed
166 : // process where logging to file doesn't help us any. In other cases the
167 : // log output will still go to console.
168 E : base::CommandLine::Init(0, NULL);
169 E : ::common::InitLoggingForDll(L"asan");
170 :
171 : // Setup the ASAN runtime. If this fails then |asan_runtime| will remain
172 : // nullptr, and the stub redirection will enable the noop probes.
173 E : SetUpAsanRuntime(&asan_runtime);
174 :
175 : // Hookup IAT patching on redirector stub entry.
176 E : agent::asan::SetRedirectEntryCallback(base::Bind(OnRedirectStubEntry));
177 E : break;
178 : }
179 :
180 : case DLL_THREAD_ATTACH: {
181 E : agent::asan::AsanRuntime* runtime = agent::asan::AsanRuntime::runtime();
182 E : DCHECK_NE(static_cast<agent::asan::AsanRuntime*>(nullptr), runtime);
183 E : runtime->AddThreadId(::GetCurrentThreadId());
184 E : break;
185 : }
186 :
187 : case DLL_THREAD_DETACH:
188 : // Nothing to do here.
189 i : break;
190 :
191 : case DLL_PROCESS_DETACH: {
192 E : base::CommandLine::Reset();
193 : // This should be the last thing called in the agent DLL before it
194 : // gets unloaded. Everything should otherwise have been initialized
195 : // and we're now just cleaning it up again.
196 E : TearDownAsanRuntime(&asan_runtime);
197 E : TearDownAtExitManager();
198 E : break;
199 : }
200 :
201 : default:
202 i : NOTREACHED();
203 : break;
204 : }
205 :
206 E : return TRUE;
207 E : }
208 :
209 : // Enables the deferred free mechanism. This can be called only once per
210 : // execution.
211 i : VOID WINAPI asan_EnableDeferredFreeThread() {
212 i : asan_runtime->EnableDeferredFreeThread();
213 i : }
214 :
215 : // Disables the deferred free mechanism. This must be called before shutdown if
216 : // the thread was started.
217 i : VOID WINAPI asan_DisableDeferredFreeThread() {
218 i : asan_runtime->DisableDeferredFreeThread();
219 i : }
220 :
221 E : void WINAPI asan_EnumExperiments(AsanExperimentCallback callback) {
222 E : DCHECK(callback != nullptr);
223 :
224 : // Under the current implementation, each randomized feature is considered an
225 : // individual experiment, with two groups "Enabled" and "Disabled"
226 E : AsanFeatureSet enabled_features = asan_runtime->GetEnabledFeatureSet();
227 E : for (const auto& feature : kAsanFeatureNames) {
228 E : if (feature.name) {
229 E : const char* state = "Disabled";
230 E : if ((enabled_features & feature.flag) != 0)
231 E : state = "Enabled";
232 E : callback(feature.name, state);
233 E : } else {
234 : // Deprecated features should never be enabled.
235 E : DCHECK_EQ(0U, enabled_features & feature.flag);
236 : }
237 :
238 : // Mask out this feature.
239 E : enabled_features &= ~feature.flag;
240 E : }
241 :
242 : // Check that we had names for all the features.
243 E : DCHECK_EQ(0U, enabled_features);
244 E : }
245 :
246 : } // extern "C"
247 :
248 : } // namespace asan
249 : } // namespace agent
|