summaryrefslogtreecommitdiffstats
path: root/xpcom/build/PoisonIOInterposerWin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/build/PoisonIOInterposerWin.cpp')
-rw-r--r--xpcom/build/PoisonIOInterposerWin.cpp501
1 files changed, 501 insertions, 0 deletions
diff --git a/xpcom/build/PoisonIOInterposerWin.cpp b/xpcom/build/PoisonIOInterposerWin.cpp
new file mode 100644
index 000000000..752743b34
--- /dev/null
+++ b/xpcom/build/PoisonIOInterposerWin.cpp
@@ -0,0 +1,501 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PoisonIOInterposer.h"
+
+#include <algorithm>
+#include <stdio.h>
+#include <vector>
+
+#include <io.h>
+#include <windows.h>
+#include <winternl.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/FileUtilsWin.h"
+#include "mozilla/IOInterposer.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/TimeStamp.h"
+#include "nsTArray.h"
+#include "nsWindowsDllInterceptor.h"
+#include "plstr.h"
+
+#ifdef MOZ_REPLACE_MALLOC
+#include "replace_malloc_bridge.h"
+#endif
+
+using namespace mozilla;
+
+namespace {
+
+// Keep track of poisoned state. Notice that there is no reason to lock access
+// to this variable as it's only changed in InitPoisonIOInterposer and
+// ClearPoisonIOInterposer which may only be called on the main-thread when no
+// other threads are running.
+static bool sIOPoisoned = false;
+
+/************************ Internal NT API Declarations ************************/
+
+/*
+ * Function pointer declaration for internal NT routine to create/open files.
+ * For documentation on the NtCreateFile routine, see MSDN.
+ */
+typedef NTSTATUS (NTAPI* NtCreateFileFn)(
+ PHANDLE aFileHandle,
+ ACCESS_MASK aDesiredAccess,
+ POBJECT_ATTRIBUTES aObjectAttributes,
+ PIO_STATUS_BLOCK aIoStatusBlock,
+ PLARGE_INTEGER aAllocationSize,
+ ULONG aFileAttributes,
+ ULONG aShareAccess,
+ ULONG aCreateDisposition,
+ ULONG aCreateOptions,
+ PVOID aEaBuffer,
+ ULONG aEaLength);
+
+/**
+ * Function pointer declaration for internal NT routine to read data from file.
+ * For documentation on the NtReadFile routine, see ZwReadFile on MSDN.
+ */
+typedef NTSTATUS (NTAPI* NtReadFileFn)(
+ HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ PVOID aBuffer,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey);
+
+/**
+ * Function pointer declaration for internal NT routine to read data from file.
+ * No documentation exists, see wine sources for details.
+ */
+typedef NTSTATUS (NTAPI* NtReadFileScatterFn)(
+ HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ FILE_SEGMENT_ELEMENT* aSegments,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey);
+
+/**
+ * Function pointer declaration for internal NT routine to write data to file.
+ * For documentation on the NtWriteFile routine, see ZwWriteFile on MSDN.
+ */
+typedef NTSTATUS (NTAPI* NtWriteFileFn)(
+ HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ PVOID aBuffer,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey);
+
+/**
+ * Function pointer declaration for internal NT routine to write data to file.
+ * No documentation exists, see wine sources for details.
+ */
+typedef NTSTATUS (NTAPI* NtWriteFileGatherFn)(
+ HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ FILE_SEGMENT_ELEMENT* aSegments,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey);
+
+/**
+ * Function pointer declaration for internal NT routine to flush to disk.
+ * For documentation on the NtFlushBuffersFile routine, see ZwFlushBuffersFile
+ * on MSDN.
+ */
+typedef NTSTATUS (NTAPI* NtFlushBuffersFileFn)(
+ HANDLE aFileHandle,
+ PIO_STATUS_BLOCK aIoStatusBlock);
+
+typedef struct _FILE_NETWORK_OPEN_INFORMATION* PFILE_NETWORK_OPEN_INFORMATION;
+/**
+ * Function pointer delaration for internal NT routine to query file attributes.
+ * (equivalent to stat)
+ */
+typedef NTSTATUS (NTAPI* NtQueryFullAttributesFileFn)(
+ POBJECT_ATTRIBUTES aObjectAttributes,
+ PFILE_NETWORK_OPEN_INFORMATION aFileInformation);
+
+/*************************** Auxiliary Declarations ***************************/
+
+/**
+ * RAII class for timing the duration of an I/O call and reporting the result
+ * to the IOInterposeObserver API.
+ */
+class WinIOAutoObservation : public IOInterposeObserver::Observation
+{
+public:
+ WinIOAutoObservation(IOInterposeObserver::Operation aOp,
+ HANDLE aFileHandle, const LARGE_INTEGER* aOffset)
+ : IOInterposeObserver::Observation(
+ aOp, sReference, !IsDebugFile(reinterpret_cast<intptr_t>(aFileHandle)))
+ , mFileHandle(aFileHandle)
+ , mHasQueriedFilename(false)
+ , mFilename(nullptr)
+ {
+ if (mShouldReport) {
+ mOffset.QuadPart = aOffset ? aOffset->QuadPart : 0;
+ }
+ }
+
+ WinIOAutoObservation(IOInterposeObserver::Operation aOp, nsAString& aFilename)
+ : IOInterposeObserver::Observation(aOp, sReference)
+ , mFileHandle(nullptr)
+ , mHasQueriedFilename(false)
+ , mFilename(nullptr)
+ {
+ if (mShouldReport) {
+ nsAutoString dosPath;
+ if (NtPathToDosPath(aFilename, dosPath)) {
+ mFilename = ToNewUnicode(dosPath);
+ mHasQueriedFilename = true;
+ }
+ mOffset.QuadPart = 0;
+ }
+ }
+
+ // Custom implementation of IOInterposeObserver::Observation::Filename
+ const char16_t* Filename() override;
+
+ ~WinIOAutoObservation()
+ {
+ Report();
+ if (mFilename) {
+ MOZ_ASSERT(mHasQueriedFilename);
+ free(mFilename);
+ mFilename = nullptr;
+ }
+ }
+
+private:
+ HANDLE mFileHandle;
+ LARGE_INTEGER mOffset;
+ bool mHasQueriedFilename;
+ char16_t* mFilename;
+ static const char* sReference;
+};
+
+const char* WinIOAutoObservation::sReference = "PoisonIOInterposer";
+
+// Get filename for this observation
+const char16_t*
+WinIOAutoObservation::Filename()
+{
+ // If mHasQueriedFilename is true, then filename is already stored in mFilename
+ if (mHasQueriedFilename) {
+ return mFilename;
+ }
+
+ nsAutoString utf16Filename;
+ if (HandleToFilename(mFileHandle, mOffset, utf16Filename)) {
+ // Heap allocate with leakable memory
+ mFilename = ToNewUnicode(utf16Filename);
+ }
+ mHasQueriedFilename = true;
+
+ // Return filename
+ return mFilename;
+}
+
+/*************************** IO Interposing Methods ***************************/
+
+// Function pointers to original functions
+static NtCreateFileFn gOriginalNtCreateFile;
+static NtReadFileFn gOriginalNtReadFile;
+static NtReadFileScatterFn gOriginalNtReadFileScatter;
+static NtWriteFileFn gOriginalNtWriteFile;
+static NtWriteFileGatherFn gOriginalNtWriteFileGather;
+static NtFlushBuffersFileFn gOriginalNtFlushBuffersFile;
+static NtQueryFullAttributesFileFn gOriginalNtQueryFullAttributesFile;
+
+static NTSTATUS NTAPI
+InterposedNtCreateFile(PHANDLE aFileHandle,
+ ACCESS_MASK aDesiredAccess,
+ POBJECT_ATTRIBUTES aObjectAttributes,
+ PIO_STATUS_BLOCK aIoStatusBlock,
+ PLARGE_INTEGER aAllocationSize,
+ ULONG aFileAttributes,
+ ULONG aShareAccess,
+ ULONG aCreateDisposition,
+ ULONG aCreateOptions,
+ PVOID aEaBuffer,
+ ULONG aEaLength)
+{
+ // Report IO
+ const wchar_t* buf =
+ aObjectAttributes ? aObjectAttributes->ObjectName->Buffer : L"";
+ uint32_t len =
+ aObjectAttributes ? aObjectAttributes->ObjectName->Length / sizeof(WCHAR) :
+ 0;
+ nsDependentSubstring filename(buf, len);
+ WinIOAutoObservation timer(IOInterposeObserver::OpCreateOrOpen, filename);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtCreateFile);
+
+ // Execute original function
+ return gOriginalNtCreateFile(aFileHandle,
+ aDesiredAccess,
+ aObjectAttributes,
+ aIoStatusBlock,
+ aAllocationSize,
+ aFileAttributes,
+ aShareAccess,
+ aCreateDisposition,
+ aCreateOptions,
+ aEaBuffer,
+ aEaLength);
+}
+
+static NTSTATUS NTAPI
+InterposedNtReadFile(HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ PVOID aBuffer,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey)
+{
+ // Report IO
+ WinIOAutoObservation timer(IOInterposeObserver::OpRead, aFileHandle, aOffset);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtReadFile);
+
+ // Execute original function
+ return gOriginalNtReadFile(aFileHandle,
+ aEvent,
+ aApc,
+ aApcCtx,
+ aIoStatus,
+ aBuffer,
+ aLength,
+ aOffset,
+ aKey);
+}
+
+static NTSTATUS NTAPI
+InterposedNtReadFileScatter(HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ FILE_SEGMENT_ELEMENT* aSegments,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey)
+{
+ // Report IO
+ WinIOAutoObservation timer(IOInterposeObserver::OpRead, aFileHandle, aOffset);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtReadFileScatter);
+
+ // Execute original function
+ return gOriginalNtReadFileScatter(aFileHandle,
+ aEvent,
+ aApc,
+ aApcCtx,
+ aIoStatus,
+ aSegments,
+ aLength,
+ aOffset,
+ aKey);
+}
+
+// Interposed NtWriteFile function
+static NTSTATUS NTAPI
+InterposedNtWriteFile(HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ PVOID aBuffer,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey)
+{
+ // Report IO
+ WinIOAutoObservation timer(IOInterposeObserver::OpWrite, aFileHandle,
+ aOffset);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtWriteFile);
+
+ // Execute original function
+ return gOriginalNtWriteFile(aFileHandle,
+ aEvent,
+ aApc,
+ aApcCtx,
+ aIoStatus,
+ aBuffer,
+ aLength,
+ aOffset,
+ aKey);
+}
+
+// Interposed NtWriteFileGather function
+static NTSTATUS NTAPI
+InterposedNtWriteFileGather(HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ FILE_SEGMENT_ELEMENT* aSegments,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey)
+{
+ // Report IO
+ WinIOAutoObservation timer(IOInterposeObserver::OpWrite, aFileHandle,
+ aOffset);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtWriteFileGather);
+
+ // Execute original function
+ return gOriginalNtWriteFileGather(aFileHandle,
+ aEvent,
+ aApc,
+ aApcCtx,
+ aIoStatus,
+ aSegments,
+ aLength,
+ aOffset,
+ aKey);
+}
+
+static NTSTATUS NTAPI
+InterposedNtFlushBuffersFile(HANDLE aFileHandle,
+ PIO_STATUS_BLOCK aIoStatusBlock)
+{
+ // Report IO
+ WinIOAutoObservation timer(IOInterposeObserver::OpFSync, aFileHandle,
+ nullptr);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtFlushBuffersFile);
+
+ // Execute original function
+ return gOriginalNtFlushBuffersFile(aFileHandle,
+ aIoStatusBlock);
+}
+
+static NTSTATUS NTAPI
+InterposedNtQueryFullAttributesFile(
+ POBJECT_ATTRIBUTES aObjectAttributes,
+ PFILE_NETWORK_OPEN_INFORMATION aFileInformation)
+{
+ // Report IO
+ const wchar_t* buf =
+ aObjectAttributes ? aObjectAttributes->ObjectName->Buffer : L"";
+ uint32_t len =
+ aObjectAttributes ? aObjectAttributes->ObjectName->Length / sizeof(WCHAR) :
+ 0;
+ nsDependentSubstring filename(buf, len);
+ WinIOAutoObservation timer(IOInterposeObserver::OpStat, filename);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtQueryFullAttributesFile);
+
+ // Execute original function
+ return gOriginalNtQueryFullAttributesFile(aObjectAttributes,
+ aFileInformation);
+}
+
+} // namespace
+
+/******************************** IO Poisoning ********************************/
+
+// Windows DLL interceptor
+static WindowsDllInterceptor sNtDllInterceptor;
+
+namespace mozilla {
+
+void
+InitPoisonIOInterposer()
+{
+ // Don't poison twice... as this function may only be invoked on the main
+ // thread when no other threads are running, it safe to allow multiple calls
+ // to InitPoisonIOInterposer() without complaining (ie. failing assertions).
+ if (sIOPoisoned) {
+ return;
+ }
+ sIOPoisoned = true;
+
+ // Stdout and Stderr are OK.
+ MozillaRegisterDebugFD(1);
+ MozillaRegisterDebugFD(2);
+
+#ifdef MOZ_REPLACE_MALLOC
+ // The contract with InitDebugFd is that the given registry can be used
+ // at any moment, so the instance needs to persist longer than the scope
+ // of this functions.
+ static DebugFdRegistry registry;
+ ReplaceMalloc::InitDebugFd(registry);
+#endif
+
+ // Initialize dll interceptor and add hooks
+ sNtDllInterceptor.Init("ntdll.dll");
+ sNtDllInterceptor.AddHook(
+ "NtCreateFile",
+ reinterpret_cast<intptr_t>(InterposedNtCreateFile),
+ reinterpret_cast<void**>(&gOriginalNtCreateFile));
+ sNtDllInterceptor.AddHook(
+ "NtReadFile",
+ reinterpret_cast<intptr_t>(InterposedNtReadFile),
+ reinterpret_cast<void**>(&gOriginalNtReadFile));
+ sNtDllInterceptor.AddHook(
+ "NtReadFileScatter",
+ reinterpret_cast<intptr_t>(InterposedNtReadFileScatter),
+ reinterpret_cast<void**>(&gOriginalNtReadFileScatter));
+ sNtDllInterceptor.AddHook(
+ "NtWriteFile",
+ reinterpret_cast<intptr_t>(InterposedNtWriteFile),
+ reinterpret_cast<void**>(&gOriginalNtWriteFile));
+ sNtDllInterceptor.AddHook(
+ "NtWriteFileGather",
+ reinterpret_cast<intptr_t>(InterposedNtWriteFileGather),
+ reinterpret_cast<void**>(&gOriginalNtWriteFileGather));
+ sNtDllInterceptor.AddHook(
+ "NtFlushBuffersFile",
+ reinterpret_cast<intptr_t>(InterposedNtFlushBuffersFile),
+ reinterpret_cast<void**>(&gOriginalNtFlushBuffersFile));
+ sNtDllInterceptor.AddHook(
+ "NtQueryFullAttributesFile",
+ reinterpret_cast<intptr_t>(InterposedNtQueryFullAttributesFile),
+ reinterpret_cast<void**>(&gOriginalNtQueryFullAttributesFile));
+}
+
+void
+ClearPoisonIOInterposer()
+{
+ MOZ_ASSERT(false);
+ if (sIOPoisoned) {
+ // Destroy the DLL interceptor
+ sIOPoisoned = false;
+ sNtDllInterceptor = WindowsDllInterceptor();
+ }
+}
+
+} // namespace mozilla