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

Unified Diff: chrome/common/conflicts/module_watcher_win.cc

Issue 2473783005: [Win] Create ModuleWatcher. (Closed)
Patch Set: Add conflits_dll for testing. 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 side-by-side diff with in-line comments
Download patch
Index: chrome/common/conflicts/module_watcher_win.cc
diff --git a/chrome/common/conflicts/module_watcher_win.cc b/chrome/common/conflicts/module_watcher_win.cc
new file mode 100644
index 0000000000000000000000000000000000000000..424fd134ec78875c4fae6e7443c1d6fe467579de
--- /dev/null
+++ b/chrome/common/conflicts/module_watcher_win.cc
@@ -0,0 +1,232 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/conflicts/module_watcher_win.h"
+
+#include <windows.h>
+#include <tlhelp32.h>
+#include <winternl.h> // For UNICODE_STRING.
+
+#include <string>
+
+#include "base/lazy_instance.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/lock.h"
+#include "base/win/scoped_handle.h"
+
+// These structures and functions are documented in MSDN, see
+// http://msdn.microsoft.com/en-us/library/gg547638(v=vs.85).aspx
+// there are however no headers or import libraries available in the
+// Platform SDK. They are declared outside of the anonymous namespace to
+// allow them to be forward declared in the header file.
+enum {
+ // The DLL was loaded. The NotificationData parameter points to a
+ // LDR_DLL_LOADED_NOTIFICATION_DATA structure.
+ LDR_DLL_NOTIFICATION_REASON_LOADED = 1,
+ // The DLL was unloaded. The NotificationData parameter points to a
+ // LDR_DLL_UNLOADED_NOTIFICATION_DATA structure.
+ LDR_DLL_NOTIFICATION_REASON_UNLOADED = 2,
+};
+
+// Structure that is used for module load notifications.
+struct LDR_DLL_LOADED_NOTIFICATION_DATA {
+ // Reserved.
+ ULONG Flags;
+ // The full path name of the DLL module.
+ PCUNICODE_STRING FullDllName;
+ // The base file name of the DLL module.
+ PCUNICODE_STRING BaseDllName;
+ // A pointer to the base address for the DLL in memory.
+ PVOID DllBase;
+ // The size of the DLL image, in bytes.
+ ULONG SizeOfImage;
+};
+using PLDR_DLL_LOADED_NOTIFICATION_DATA = LDR_DLL_LOADED_NOTIFICATION_DATA*;
+
+// Structure that is used for module unload notifications.
+struct LDR_DLL_UNLOADED_NOTIFICATION_DATA {
+ // Reserved.
+ ULONG Flags;
+ // The full path name of the DLL module.
+ PCUNICODE_STRING FullDllName;
+ // The base file name of the DLL module.
+ PCUNICODE_STRING BaseDllName;
+ // A pointer to the base address for the DLL in memory.
+ PVOID DllBase;
+ // The size of the DLL image, in bytes.
+ ULONG SizeOfImage;
+};
+using PLDR_DLL_UNLOADED_NOTIFICATION_DATA = LDR_DLL_UNLOADED_NOTIFICATION_DATA*;
+
+// Union that is used for notifications.
+union LDR_DLL_NOTIFICATION_DATA {
+ LDR_DLL_LOADED_NOTIFICATION_DATA Loaded;
+ LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded;
+};
+using PLDR_DLL_NOTIFICATION_DATA = LDR_DLL_NOTIFICATION_DATA*;
+
+// Signature of the notification callback function.
+using PLDR_DLL_NOTIFICATION_FUNCTION =
+ VOID(CALLBACK*)(ULONG notification_reason,
+ const LDR_DLL_NOTIFICATION_DATA* notification_data,
+ PVOID context);
+
+// Signatures of the functions used for registering DLL notification callbacks.
+using LdrRegisterDllNotificationFunc =
+ NTSTATUS(NTAPI*)(ULONG flags,
+ PLDR_DLL_NOTIFICATION_FUNCTION notification_function,
+ PVOID context,
+ PVOID* cookie);
+using LdrUnregisterDllNotificationFunc = NTSTATUS(NTAPI*)(PVOID cookie);
+
+namespace {
+
+// Global lock for ensuring synchronization of destruction and notifications.
+base::LazyInstance<base::Lock>::Leaky g_module_watcher_lock =
+ LAZY_INSTANCE_INITIALIZER;
+// Global pointer to the singleton ModuleWatcher, if one exists. Under
+// |module_watcher_lock|.
+ModuleWatcher* g_module_watcher_instance = nullptr;
+
+// Names of the DLL notification registration functions. These are exported by
+// ntdll.
+constexpr wchar_t kNtDll[] = L"ntdll.dll";
+constexpr char kLdrRegisterDllNotification[] = "LdrRegisterDllNotification";
+constexpr char kLdrUnregisterDllNotification[] = "LdrUnregisterDllNotification";
+
+// Helper function for converting a UNICODE_STRING to a UTF8 std::string.
+std::string ToString(const UNICODE_STRING* str) {
+ std::string s;
+ base::WideToUTF8(str->Buffer, str->Length / sizeof(wchar_t), &s);
+ return s;
+}
+
+template <typename NotificationDataType>
+void OnModuleEvent(mojom::ModuleEventType event_type,
+ const NotificationDataType& notification_data,
+ const ModuleWatcher::OnModuleEventCallback& callback) {
+ mojom::ModuleEvent event;
+ event.event_type = event_type;
+ event.module_path = ToString(notification_data.FullDllName);
+ event.load_address = reinterpret_cast<uintptr_t>(notification_data.DllBase);
+ event.size = notification_data.SizeOfImage;
+ callback.Run(event);
+}
+
+} // namespace
+
+// static
+std::unique_ptr<ModuleWatcher> ModuleWatcher::Create(
+ const OnModuleEventCallback& callback) {
+ // If a ModuleWatcher already exists then bail out.
+ base::AutoLock lock(g_module_watcher_lock.Get());
+ if (g_module_watcher_instance)
+ return nullptr;
+
+ // This thread acquired the right to create a ModuleWatcher, so do so.
+ g_module_watcher_instance = new ModuleWatcher(callback);
+ return base::WrapUnique(g_module_watcher_instance);
+}
+
+ModuleWatcher::~ModuleWatcher() {
+ // As soon as |g_module_watcher_instance| is null any dispatched callbacks
+ // will be silently absorbed by LoaderNotificationCallback.
+ base::AutoLock lock(g_module_watcher_lock.Get());
+ DCHECK_EQ(g_module_watcher_instance, this);
+ g_module_watcher_instance = nullptr;
+ UnregisterDllNotificationCallback();
+}
+
+void ModuleWatcher::RegisterDllNotificationCallback() {
+ LdrRegisterDllNotificationFunc reg_fn =
+ reinterpret_cast<LdrRegisterDllNotificationFunc>(::GetProcAddress(
+ ::GetModuleHandle(kNtDll), kLdrRegisterDllNotification));
+ if (reg_fn)
+ reg_fn(0, &LoaderNotificationCallback, this, &dll_notification_cookie_);
+}
+
+void ModuleWatcher::UnregisterDllNotificationCallback() {
+ LdrUnregisterDllNotificationFunc unreg_fn =
+ reinterpret_cast<LdrUnregisterDllNotificationFunc>(::GetProcAddress(
+ ::GetModuleHandle(kNtDll), kLdrUnregisterDllNotification));
+ if (unreg_fn)
+ unreg_fn(dll_notification_cookie_);
+}
+
+void ModuleWatcher::EnumerateAlreadyLoadedModules() {
+ // Get all modules in the current process. According to MSDN,
+ // CreateToolhelp32Snapshot should be retried as long as its returning
+ // ERROR_BAD_LENGTH. To avoid locking up here a retry limit is enforced.
+ base::win::ScopedHandle snap;
+ DWORD process_id = ::GetCurrentProcessId();
+ for (size_t i = 0; i < 5; ++i) {
+ snap.Set(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32,
+ process_id));
+ if (snap.IsValid())
+ break;
+ if (::GetLastError() != ERROR_BAD_LENGTH)
+ return;
+ }
+ if (!snap.IsValid())
+ return;
+
+ // Walk the module list.
+ MODULEENTRY32 module = {sizeof(module)};
+ std::string path;
+ for (BOOL result = ::Module32First(snap.Get(), &module); result != FALSE;
+ result = ::Module32Next(snap.Get(), &module)) {
+ base::WideToUTF8(module.szExePath, ::wcslen(module.szExePath), &path);
+ mojom::ModuleEvent event;
+ event.event_type = mojom::ModuleEventType::MODULE_ALREADY_LOADED;
+ event.module_path = path;
+ event.load_address = reinterpret_cast<uintptr_t>(module.modBaseAddr);
+ event.size = module.modBaseSize;
+ callback_.Run(event);
+ }
+
+ return;
+}
+
+// static
+ModuleWatcher::OnModuleEventCallback ModuleWatcher::GetCallbackForContext(
+ void* context) {
+ base::AutoLock lock(g_module_watcher_lock.Get());
+ if (context != g_module_watcher_instance)
+ return OnModuleEventCallback();
+ return g_module_watcher_instance->callback_;
+}
+
+// static
+void __stdcall ModuleWatcher::LoaderNotificationCallback(
+ unsigned long notification_reason,
+ const LDR_DLL_NOTIFICATION_DATA* notification_data,
+ void* context) {
+ auto callback = GetCallbackForContext(context);
+ if (!callback)
+ return;
+
+ switch (notification_reason) {
+ case LDR_DLL_NOTIFICATION_REASON_LOADED:
+ OnModuleEvent(mojom::ModuleEventType::MODULE_LOADED,
+ notification_data->Loaded, callback);
+ break;
+
+ case LDR_DLL_NOTIFICATION_REASON_UNLOADED:
+ OnModuleEvent(mojom::ModuleEventType::MODULE_UNLOADED,
+ notification_data->Unloaded, callback);
+ break;
+
+ default:
+ // This is unexpected, but not a reason to crash.
+ NOTREACHED() << "Unknown LDR_DLL_NOTIFICATION_REASON: "
+ << notification_reason;
+ }
+}
+
+ModuleWatcher::ModuleWatcher(const OnModuleEventCallback& callback)
+ : callback_(callback) {
+ RegisterDllNotificationCallback();
+ EnumerateAlreadyLoadedModules();
+}
« no previous file with comments | « chrome/common/conflicts/module_watcher_win.h ('k') | chrome/common/conflicts/module_watcher_win_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698