Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(186)

Side by Side Diff: chrome/common/conflicts/module_watcher_win.cc

Issue 2473783005: [Win] Create ModuleWatcher. (Closed)
Patch Set: Small cleanup. Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/common/conflicts/module_watcher_win.h"
6
7 #include <windows.h>
8 #include <tlhelp32.h>
9 #include <winternl.h> // For UNICODE_STRING.
10
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/win/scoped_handle.h"
13
14 namespace {
15
16 // Global lock for ensuring synchronization of destruction and notifications.
17 base::Lock module_watcher_lock;
grt (UTC plus 2) 2016/11/10 08:52:12 you need to use a leaky lazy instance here to avoi
grt (UTC plus 2) 2016/11/10 08:52:12 nit: g_foo here and below
chrisha 2016/11/14 16:06:44 Done.
chrisha 2016/11/14 16:06:45 Done.
18 // Global pointer to the singleton ModuleWatcher, if one exists. Under
19 // |module_watcher_lock|.
20 ModuleWatcher* module_watcher_instance = nullptr;
21
22 // These structures and functions are documented in MSDN, see
23 // http://msdn.microsoft.com/en-us/library/gg547638(v=vs.85).aspx
24 // there are however no headers or import libraries available in the
25 // Platform SDK.
26 enum {
27 // The DLL was loaded. The NotificationData parameter points to a
28 // LDR_DLL_LOADED_NOTIFICATION_DATA structure.
29 LDR_DLL_NOTIFICATION_REASON_LOADED = 1,
30 // The DLL was unloaded. The NotificationData parameter points to a
31 // LDR_DLL_UNLOADED_NOTIFICATION_DATA structure.
32 LDR_DLL_NOTIFICATION_REASON_UNLOADED = 2,
33 };
34
35 // Structure that is used for module load notifications.
36 typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA {
37 // Reserved.
38 ULONG Flags;
39 // The full path name of the DLL module.
40 PCUNICODE_STRING FullDllName;
41 // The base file name of the DLL module.
42 PCUNICODE_STRING BaseDllName;
43 // A pointer to the base address for the DLL in memory.
44 PVOID DllBase;
45 // The size of the DLL image, in bytes.
46 ULONG SizeOfImage;
47 } LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA;
48
49 // Structure that is used for module unload notifications.
50 typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA {
51 // Reserved.
52 ULONG Flags;
53 // The full path name of the DLL module.
54 PCUNICODE_STRING FullDllName;
55 // The base file name of the DLL module.
56 PCUNICODE_STRING BaseDllName;
57 // A pointer to the base address for the DLL in memory.
58 PVOID DllBase;
59 // The size of the DLL image, in bytes.
60 ULONG SizeOfImage;
61 } LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA;
62
63 // Union that is used for notifications.
64 typedef union _LDR_DLL_NOTIFICATION_DATA {
65 LDR_DLL_LOADED_NOTIFICATION_DATA Loaded;
66 LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded;
67 } LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA;
68
69 // Signature of the notification callback function.
70 using PLDR_DLL_NOTIFICATION_FUNCTION =
71 VOID(CALLBACK*)(ULONG notification_reason,
72 const LDR_DLL_NOTIFICATION_DATA* notification_data,
73 PVOID context);
74
75 // Signatures of the functions used for registering DLL notification callbacks.
76 using LdrRegisterDllNotificationFunc =
77 NTSTATUS(NTAPI*)(ULONG flags,
78 PLDR_DLL_NOTIFICATION_FUNCTION notification_function,
79 PVOID context,
80 PVOID* cookie);
81 using LdrUnregisterDllNotificationFunc = NTSTATUS(NTAPI*)(PVOID cookie);
82
83 // Names of the DLL notification registration functions. These are exported by
84 // ntdll.
85 constexpr wchar_t kNtDll[] = L"ntdll.dll";
86 constexpr char kLdrRegisterDllNotification[] = "LdrRegisterDllNotification";
87 constexpr char kLdrUnregisterDllNotification[] = "LdrUnregisterDllNotification";
88
89 // Helper function for converting a UNICODE_STRING to a UTF8 std::string.
90 std::string ToString(const UNICODE_STRING* str) {
91 std::string s;
92 base::WideToUTF8(str->Buffer, str->Length / sizeof(wchar_t), &s);
93 return s;
94 }
95
96 // static
grt (UTC plus 2) 2016/11/10 08:52:12 remove
chrisha 2016/11/14 16:06:45 Done.
97 template <typename NotificationDataType>
98 void OnModuleEvent(mojom::ModuleEventType event_type,
99 const NotificationDataType& notification_data,
100 const ModuleWatcher::OnModuleEventCallback& callback) {
101 mojom::ModuleEvent event;
102 event.event_type = event_type;
103 event.module_path = ToString(notification_data.FullDllName);
104 event.load_address = reinterpret_cast<uintptr_t>(notification_data.DllBase);
105 event.size = notification_data.SizeOfImage;
106 callback.Run(event);
107 }
108
109 } // namespace
110
111 // static
112 std::unique_ptr<ModuleWatcher> ModuleWatcher::Create(
113 const OnModuleEventCallback& callback) {
114 // If a ModuleWatcher already exists then bail out.
115 base::AutoLock lock(module_watcher_lock);
116 if (module_watcher_instance)
117 return nullptr;
118
119 // This thread acquired the right to create a ModuleWatcher, so do so.
120 module_watcher_instance = new ModuleWatcher(callback);
121 return std::unique_ptr<ModuleWatcher>(module_watcher_instance);
grt (UTC plus 2) 2016/11/10 08:52:12 nit: return base::WrapUnique<ModuleWatcher>(modu
chrisha 2016/11/14 16:06:45 Done.
122 }
123
124 ModuleWatcher::ModuleWatcher(const OnModuleEventCallback& callback)
grt (UTC plus 2) 2016/11/10 08:52:12 nit: move down below LoaderNotificationCallback to
chrisha 2016/11/14 16:06:45 Done.
125 : callback_(callback) {
126 RegisterDllNotificationCallback();
127 EnumerateAlreadyLoadedModules();
128 }
129
130 ModuleWatcher::~ModuleWatcher() {
131 // As soon as |module_watcher_instance| is null any dispatched callbacks
132 // will be silently absorbed by LoaderNotificationCallback.
133 base::AutoLock lock(module_watcher_lock);
134 DCHECK_EQ(module_watcher_instance, this);
135 module_watcher_instance = nullptr;
136 UnregisterDllNotificationCallback();
137 }
138
139 bool ModuleWatcher::RegisterDllNotificationCallback() {
140 LdrRegisterDllNotificationFunc reg_fn =
141 reinterpret_cast<LdrRegisterDllNotificationFunc>(::GetProcAddress(
142 ::GetModuleHandle(kNtDll), kLdrRegisterDllNotification));
143
144 if (!reg_fn)
145 return false;
146
147 NTSTATUS status = reg_fn(0, reinterpret_cast<PLDR_DLL_NOTIFICATION_FUNCTION>(
148 &ModuleWatcher::LoaderNotificationCallback),
grt (UTC plus 2) 2016/11/10 08:52:12 nit: omit "ModuleWatcher::"
chrisha 2016/11/14 16:06:45 Done.
149 this, &dll_notification_cookie_);
150 return status == 0;
151 }
152
153 bool ModuleWatcher::UnregisterDllNotificationCallback() {
154 LdrUnregisterDllNotificationFunc unreg_fn =
155 reinterpret_cast<LdrUnregisterDllNotificationFunc>(::GetProcAddress(
156 ::GetModuleHandle(kNtDll), kLdrUnregisterDllNotification));
157
158 if (!unreg_fn)
159 return false;
160
161 NTSTATUS status = unreg_fn(dll_notification_cookie_);
162 return status == 0;
163 }
164
165 bool ModuleWatcher::EnumerateAlreadyLoadedModules() {
166 // Get all modules in the current process. According to MSDN,
167 // CreateToolhelp32Snapshot should be retried as long as its returning
168 // ERROR_BAD_LENGTH. To avoid locking up here a retry limit is enforced.
169 base::win::ScopedHandle snap;
170 for (size_t i = 0; i < 5; ++i) {
171 snap.Set(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32,
172 ::GetCurrentProcessId()));
grt (UTC plus 2) 2016/11/09 08:37:52 is GetCurrentProcessId free? if no, move outside o
chrisha 2016/11/14 16:06:45 Done.
173 if (snap.IsValid())
174 break;
175 if (::GetLastError() == ERROR_BAD_LENGTH)
grt (UTC plus 2) 2016/11/09 08:37:52 personal style: i'd invert the logic here and do a
chrisha 2016/11/14 16:06:44 Done.
176 continue;
177 return false;
178 }
179 if (!snap.IsValid())
180 return false;
181
182 // Walk the module list.
183 MODULEENTRY32 module = {sizeof(module)};
184 if (!::Module32First(snap.Get(), &module))
grt (UTC plus 2) 2016/11/09 08:37:52 both functions are documented as handing success/f
chrisha 2016/11/14 16:06:44 Yeah, I initially was CHECKing on these, but obvio
185 return false;
186
187 do {
188 std::string s;
189 base::WideToUTF8(module.szExePath, ::wcslen(module.szExePath), &s);
190
191 mojom::ModuleEvent event;
192 event.event_type = mojom::ModuleEventType::MODULE_ALREADY_LOADED;
193 event.module_path = s;
194 event.load_address = reinterpret_cast<uintptr_t>(module.modBaseAddr);
195 event.size = module.modBaseSize;
196
197 callback_.Run(event);
198 } while (::Module32Next(snap.Get(), &module));
199
200 return true;
201 }
202
203 // static
204 void __stdcall ModuleWatcher::LoaderNotificationCallback(
205 uint32_t notification_reason,
206 const void* notification_data,
207 void* context) {
208 auto* data =
209 reinterpret_cast<const LDR_DLL_NOTIFICATION_DATA*>(notification_data);
210
211 // Get a copy of the callback to invoke.
212 ModuleWatcher* module_watcher = reinterpret_cast<ModuleWatcher*>(context);
grt (UTC plus 2) 2016/11/10 08:52:12 |module_watcher| shouldn't actually be used as one
chrisha 2016/11/14 16:06:45 Great idea! Done.
213 ModuleWatcher::OnModuleEventCallback callback;
grt (UTC plus 2) 2016/11/10 08:52:12 nit: omit "ModuleWatcher::"
chrisha 2016/11/14 16:06:45 Done.
214 {
215 base::AutoLock lock(module_watcher_lock);
216 if (module_watcher_instance != module_watcher)
217 return;
218 callback = module_watcher->callback_;
219 }
220
221 switch (notification_reason) {
222 case LDR_DLL_NOTIFICATION_REASON_LOADED:
223 OnModuleEvent(mojom::ModuleEventType::MODULE_LOADED, data->Loaded,
224 callback);
225 break;
226
227 case LDR_DLL_NOTIFICATION_REASON_UNLOADED:
228 OnModuleEvent(mojom::ModuleEventType::MODULE_UNLOADED, data->Unloaded,
229 callback);
230 break;
231
232 default:
233 // This is unexpected, but not a reason to crash.
234 DCHECK(false) << "Unknown LDR_DLL_NOTIFICATION_REASON: "
grt (UTC plus 2) 2016/11/09 08:37:52 NOTREACHED()
chrisha 2016/11/09 15:38:07 Oops, I thought NOTREACHED was a fatal thing. Didn
235 << notification_reason;
236 }
237 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698