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 m : namespace agent {
31 m : namespace asan {
32 m : namespace {
33 :
34 : // This lock guards against IAT patching on multiple threads concurrently.
35 m : base::Lock patch_lock;
36 :
37 : // Our AtExit manager required by base.
38 m : base::AtExitManager* at_exit = nullptr;
39 :
40 : // The asan runtime manager.
41 m : AsanRuntime* asan_runtime = nullptr;
42 :
43 m : void SetUpAtExitManager() {
44 m : DCHECK_EQ(static_cast<base::AtExitManager*>(nullptr), at_exit);
45 m : at_exit = new base::AtExitManager();
46 m : CHECK_NE(static_cast<base::AtExitManager*>(nullptr), at_exit);
47 m : }
48 :
49 m : void TearDownAtExitManager() {
50 m : DCHECK_NE(static_cast<base::AtExitManager*>(nullptr), at_exit);
51 m : delete at_exit;
52 m : at_exit = nullptr;
53 m : }
54 :
55 m : MemoryAccessorMode SelectMemoryAccessorMode() {
56 m : static uint64_t kOneGB = 1ull << 30;
57 :
58 : // If there is no runtime then use the noop probes.
59 m : if (asan_runtime == nullptr)
60 m : return MEMORY_ACCESSOR_MODE_NOOP;
61 :
62 : // Determine the amount of shadow memory allocated.
63 m : uint64_t gb = asan_runtime->shadow()->length();
64 m : gb <<= kShadowRatioLog;
65 m : gb /= kOneGB;
66 :
67 m : switch (gb) {
68 m : case 2:
69 m : return MEMORY_ACCESSOR_MODE_2G;
70 m : case 4:
71 m : return MEMORY_ACCESSOR_MODE_4G;
72 : // 1GB should never happen, and 3GB simply isn't properly supported.
73 m : default:
74 m : return MEMORY_ACCESSOR_MODE_NOOP;
75 m : }
76 m : }
77 :
78 m : MemoryAccessorMode OnRedirectStubEntry(const void* caller_address) {
79 : // This grabs the loader's lock, which could be a problem. If there are
80 : // multiple instrumented DLLs, or a single one executing on multiple threads,
81 : // there could be lock inversion here. The possibility seems remote, though.
82 : // Maybe locating the module associated with the caller_address can be done
83 : // with a VirtualQuery, with a fallback to the loader for an additional pair
84 : // of belt-and-suspenders...
85 m : const DWORD kFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
86 m : GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
87 m : HMODULE calling_module = nullptr;
88 m : BOOL success = ::GetModuleHandleEx(
89 m : kFlags, reinterpret_cast<LPCWSTR>(caller_address), &calling_module);
90 m : CHECK_EQ(TRUE, success);
91 :
92 : // TODO(chrisha): Implement logic for selecting the noop mode if the system
93 : // isn't up to par, if so configured by Finch, if the shadow memory
94 : // allocation failed, etc.
95 m : MemoryAccessorMode mode = SelectMemoryAccessorMode();
96 :
97 : // If a runtime has been successfully allocated but for whatever reason the
98 : // noop instrumentation has been selected, then cleanup the runtime
99 : // allocation.
100 m : if (mode == MEMORY_ACCESSOR_MODE_NOOP && asan_runtime != nullptr)
101 m : TearDownAsanRuntime(&asan_runtime);
102 :
103 : // Build the IAT patch map.
104 m : IATPatchMap patch_map;
105 m : for (size_t i = 0; i < kNumMemoryAccessorVariants; ++i) {
106 m : patch_map.insert(
107 m : std::make_pair(kMemoryAccessorVariants[i].name,
108 m : kMemoryAccessorVariants[i].accessors[mode]));
109 m : }
110 :
111 : // Grab the patching lock only while patching the caller's IAT. Assuming no
112 : // other parties are patching this IAT, this is sufficient to make
113 : // double-patching due to multiple threads invoking on instrumentation
114 : // concurrently idempotent.
115 m : base::AutoLock lock(patch_lock);
116 m : CHECK(PatchIATForModule(calling_module, patch_map));
117 :
118 m : return mode;
119 m : }
120 :
121 m : } // namespace
122 :
123 m : extern "C" {
124 :
125 m : BOOL WINAPI DllMain(HMODULE instance, DWORD reason, LPVOID reserved) {
126 m : agent::common::InitializeCrt();
127 :
128 m : switch (reason) {
129 m : case DLL_PROCESS_ATTACH: {
130 : // Create the At-Exit manager.
131 m : SetUpAtExitManager();
132 :
133 : // Disable logging. In the case of Chrome this is running in a sandboxed
134 : // process where logging to file doesn't help us any. In other cases the
135 : // log output will still go to console.
136 m : base::CommandLine::Init(0, NULL);
137 m : ::common::InitLoggingForDll(L"asan");
138 :
139 : // Setup the ASAN runtime. If this fails then |asan_runtime| will remain
140 : // nullptr, and the stub redirection will enable the noop probes.
141 m : SetUpAsanRuntime(&asan_runtime);
142 :
143 : // Hookup IAT patching on redirector stub entry.
144 m : agent::asan::SetRedirectEntryCallback(base::Bind(OnRedirectStubEntry));
145 m : break;
146 m : }
147 :
148 m : case DLL_THREAD_ATTACH: {
149 m : agent::asan::AsanRuntime* runtime = agent::asan::AsanRuntime::runtime();
150 m : DCHECK_NE(static_cast<agent::asan::AsanRuntime*>(nullptr), runtime);
151 m : runtime->AddThreadId(::GetCurrentThreadId());
152 m : break;
153 m : }
154 :
155 m : case DLL_THREAD_DETACH:
156 : // Nothing to do here.
157 m : break;
158 :
159 m : case DLL_PROCESS_DETACH: {
160 m : base::CommandLine::Reset();
161 : // This should be the last thing called in the agent DLL before it
162 : // gets unloaded. Everything should otherwise have been initialized
163 : // and we're now just cleaning it up again.
164 m : TearDownAsanRuntime(&asan_runtime);
165 m : TearDownAtExitManager();
166 m : break;
167 m : }
168 :
169 m : default:
170 m : NOTREACHED();
171 m : break;
172 m : }
173 :
174 m : return TRUE;
175 m : }
176 :
177 m : } // extern "C"
178 :
179 m : } // namespace asan
180 m : } // namespace agent
|