Bug 1603974 - Part 1: Implement nt::VirtualQuery consuming only ntdll.dll. r=mhowell

This patch introduces `nt::VirtualQuery` which consumes only ntdll's functions
to reduce dependency in `MMPolicy` on kernel32.dll.  With this, `MMPolicy` still
depends on kernel32.dll, that will be solved by a coming patch.

Differential Revision: https://phabricator.services.mozilla.com/D68342

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Toshihito Kikuchi 2020-04-08 14:27:01 +00:00
Родитель d7147ca5b6
Коммит 8bb38652d4
5 изменённых файлов: 93 добавлений и 4 удалений

Просмотреть файл

@ -48,6 +48,10 @@ if CONFIG['OS_ARCH'] == 'WINNT':
'winmm.dll',
'user32.dll',
]
OS_LIBS += [
'ntdll',
]
DELAYLOAD_DLLS += [
'xul.dll',

Просмотреть файл

@ -50,6 +50,10 @@ if CONFIG['OS_ARCH'] == 'WINNT':
'user32.dll',
]
OS_LIBS += [
'ntdll',
]
DELAYLOAD_DLLS += [
'xul.dll',
]

Просмотреть файл

@ -89,6 +89,9 @@ VOID NTAPI RtlAcquireSRWLockShared(PSRWLOCK aLock);
VOID NTAPI RtlReleaseSRWLockExclusive(PSRWLOCK aLock);
VOID NTAPI RtlReleaseSRWLockShared(PSRWLOCK aLock);
ULONG NTAPI RtlNtStatusToDosError(NTSTATUS aStatus);
VOID NTAPI RtlSetLastWin32Error(DWORD aError);
NTSTATUS NTAPI NtReadVirtualMemory(HANDLE aProcessHandle, PVOID aBaseAddress,
PVOID aBuffer, SIZE_T aNumBytesToRead,
PSIZE_T aNumBytesRead);
@ -1099,6 +1102,8 @@ inline DWORD RtlGetCurrentThreadId() {
0xFFFFFFFFUL);
}
const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
inline LauncherResult<DWORD> GetParentProcessId() {
struct PROCESS_BASIC_INFORMATION {
NTSTATUS ExitStatus;
@ -1109,7 +1114,6 @@ inline LauncherResult<DWORD> GetParentProcessId() {
ULONG_PTR InheritedFromUniqueProcessId;
};
const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
ULONG returnLength;
PROCESS_BASIC_INFORMATION pbi = {};
NTSTATUS status =
@ -1122,6 +1126,30 @@ inline LauncherResult<DWORD> GetParentProcessId() {
return static_cast<DWORD>(pbi.InheritedFromUniqueProcessId & 0xFFFFFFFF);
}
inline SIZE_T WINAPI VirtualQueryEx(HANDLE aProcess, LPCVOID aAddress,
PMEMORY_BASIC_INFORMATION aMemInfo,
SIZE_T aMemInfoLen) {
#if defined(MOZILLA_INTERNAL_API)
return ::VirtualQueryEx(aProcess, aAddress, aMemInfo, aMemInfoLen);
#else
SIZE_T returnedLength;
NTSTATUS status = ::NtQueryVirtualMemory(
aProcess, const_cast<PVOID>(aAddress), MemoryBasicInformation, aMemInfo,
aMemInfoLen, &returnedLength);
if (!NT_SUCCESS(status)) {
::RtlSetLastWin32Error(::RtlNtStatusToDosError(status));
returnedLength = 0;
}
return returnedLength;
#endif // defined(MOZILLA_INTERNAL_API)
}
inline SIZE_T WINAPI VirtualQuery(LPCVOID aAddress,
PMEMORY_BASIC_INFORMATION aMemInfo,
SIZE_T aMemInfoLen) {
return nt::VirtualQueryEx(kCurrentProcess, aAddress, aMemInfo, aMemInfoLen);
}
struct DataDirectoryEntry : public _IMAGE_DATA_DIRECTORY {
DataDirectoryEntry() : _IMAGE_DATA_DIRECTORY() {}

Просмотреть файл

@ -74,6 +74,19 @@ PVOID WINAPI MapViewOfFile3(HANDLE FileMapping, HANDLE Process,
extern "C" errno_t rand_s(unsigned int* randomValue);
#endif // !defined(_CRT_RAND_S)
// Declaring only the functions we need in NativeNt.h. To include the entire
// NativeNt.h causes circular dependency.
namespace mozilla {
namespace nt {
SIZE_T WINAPI VirtualQueryEx(HANDLE aProcess, LPCVOID aAddress,
PMEMORY_BASIC_INFORMATION aMemInfo,
SIZE_T aMemInfoLen);
SIZE_T WINAPI VirtualQuery(LPCVOID aAddress, PMEMORY_BASIC_INFORMATION aMemInfo,
SIZE_T aMemInfoLen);
} // namespace nt
} // namespace mozilla
namespace mozilla {
namespace interceptor {
@ -276,7 +289,7 @@ class MOZ_TRIVIAL_CTOR_DTOR MMPolicyBase {
// Scan the range for a free chunk that is at least as large as
// aDesiredBytesLen
while (address <= kMaxPtr &&
::VirtualQueryEx(aProcess, address, &mbi, len)) {
nt::VirtualQueryEx(aProcess, address, &mbi, len)) {
if (mbi.State == MEM_FREE && mbi.RegionSize >= aDesiredBytesLen) {
return mbi.BaseAddress;
}
@ -424,7 +437,7 @@ class MOZ_TRIVIAL_CTOR_DTOR MMPolicyInProcess : public MMPolicyBase {
*/
bool IsPageAccessible(void* aVAddress) const {
MEMORY_BASIC_INFORMATION mbi;
SIZE_T result = ::VirtualQuery(aVAddress, &mbi, sizeof(mbi));
SIZE_T result = nt::VirtualQuery(aVAddress, &mbi, sizeof(mbi));
return result && mbi.AllocationProtect && (mbi.Type & MEM_IMAGE) &&
mbi.State == MEM_COMMIT && mbi.Protect != PAGE_NOACCESS;
@ -696,7 +709,7 @@ class MMPolicyOutOfProcess : public MMPolicyBase {
*/
bool IsPageAccessible(void* aVAddress) const {
MEMORY_BASIC_INFORMATION mbi;
SIZE_T result = ::VirtualQueryEx(mProcess, aVAddress, &mbi, sizeof(mbi));
SIZE_T result = nt::VirtualQueryEx(mProcess, aVAddress, &mbi, sizeof(mbi));
return result && mbi.AllocationProtect && (mbi.Type & MEM_IMAGE) &&
mbi.State == MEM_COMMIT && mbi.Protect != PAGE_NOACCESS;

Просмотреть файл

@ -41,6 +41,29 @@ const char kFailFmt[] =
using namespace mozilla;
using namespace mozilla::nt;
bool TestVirtualQuery(HANDLE aProcess, LPCVOID aAddress) {
MEMORY_BASIC_INFORMATION info1 = {}, info2 = {};
SIZE_T result1 = ::VirtualQueryEx(aProcess, aAddress, &info1, sizeof(info1)),
result2 = mozilla::nt::VirtualQueryEx(aProcess, aAddress, &info2,
sizeof(info2));
if (result1 != result2) {
printf("TEST-FAILED | NativeNt | The returned values mismatch\n");
return false;
}
if (!result1) {
// Both APIs failed.
return true;
}
if (memcmp(&info1, &info2, result1) != 0) {
printf("TEST-FAILED | NativeNt | The returned structures mismatch\n");
return false;
}
return true;
}
// Need a non-inline function to bypass compiler optimization that the thread
// local storage pointer is cached in a register before accessing a thread-local
// variable.
@ -226,6 +249,23 @@ int wmain(int argc, wchar_t* argv[]) {
return 1;
}
// To test the Ex version of API, we purposely get a real handle
// instead of a pseudo handle.
nsAutoHandle process(
::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()));
if (!process) {
printf("TEST-FAILED | NativeNt | OpenProcess() failed - %08lx\n",
::GetLastError());
return 1;
}
// Test Null page, Heap, Mapped image, and Invalid handle
if (!TestVirtualQuery(process, nullptr) || !TestVirtualQuery(process, argv) ||
!TestVirtualQuery(process, kNormal) ||
!TestVirtualQuery(nullptr, kNormal)) {
return 1;
}
printf("TEST-PASS | NativeNt | All tests ran successfully\n");
return 0;
}