blob: 7ede51c9a8b060445460659f315030257ed6f176 [file] [log] [blame]
// 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 <memory>
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/common/conflicts/module_event_sink_win.mojom.h"
class ModuleWatcherTest;
// This class observes modules as they are loaded into a process's address
// space.
// This class is safe to be created on any thread. Similarly, it is safe to be
// destroyed on any thread, independent of the thread on which the instance was
// created.
class ModuleWatcher {
// Houses information about a module event, and some module metadata.
struct ModuleEvent {
ModuleEvent() = default;
ModuleEvent(const ModuleEvent& other) = default;
ModuleEvent(mojom::ModuleEventType event_type,
const base::FilePath& module_path,
void* module_load_address,
size_t module_size)
: event_type(event_type),
module_size(module_size) {}
// The type of module event.
mojom::ModuleEventType event_type;
// The full path to the module on disk.
base::FilePath module_path;
// The load address of the module. Careful consideration must be made before
// accessing memory at this address. See the comment for
// OnModuleEventCallback.
void* module_load_address;
// The size of the module in memory.
size_t module_size;
// The type of callback that will be invoked for each module event. This
// callback may be run from any thread in the process, and may be invoked
// during initialization (while iterating over already loaded modules) or in
// response to LdrDllNotifications received from the loader. As such, keep the
// amount of work performed here to an absolute minimum.
// MODULE_LOADED events are always dispatched directly from the loader while
// under the loader's lock, so the module is guaranteed to be loaded in memory
// (it is safe to access module_load_address).
// If the event is of type MODULE_ALREADY_LOADED, then the module data comes
// from a snapshot and it is possible that its |module_load_address| is
// invalid by the time the event is sent.
// Note that it is possible for this callback to be invoked after the
// destruction of the watcher.
using OnModuleEventCallback =
base::RepeatingCallback<void(const ModuleEvent& event)>;
// Creates and starts a watcher. This enumerates all loaded modules
// synchronously on the current thread during construction, and provides
// synchronous notifications as modules are loaded. The callback is invoked in
// the context of the thread that is loading a module, and as such may be
// invoked on any thread in the process. Note that it is possible to receive
// two notifications for some modules as the initial loaded module enumeration
// races briefly with the callback mechanism. In this case both a
// MODULE_LOADED and a MODULE_ALREADY_LOADED event will be received for the
// same module. Since the callback is installed first no modules can be
// missed, however. This factory function may be called on any thread.
// Only a single instance of a watcher may exist at any moment. This will
// return nullptr when trying to create a second watcher.
static std::unique_ptr<ModuleWatcher> Create(OnModuleEventCallback callback);
// This can be called on any thread. After destruction the |callback|
// provided to the constructor will no longer be invoked with module events.
// For unittesting.
friend class ModuleWatcherTest;
// Private to enforce Singleton semantics. See Create above.
// Initializes the ModuleWatcher instance.
void Initialize(OnModuleEventCallback callback);
// Registers a DllNotification callback with the OS. Modifies
// |dll_notification_cookie_|. Can be called on any thread.
void RegisterDllNotificationCallback();
// Removes the installed DllNotification callback. Modifies
// |dll_notification_cookie_|. Can be called on any thread.
void UnregisterDllNotificationCallback();
// Enumerates all currently loaded modules, synchronously invoking callbacks
// on the current thread. Can be called on any thread.
static void EnumerateAlreadyLoadedModules(
scoped_refptr<base::SequencedTaskRunner> task_runner,
OnModuleEventCallback callback);
// Helper function for retrieving the callback associated with a given
// LdrNotification context.
static OnModuleEventCallback GetCallbackForContext(void* context);
// The loader notification callback. This is actually
// void CALLBACK LoaderNotificationCallback(
// Not using CALLBACK/DWORD/PVOID allows skipping the windows.h header from
// this file.
static void __stdcall LoaderNotificationCallback(
unsigned long notification_reason,
const LDR_DLL_NOTIFICATION_DATA* notification_data,
void* context);
// Used to bind the |callback_| to a WeakPtr.
void RunCallback(const ModuleEvent& event);
// The current callback. Can end up being invoked on any thread.
OnModuleEventCallback callback_;
// Used by the DllNotification mechanism.
void* dll_notification_cookie_ = nullptr;
base::WeakPtrFactory<ModuleWatcher> weak_ptr_factory_;