diff -r 000000000000 -r 6474c204b198 xpcom/build/PoisonIOInterposerWin.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xpcom/build/PoisonIOInterposerWin.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,515 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 ci et: */ +/* 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 +#include +#include + +#include +#include +#include + +#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" + +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)( + OUT PHANDLE aFileHandle, + IN ACCESS_MASK aDesiredAccess, + IN POBJECT_ATTRIBUTES aObjectAttributes, + OUT PIO_STATUS_BLOCK aIoStatusBlock, + IN PLARGE_INTEGER aAllocationSize, + IN ULONG aFileAttributes, + IN ULONG aShareAccess, + IN ULONG aCreateDisposition, + IN ULONG aCreateOptions, + IN PVOID aEaBuffer, + IN 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)( + IN HANDLE aFileHandle, + IN HANDLE aEvent, + IN PIO_APC_ROUTINE aApc, + IN PVOID aApcCtx, + OUT PIO_STATUS_BLOCK aIoStatus, + OUT PVOID aBuffer, + IN ULONG aLength, + IN PLARGE_INTEGER aOffset, + IN 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)( + IN HANDLE aFileHandle, + IN HANDLE aEvent, + IN PIO_APC_ROUTINE aApc, + IN PVOID aApcCtx, + OUT PIO_STATUS_BLOCK aIoStatus, + IN FILE_SEGMENT_ELEMENT* aSegments, + IN ULONG aLength, + IN PLARGE_INTEGER aOffset, + IN 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)( + IN HANDLE aFileHandle, + IN HANDLE aEvent, + IN PIO_APC_ROUTINE aApc, + IN PVOID aApcCtx, + OUT PIO_STATUS_BLOCK aIoStatus, + IN PVOID aBuffer, + IN ULONG aLength, + IN PLARGE_INTEGER aOffset, + IN 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)( + IN HANDLE aFileHandle, + IN HANDLE aEvent, + IN PIO_APC_ROUTINE aApc, + IN PVOID aApcCtx, + OUT PIO_STATUS_BLOCK aIoStatus, + IN FILE_SEGMENT_ELEMENT* aSegments, + IN ULONG aLength, + IN PLARGE_INTEGER aOffset, + IN 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)( + IN HANDLE aFileHandle, + OUT 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)( + IN POBJECT_ATTRIBUTES aObjectAttributes, + OUT 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( + 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() MOZ_OVERRIDE; + + ~WinIOAutoObservation() + { + Report(); + if (mFilename) { + MOZ_ASSERT(mHasQueriedFilename); + NS_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 + ); +} + +} // anonymous 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); + + // Initialize dll interceptor and add hooks + sNtDllInterceptor.Init("ntdll.dll"); + sNtDllInterceptor.AddHook( + "NtCreateFile", + reinterpret_cast(InterposedNtCreateFile), + reinterpret_cast(&gOriginalNtCreateFile) + ); + sNtDllInterceptor.AddHook( + "NtReadFile", + reinterpret_cast(InterposedNtReadFile), + reinterpret_cast(&gOriginalNtReadFile) + ); + sNtDllInterceptor.AddHook( + "NtReadFileScatter", + reinterpret_cast(InterposedNtReadFileScatter), + reinterpret_cast(&gOriginalNtReadFileScatter) + ); + sNtDllInterceptor.AddHook( + "NtWriteFile", + reinterpret_cast(InterposedNtWriteFile), + reinterpret_cast(&gOriginalNtWriteFile) + ); + sNtDllInterceptor.AddHook( + "NtWriteFileGather", + reinterpret_cast(InterposedNtWriteFileGather), + reinterpret_cast(&gOriginalNtWriteFileGather) + ); + sNtDllInterceptor.AddHook( + "NtFlushBuffersFile", + reinterpret_cast(InterposedNtFlushBuffersFile), + reinterpret_cast(&gOriginalNtFlushBuffersFile) + ); + sNtDllInterceptor.AddHook( + "NtQueryFullAttributesFile", + reinterpret_cast(InterposedNtQueryFullAttributesFile), + reinterpret_cast(&gOriginalNtQueryFullAttributesFile) + ); +} + +void ClearPoisonIOInterposer() { + MOZ_ASSERT(false); + if (sIOPoisoned) { + // Destroy the DLL interceptor + sIOPoisoned = false; + sNtDllInterceptor = WindowsDllInterceptor(); + } +} + +} // namespace mozilla