1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/build/PoisonIOInterposerWin.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,515 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 ci et: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "PoisonIOInterposer.h" 1.11 + 1.12 +#include <algorithm> 1.13 +#include <stdio.h> 1.14 +#include <vector> 1.15 + 1.16 +#include <io.h> 1.17 +#include <windows.h> 1.18 +#include <winternl.h> 1.19 + 1.20 +#include "mozilla/Assertions.h" 1.21 +#include "mozilla/FileUtilsWin.h" 1.22 +#include "mozilla/IOInterposer.h" 1.23 +#include "mozilla/Mutex.h" 1.24 +#include "mozilla/TimeStamp.h" 1.25 +#include "nsTArray.h" 1.26 +#include "nsWindowsDllInterceptor.h" 1.27 +#include "plstr.h" 1.28 + 1.29 +using namespace mozilla; 1.30 + 1.31 +namespace { 1.32 + 1.33 +// Keep track of poisoned state. Notice that there is no reason to lock access 1.34 +// to this variable as it's only changed in InitPoisonIOInterposer and 1.35 +// ClearPoisonIOInterposer which may only be called on the main-thread when no 1.36 +// other threads are running. 1.37 +static bool sIOPoisoned = false; 1.38 + 1.39 +/************************ Internal NT API Declarations ************************/ 1.40 + 1.41 +/* 1.42 + * Function pointer declaration for internal NT routine to create/open files. 1.43 + * For documentation on the NtCreateFile routine, see MSDN. 1.44 + */ 1.45 +typedef NTSTATUS (NTAPI *NtCreateFileFn)( 1.46 + OUT PHANDLE aFileHandle, 1.47 + IN ACCESS_MASK aDesiredAccess, 1.48 + IN POBJECT_ATTRIBUTES aObjectAttributes, 1.49 + OUT PIO_STATUS_BLOCK aIoStatusBlock, 1.50 + IN PLARGE_INTEGER aAllocationSize, 1.51 + IN ULONG aFileAttributes, 1.52 + IN ULONG aShareAccess, 1.53 + IN ULONG aCreateDisposition, 1.54 + IN ULONG aCreateOptions, 1.55 + IN PVOID aEaBuffer, 1.56 + IN ULONG aEaLength 1.57 +); 1.58 + 1.59 +/** 1.60 + * Function pointer declaration for internal NT routine to read data from file. 1.61 + * For documentation on the NtReadFile routine, see ZwReadFile on MSDN. 1.62 + */ 1.63 +typedef NTSTATUS (NTAPI *NtReadFileFn)( 1.64 + IN HANDLE aFileHandle, 1.65 + IN HANDLE aEvent, 1.66 + IN PIO_APC_ROUTINE aApc, 1.67 + IN PVOID aApcCtx, 1.68 + OUT PIO_STATUS_BLOCK aIoStatus, 1.69 + OUT PVOID aBuffer, 1.70 + IN ULONG aLength, 1.71 + IN PLARGE_INTEGER aOffset, 1.72 + IN PULONG aKey 1.73 +); 1.74 + 1.75 +/** 1.76 + * Function pointer declaration for internal NT routine to read data from file. 1.77 + * No documentation exists, see wine sources for details. 1.78 + */ 1.79 +typedef NTSTATUS (NTAPI* NtReadFileScatterFn)( 1.80 + IN HANDLE aFileHandle, 1.81 + IN HANDLE aEvent, 1.82 + IN PIO_APC_ROUTINE aApc, 1.83 + IN PVOID aApcCtx, 1.84 + OUT PIO_STATUS_BLOCK aIoStatus, 1.85 + IN FILE_SEGMENT_ELEMENT* aSegments, 1.86 + IN ULONG aLength, 1.87 + IN PLARGE_INTEGER aOffset, 1.88 + IN PULONG aKey 1.89 +); 1.90 + 1.91 +/** 1.92 + * Function pointer declaration for internal NT routine to write data to file. 1.93 + * For documentation on the NtWriteFile routine, see ZwWriteFile on MSDN. 1.94 + */ 1.95 +typedef NTSTATUS (NTAPI *NtWriteFileFn)( 1.96 + IN HANDLE aFileHandle, 1.97 + IN HANDLE aEvent, 1.98 + IN PIO_APC_ROUTINE aApc, 1.99 + IN PVOID aApcCtx, 1.100 + OUT PIO_STATUS_BLOCK aIoStatus, 1.101 + IN PVOID aBuffer, 1.102 + IN ULONG aLength, 1.103 + IN PLARGE_INTEGER aOffset, 1.104 + IN PULONG aKey 1.105 +); 1.106 + 1.107 +/** 1.108 + * Function pointer declaration for internal NT routine to write data to file. 1.109 + * No documentation exists, see wine sources for details. 1.110 + */ 1.111 +typedef NTSTATUS (NTAPI *NtWriteFileGatherFn)( 1.112 + IN HANDLE aFileHandle, 1.113 + IN HANDLE aEvent, 1.114 + IN PIO_APC_ROUTINE aApc, 1.115 + IN PVOID aApcCtx, 1.116 + OUT PIO_STATUS_BLOCK aIoStatus, 1.117 + IN FILE_SEGMENT_ELEMENT* aSegments, 1.118 + IN ULONG aLength, 1.119 + IN PLARGE_INTEGER aOffset, 1.120 + IN PULONG aKey 1.121 +); 1.122 + 1.123 +/** 1.124 + * Function pointer declaration for internal NT routine to flush to disk. 1.125 + * For documentation on the NtFlushBuffersFile routine, see ZwFlushBuffersFile 1.126 + * on MSDN. 1.127 + */ 1.128 +typedef NTSTATUS (NTAPI *NtFlushBuffersFileFn)( 1.129 + IN HANDLE aFileHandle, 1.130 + OUT PIO_STATUS_BLOCK aIoStatusBlock 1.131 +); 1.132 + 1.133 +typedef struct _FILE_NETWORK_OPEN_INFORMATION* PFILE_NETWORK_OPEN_INFORMATION; 1.134 +/** 1.135 + * Function pointer delaration for internal NT routine to query file attributes. 1.136 + * (equivalent to stat) 1.137 + */ 1.138 +typedef NTSTATUS (NTAPI *NtQueryFullAttributesFileFn)( 1.139 + IN POBJECT_ATTRIBUTES aObjectAttributes, 1.140 + OUT PFILE_NETWORK_OPEN_INFORMATION aFileInformation 1.141 +); 1.142 + 1.143 +/*************************** Auxiliary Declarations ***************************/ 1.144 + 1.145 +/** 1.146 + * RAII class for timing the duration of an I/O call and reporting the result 1.147 + * to the IOInterposeObserver API. 1.148 + */ 1.149 +class WinIOAutoObservation : public IOInterposeObserver::Observation 1.150 +{ 1.151 +public: 1.152 + WinIOAutoObservation(IOInterposeObserver::Operation aOp, 1.153 + HANDLE aFileHandle, const LARGE_INTEGER* aOffset) 1.154 + : IOInterposeObserver::Observation(aOp, sReference, 1.155 + !IsDebugFile(reinterpret_cast<intptr_t>( 1.156 + aFileHandle))) 1.157 + , mFileHandle(aFileHandle) 1.158 + , mHasQueriedFilename(false) 1.159 + , mFilename(nullptr) 1.160 + { 1.161 + if (mShouldReport) { 1.162 + mOffset.QuadPart = aOffset ? aOffset->QuadPart : 0; 1.163 + } 1.164 + } 1.165 + 1.166 + WinIOAutoObservation(IOInterposeObserver::Operation aOp, nsAString& aFilename) 1.167 + : IOInterposeObserver::Observation(aOp, sReference) 1.168 + , mFileHandle(nullptr) 1.169 + , mHasQueriedFilename(false) 1.170 + , mFilename(nullptr) 1.171 + { 1.172 + if (mShouldReport) { 1.173 + nsAutoString dosPath; 1.174 + if (NtPathToDosPath(aFilename, dosPath)) { 1.175 + mFilename = ToNewUnicode(dosPath); 1.176 + mHasQueriedFilename = true; 1.177 + } 1.178 + mOffset.QuadPart = 0; 1.179 + } 1.180 + } 1.181 + 1.182 + // Custom implementation of IOInterposeObserver::Observation::Filename 1.183 + const char16_t* Filename() MOZ_OVERRIDE; 1.184 + 1.185 + ~WinIOAutoObservation() 1.186 + { 1.187 + Report(); 1.188 + if (mFilename) { 1.189 + MOZ_ASSERT(mHasQueriedFilename); 1.190 + NS_Free(mFilename); 1.191 + mFilename = nullptr; 1.192 + } 1.193 + } 1.194 + 1.195 +private: 1.196 + HANDLE mFileHandle; 1.197 + LARGE_INTEGER mOffset; 1.198 + bool mHasQueriedFilename; 1.199 + char16_t* mFilename; 1.200 + static const char* sReference; 1.201 +}; 1.202 + 1.203 +const char* WinIOAutoObservation::sReference = "PoisonIOInterposer"; 1.204 + 1.205 +// Get filename for this observation 1.206 +const char16_t* WinIOAutoObservation::Filename() 1.207 +{ 1.208 + // If mHasQueriedFilename is true, then filename is already stored in mFilename 1.209 + if (mHasQueriedFilename) { 1.210 + return mFilename; 1.211 + } 1.212 + 1.213 + nsAutoString utf16Filename; 1.214 + if (HandleToFilename(mFileHandle, mOffset, utf16Filename)) { 1.215 + // Heap allocate with leakable memory 1.216 + mFilename = ToNewUnicode(utf16Filename); 1.217 + } 1.218 + mHasQueriedFilename = true; 1.219 + 1.220 + // Return filename 1.221 + return mFilename; 1.222 +} 1.223 + 1.224 +/*************************** IO Interposing Methods ***************************/ 1.225 + 1.226 +// Function pointers to original functions 1.227 +static NtCreateFileFn gOriginalNtCreateFile; 1.228 +static NtReadFileFn gOriginalNtReadFile; 1.229 +static NtReadFileScatterFn gOriginalNtReadFileScatter; 1.230 +static NtWriteFileFn gOriginalNtWriteFile; 1.231 +static NtWriteFileGatherFn gOriginalNtWriteFileGather; 1.232 +static NtFlushBuffersFileFn gOriginalNtFlushBuffersFile; 1.233 +static NtQueryFullAttributesFileFn gOriginalNtQueryFullAttributesFile; 1.234 + 1.235 +static NTSTATUS NTAPI InterposedNtCreateFile( 1.236 + PHANDLE aFileHandle, 1.237 + ACCESS_MASK aDesiredAccess, 1.238 + POBJECT_ATTRIBUTES aObjectAttributes, 1.239 + PIO_STATUS_BLOCK aIoStatusBlock, 1.240 + PLARGE_INTEGER aAllocationSize, 1.241 + ULONG aFileAttributes, 1.242 + ULONG aShareAccess, 1.243 + ULONG aCreateDisposition, 1.244 + ULONG aCreateOptions, 1.245 + PVOID aEaBuffer, 1.246 + ULONG aEaLength 1.247 +) 1.248 +{ 1.249 + // Report IO 1.250 + const wchar_t* buf = aObjectAttributes ? 1.251 + aObjectAttributes->ObjectName->Buffer : 1.252 + L""; 1.253 + uint32_t len = aObjectAttributes ? 1.254 + aObjectAttributes->ObjectName->Length / sizeof(WCHAR) : 1.255 + 0; 1.256 + nsDependentSubstring filename(buf, len); 1.257 + WinIOAutoObservation timer(IOInterposeObserver::OpCreateOrOpen, filename); 1.258 + 1.259 + // Something is badly wrong if this function is undefined 1.260 + MOZ_ASSERT(gOriginalNtCreateFile); 1.261 + 1.262 + // Execute original function 1.263 + return gOriginalNtCreateFile( 1.264 + aFileHandle, 1.265 + aDesiredAccess, 1.266 + aObjectAttributes, 1.267 + aIoStatusBlock, 1.268 + aAllocationSize, 1.269 + aFileAttributes, 1.270 + aShareAccess, 1.271 + aCreateDisposition, 1.272 + aCreateOptions, 1.273 + aEaBuffer, 1.274 + aEaLength 1.275 + ); 1.276 +} 1.277 + 1.278 +static NTSTATUS NTAPI InterposedNtReadFile( 1.279 + HANDLE aFileHandle, 1.280 + HANDLE aEvent, 1.281 + PIO_APC_ROUTINE aApc, 1.282 + PVOID aApcCtx, 1.283 + PIO_STATUS_BLOCK aIoStatus, 1.284 + PVOID aBuffer, 1.285 + ULONG aLength, 1.286 + PLARGE_INTEGER aOffset, 1.287 + PULONG aKey) 1.288 +{ 1.289 + // Report IO 1.290 + WinIOAutoObservation timer(IOInterposeObserver::OpRead, aFileHandle, aOffset); 1.291 + 1.292 + // Something is badly wrong if this function is undefined 1.293 + MOZ_ASSERT(gOriginalNtReadFile); 1.294 + 1.295 + // Execute original function 1.296 + return gOriginalNtReadFile( 1.297 + aFileHandle, 1.298 + aEvent, 1.299 + aApc, 1.300 + aApcCtx, 1.301 + aIoStatus, 1.302 + aBuffer, 1.303 + aLength, 1.304 + aOffset, 1.305 + aKey 1.306 + ); 1.307 +} 1.308 + 1.309 +static NTSTATUS NTAPI InterposedNtReadFileScatter( 1.310 + HANDLE aFileHandle, 1.311 + HANDLE aEvent, 1.312 + PIO_APC_ROUTINE aApc, 1.313 + PVOID aApcCtx, 1.314 + PIO_STATUS_BLOCK aIoStatus, 1.315 + FILE_SEGMENT_ELEMENT* aSegments, 1.316 + ULONG aLength, 1.317 + PLARGE_INTEGER aOffset, 1.318 + PULONG aKey) 1.319 +{ 1.320 + // Report IO 1.321 + WinIOAutoObservation timer(IOInterposeObserver::OpRead, aFileHandle, aOffset); 1.322 + 1.323 + // Something is badly wrong if this function is undefined 1.324 + MOZ_ASSERT(gOriginalNtReadFileScatter); 1.325 + 1.326 + // Execute original function 1.327 + return gOriginalNtReadFileScatter( 1.328 + aFileHandle, 1.329 + aEvent, 1.330 + aApc, 1.331 + aApcCtx, 1.332 + aIoStatus, 1.333 + aSegments, 1.334 + aLength, 1.335 + aOffset, 1.336 + aKey 1.337 + ); 1.338 +} 1.339 + 1.340 +// Interposed NtWriteFile function 1.341 +static NTSTATUS NTAPI InterposedNtWriteFile( 1.342 + HANDLE aFileHandle, 1.343 + HANDLE aEvent, 1.344 + PIO_APC_ROUTINE aApc, 1.345 + PVOID aApcCtx, 1.346 + PIO_STATUS_BLOCK aIoStatus, 1.347 + PVOID aBuffer, 1.348 + ULONG aLength, 1.349 + PLARGE_INTEGER aOffset, 1.350 + PULONG aKey) 1.351 +{ 1.352 + // Report IO 1.353 + WinIOAutoObservation timer(IOInterposeObserver::OpWrite, aFileHandle, 1.354 + aOffset); 1.355 + 1.356 + // Something is badly wrong if this function is undefined 1.357 + MOZ_ASSERT(gOriginalNtWriteFile); 1.358 + 1.359 + // Execute original function 1.360 + return gOriginalNtWriteFile( 1.361 + aFileHandle, 1.362 + aEvent, 1.363 + aApc, 1.364 + aApcCtx, 1.365 + aIoStatus, 1.366 + aBuffer, 1.367 + aLength, 1.368 + aOffset, 1.369 + aKey 1.370 + ); 1.371 +} 1.372 + 1.373 +// Interposed NtWriteFileGather function 1.374 +static NTSTATUS NTAPI InterposedNtWriteFileGather( 1.375 + HANDLE aFileHandle, 1.376 + HANDLE aEvent, 1.377 + PIO_APC_ROUTINE aApc, 1.378 + PVOID aApcCtx, 1.379 + PIO_STATUS_BLOCK aIoStatus, 1.380 + FILE_SEGMENT_ELEMENT* aSegments, 1.381 + ULONG aLength, 1.382 + PLARGE_INTEGER aOffset, 1.383 + PULONG aKey) 1.384 +{ 1.385 + // Report IO 1.386 + WinIOAutoObservation timer(IOInterposeObserver::OpWrite, aFileHandle, 1.387 + aOffset); 1.388 + 1.389 + // Something is badly wrong if this function is undefined 1.390 + MOZ_ASSERT(gOriginalNtWriteFileGather); 1.391 + 1.392 + // Execute original function 1.393 + return gOriginalNtWriteFileGather( 1.394 + aFileHandle, 1.395 + aEvent, 1.396 + aApc, 1.397 + aApcCtx, 1.398 + aIoStatus, 1.399 + aSegments, 1.400 + aLength, 1.401 + aOffset, 1.402 + aKey 1.403 + ); 1.404 +} 1.405 + 1.406 +static NTSTATUS NTAPI InterposedNtFlushBuffersFile( 1.407 + HANDLE aFileHandle, 1.408 + PIO_STATUS_BLOCK aIoStatusBlock) 1.409 +{ 1.410 + // Report IO 1.411 + WinIOAutoObservation timer(IOInterposeObserver::OpFSync, aFileHandle, 1.412 + nullptr); 1.413 + 1.414 + // Something is badly wrong if this function is undefined 1.415 + MOZ_ASSERT(gOriginalNtFlushBuffersFile); 1.416 + 1.417 + // Execute original function 1.418 + return gOriginalNtFlushBuffersFile( 1.419 + aFileHandle, 1.420 + aIoStatusBlock 1.421 + ); 1.422 +} 1.423 + 1.424 +static NTSTATUS NTAPI InterposedNtQueryFullAttributesFile( 1.425 + POBJECT_ATTRIBUTES aObjectAttributes, 1.426 + PFILE_NETWORK_OPEN_INFORMATION aFileInformation) 1.427 +{ 1.428 + // Report IO 1.429 + const wchar_t* buf = aObjectAttributes ? 1.430 + aObjectAttributes->ObjectName->Buffer : 1.431 + L""; 1.432 + uint32_t len = aObjectAttributes ? 1.433 + aObjectAttributes->ObjectName->Length / sizeof(WCHAR) : 1.434 + 0; 1.435 + nsDependentSubstring filename(buf, len); 1.436 + WinIOAutoObservation timer(IOInterposeObserver::OpStat, filename); 1.437 + 1.438 + // Something is badly wrong if this function is undefined 1.439 + MOZ_ASSERT(gOriginalNtQueryFullAttributesFile); 1.440 + 1.441 + // Execute original function 1.442 + return gOriginalNtQueryFullAttributesFile( 1.443 + aObjectAttributes, 1.444 + aFileInformation 1.445 + ); 1.446 +} 1.447 + 1.448 +} // anonymous namespace 1.449 + 1.450 +/******************************** IO Poisoning ********************************/ 1.451 + 1.452 +// Windows DLL interceptor 1.453 +static WindowsDllInterceptor sNtDllInterceptor; 1.454 + 1.455 +namespace mozilla { 1.456 + 1.457 +void InitPoisonIOInterposer() { 1.458 + // Don't poison twice... as this function may only be invoked on the main 1.459 + // thread when no other threads are running, it safe to allow multiple calls 1.460 + // to InitPoisonIOInterposer() without complaining (ie. failing assertions). 1.461 + if (sIOPoisoned) { 1.462 + return; 1.463 + } 1.464 + sIOPoisoned = true; 1.465 + 1.466 + // Stdout and Stderr are OK. 1.467 + MozillaRegisterDebugFD(1); 1.468 + MozillaRegisterDebugFD(2); 1.469 + 1.470 + // Initialize dll interceptor and add hooks 1.471 + sNtDllInterceptor.Init("ntdll.dll"); 1.472 + sNtDllInterceptor.AddHook( 1.473 + "NtCreateFile", 1.474 + reinterpret_cast<intptr_t>(InterposedNtCreateFile), 1.475 + reinterpret_cast<void**>(&gOriginalNtCreateFile) 1.476 + ); 1.477 + sNtDllInterceptor.AddHook( 1.478 + "NtReadFile", 1.479 + reinterpret_cast<intptr_t>(InterposedNtReadFile), 1.480 + reinterpret_cast<void**>(&gOriginalNtReadFile) 1.481 + ); 1.482 + sNtDllInterceptor.AddHook( 1.483 + "NtReadFileScatter", 1.484 + reinterpret_cast<intptr_t>(InterposedNtReadFileScatter), 1.485 + reinterpret_cast<void**>(&gOriginalNtReadFileScatter) 1.486 + ); 1.487 + sNtDllInterceptor.AddHook( 1.488 + "NtWriteFile", 1.489 + reinterpret_cast<intptr_t>(InterposedNtWriteFile), 1.490 + reinterpret_cast<void**>(&gOriginalNtWriteFile) 1.491 + ); 1.492 + sNtDllInterceptor.AddHook( 1.493 + "NtWriteFileGather", 1.494 + reinterpret_cast<intptr_t>(InterposedNtWriteFileGather), 1.495 + reinterpret_cast<void**>(&gOriginalNtWriteFileGather) 1.496 + ); 1.497 + sNtDllInterceptor.AddHook( 1.498 + "NtFlushBuffersFile", 1.499 + reinterpret_cast<intptr_t>(InterposedNtFlushBuffersFile), 1.500 + reinterpret_cast<void**>(&gOriginalNtFlushBuffersFile) 1.501 + ); 1.502 + sNtDllInterceptor.AddHook( 1.503 + "NtQueryFullAttributesFile", 1.504 + reinterpret_cast<intptr_t>(InterposedNtQueryFullAttributesFile), 1.505 + reinterpret_cast<void**>(&gOriginalNtQueryFullAttributesFile) 1.506 + ); 1.507 +} 1.508 + 1.509 +void ClearPoisonIOInterposer() { 1.510 + MOZ_ASSERT(false); 1.511 + if (sIOPoisoned) { 1.512 + // Destroy the DLL interceptor 1.513 + sIOPoisoned = false; 1.514 + sNtDllInterceptor = WindowsDllInterceptor(); 1.515 + } 1.516 +} 1.517 + 1.518 +} // namespace mozilla