windows-nt/Source/XPSP1/NT/base/wow64/tests/bvt/wow64bvt.c
2020-09-26 16:20:57 +08:00

1685 lines
44 KiB
C

// define COBJMACROS so the IUnknown_Release() gets defined
#define COBJMACROS
// disable ddraw COM declarations. Otherwise ddrawint.h defineds _NO_COM
// and includes ddraw.h. That causes ddraw.h to define IUnknown as 'void *'
// which obliterates the struct IUnknown in objbase.h. This all happens
// inside winddi.h
#define _NO_DDRAWINT_NO_COM
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <objbase.h>
#include <mshtml.h>
#include <winddi.h>
#include <io.h>
#define PAGE_4K 0x1000
// Assume an error happened. This variable controls whether a pass or fail
// is logged in the test results.
BOOL g_bError = TRUE;
// Time the test started (only valid in the main process)
time_t TestStartTime;
FILE *fpLogFile;
void __cdecl PrintToLog(char *format, ...)
{
va_list pArg;
char buffer[4096];
va_start(pArg, format);
_vsnprintf(buffer, sizeof(buffer), format, pArg);
buffer[sizeof(buffer)-1] = '\0';
printf("%s", buffer);
if (fpLogFile) {
fprintf(fpLogFile, "%s", buffer);
}
}
//////////// All this code runs in a worker thread in a child process //////
//// Prefix any output by "WOW64BVT1" so it can be identified as coming from
//// the child process.
// This routine is invoked when "childprocess" is passed on the command-line.
// The child runs synchronously with the parent to maximize the test coverage.
int BeAChildProcess(void)
{
HRESULT hr;
IUnknown *pUnk;
CLSID clsid;
HWND hwnd;
PrintToLog("WOW64BVT1: Child process running\n");
// Do some COM stuff here
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED|COINIT_DISABLE_OLE1DDE);
if (FAILED(hr)) {
PrintToLog("ERROR: WOW64BVT1: CoInitializeEx failed %x\n", hr);
return 3;
}
// Load and call 32-bit mshtml inproc
hr = CLSIDFromProgID(L"Shell.Explorer", &clsid);
if (FAILED(hr)) {
PrintToLog("ERROR: WOW64BVT1: CLSIDFromProgID for Shell.Explorer failed %x\n", hr);
return 3;
}
#if 0 // The cocreate fails on IA64 with 8007000e (E_OUTOFMEMORY)
hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (PVOID *)&pUnk);
if (FAILED(hr)) {
PrintToLog("ERROR: WOW64BVT1: CoCreateInstance for Shell.Explorer failed %x\n", hr);
return 3;
}
Sleep(1000);
IUnknown_Release(pUnk);
pUnk = NULL;
#endif
// Load and call mplay32.exe out-of-proc
#if 0 // The clsidfromprogid fails on IA64 with 800401f3 (CO_E_CLASSSTRING)
hr = CLSIDFromProgID(L"MediaPlayer.MediaPlayer.1", &clsid);
if (FAILED(hr)) {
PrintToLog("ERROR: WOW64BVT1: CLSIDFromProgID for MediaPlayer.MediaPlayer failed %x\n", hr);
return 3;
}
hr = CoCreateInstance(&clsid, NULL, CLSCTX_LOCAL_SERVER, &IID_IUnknown, (PVOID *)&pUnk);
if (FAILED(hr)) {
PrintToLog("ERROR: WOW64BVT1: CoCreateInstance for MediaPlayer.MediaPlayer failed %x\n", hr);
return 3;
}
Sleep(5000);
IUnknown_Release(pUnk);
pUnk = NULL;
#endif
// Unfortunately, mplay32 has a refcounting problem and doesn't close
// when we release it. Post a quit message to make it close.
hwnd = FindWindowW(NULL, L"Windows Media Player");
if (hwnd) {
PostMessage(hwnd, WM_QUIT, 0, 0);
}
// Wrap up COM now
CoUninitialize();
PrintToLog("WOW64BVT1: Child process done OK.\n");
return 0;
}
//////////// All this code runs in a worker thread in the main process //////
//// Prefix any output by "WOW64BVT" so it can be identified as coming from
//// the parent process.
DWORD BeAThread(LPVOID lpParam)
{
NTSTATUS st;
LPWSTR lp;
PrintToLog("WOW64BVT: Worker thread running\n");
// Call an API close to the end of whnt32.c's dispatch table
st = NtYieldExecution();
if (FAILED(st)) {
PrintToLog("ERROR: WOW64BVT: NtYieldExecution failed %x\n", st);
exit(1);
}
// Call an API close to the end of whwin32.c's dispatch table. Pass NULL,
// so it is expected to fail.
lp = EngGetPrinterDataFileName(NULL); // calls NtGdiGetDhpdev()
if (lp) {
// It succeeded.... it shouldn't have since
PrintToLog("ERROR: WOW64BVT: EngGetPrinterDataFileName succeeeded when it should not have.\n");
exit(1);
}
PrintToLog("WOW64BVT: Worker thread done OK.\n");
return 0;
}
HANDLE CreateTheThread(void)
{
HANDLE h;
DWORD dwThreadId;
PrintToLog("WOW64BVT: Creating child thread\n");
h = CreateThread(NULL, 0, BeAThread, NULL, 0, &dwThreadId);
if (h == INVALID_HANDLE_VALUE) {
PrintToLog("ERROR: WOW64BVT: Error %d creating worker thread.\n", GetLastError());
exit(2);
}
// Sleep a bit here, to try and let the child thread run a bit
Sleep(10);
return h;
}
BOOL AllocateStackAndTouch(
INT Count)
{
char temp[4096];
memset(temp, 0, sizeof(temp));
if (--Count) {
AllocateStackAndTouch(Count);
}
return TRUE;
}
DWORD WINAPI TestGuardPagesThreadProc(
PVOID lpParam)
{
try {
AllocateStackAndTouch(PtrToUlong(lpParam));
} except(EXCEPTION_EXECUTE_HANDLER) {
PrintToLog("ERROR: WOW64BVT: Error allocating stack. Exception Code = %lx\n",
GetExceptionCode());
exit(1);
}
return 0;
}
int TestGuardPages(
VOID)
{
HANDLE h;
DWORD dwExitCode, dwThreadId;
BOOL b;
ULONG NestedStackCount = 100;
DWORD StackSize = 4096;
PrintToLog("WOW64BVT: Creating worker threads to test guard pages\n");
while (StackSize < (32768+1))
{
h = CreateThread(NULL,
StackSize,
TestGuardPagesThreadProc,
UlongToPtr(NestedStackCount),
0,
&dwThreadId);
if (h == INVALID_HANDLE_VALUE) {
PrintToLog("ERROR: WOW64BVT: Error %d creating worker thread for guard page tests.\n", GetLastError());
exit(2);
}
StackSize += 4096;
WaitForSingleObject(h, INFINITE);
b = GetExitCodeThread(h, &dwExitCode);
if (b) {
if (dwExitCode) {
return (int)dwExitCode;
}
} else {
PrintToLog("ERROR: GetExitCodeThread failed with LastError = %d\n", GetLastError());
return 1;
}
}
PrintToLog("WOW64BVT: Test guard pages done OK.\n");
return 0;
}
int TestMemoryMappedFiles(
VOID)
{
HANDLE Handle;
PWCHAR pwc;
MEMORY_BASIC_INFORMATION mbi;
BOOL ExceptionHappened = FALSE;
PrintToLog("WOW64BVT: Testing memory mapped files\n");
Handle = CreateFileMappingW(INVALID_HANDLE_VALUE,
NULL,
SEC_RESERVE | PAGE_READWRITE,
0,
32 * 1024,
L"HelloWorld");
if (Handle == INVALID_HANDLE_VALUE) {
PrintToLog("ERROR: WOW64BVT : Error %d creating file mapping\n", GetLastError());
return 1;
}
pwc = (PWCHAR)MapViewOfFile(Handle,
FILE_MAP_WRITE,
0,
0,
0);
if (!pwc) {
PrintToLog("ERROR: WOW64BVT : Error %d mapping section object\n", GetLastError());
return 1;
}
if (!VirtualQuery(pwc,
&mbi,
sizeof(mbi))) {
PrintToLog("ERROR: WOW64BVT : Virtual query failed with last error = %d\n", GetLastError());
return 1;
}
if (mbi.State != MEM_RESERVE) {
PrintToLog("ERROR: WOW64BVT : Memory attributes have changed since mapped %lx\n", mbi.State);
return 1;
}
try {
*pwc = *(pwc+1);
} except(EXCEPTION_EXECUTE_HANDLER) {
ExceptionHappened = TRUE;
}
if (ExceptionHappened == FALSE) {
PrintToLog("ERROR: WOW64BVT : Memory has been committed while it should have ONLY been reserved.\n");
return 1;
}
if (!VirtualQuery(pwc,
&mbi,
sizeof(mbi))) {
PrintToLog("ERROR: WOW64BVT : Virtual query failed with last error = %d\n", GetLastError());
return 1;
}
if (mbi.State != MEM_RESERVE) {
PrintToLog("ERROR: WOW64BVT : Memory attributes have changed since mapped %lx\n", mbi.State);
return 1;
}
UnmapViewOfFile(pwc);
CloseHandle(Handle);
PrintToLog("WOW64BVT: Testing memory mapped files done OK.\n");
return 0;
}
BOOL ReleasePages(PVOID Address,
DWORD DeAllocationType,
SIZE_T ReleasedPages)
{
PVOID p = Address;
SIZE_T nPages = ReleasedPages;
NTSTATUS NtStatus;
NtStatus = NtFreeVirtualMemory(NtCurrentProcess(),
&p,
&nPages,
DeAllocationType);
return NT_SUCCESS(NtStatus);
}
BOOL VerifyPages(PVOID Address,
DWORD DeAllocationType,
SIZE_T ReleasedPages)
{
DWORD PagesState;
BOOL b;
MEMORY_BASIC_INFORMATION mbi;
if (DeAllocationType == MEM_DECOMMIT)
{
PagesState = MEM_RESERVE;
}
else if (DeAllocationType == MEM_RELEASE)
{
PagesState = MEM_FREE;
}
else
{
PagesState = DeAllocationType;
}
b = VirtualQuery(Address,
&mbi,
sizeof(mbi));
if (b)
{
if (mbi.State != PagesState)
{
PrintToLog("ERROR: WOW64BVT: Incorrect page protection set at address %p. State = %lx - %lx, RegionSize = %lx - %lx\n",
Address, mbi.State, PagesState, mbi.RegionSize, ReleasedPages);
b = FALSE;
}
}
else
{
PrintToLog("ERROR: WOW64BVT: Failed to query virtual memory at address %p - %lx\n",
Address, GetLastError());
}
return b;
}
BOOL ReleaseVerifyPages(PVOID BaseAllocation,
PVOID *Address,
SIZE_T *AllocationSize,
DWORD AllocationType,
DWORD DeAllocationType,
DWORD ReleasedPages)
{
BOOL b;
if (ReleasedPages > *AllocationSize)
{
ReleasedPages = *AllocationSize;
}
b = ReleasePages(*Address, DeAllocationType, ReleasedPages);
if (b == FALSE)
{
PrintToLog("ERROR: WOW64BVT: Failed to release a page - %lx\n", GetLastError());
return b;
}
b = VerifyPages(*Address,
DeAllocationType,
ReleasedPages);
*AllocationSize -= ReleasedPages;
*Address = (PVOID)((ULONG_PTR)*Address + ReleasedPages);
if (b == FALSE)
{
PrintToLog("ERROR: WOW64BVT: Failed to verify pages at address %lx - %lx\n",
((ULONG_PTR)Address + ReleasedPages), GetLastError());
}
return b;
}
BOOL TestVadSplitOnFreeHelper(DWORD AllocationType,
DWORD DeAllocationType,
SIZE_T TotalAllocation)
{
BOOL b;
PVOID Address;
PVOID BaseAllocation;
SIZE_T BaseAllocationSize;
INT n;
Address = VirtualAlloc(NULL,
TotalAllocation,
AllocationType,
PAGE_READWRITE);
if (Address == NULL)
{
PrintToLog("ERROR: WOW64BVT: Failed to allocate memory - %lx\n", GetLastError());
}
n = 1;
BaseAllocation = Address;
BaseAllocationSize = TotalAllocation;
while (TotalAllocation != 0)
{
b = ReleaseVerifyPages(BaseAllocation,
&Address,
&TotalAllocation,
AllocationType,
DeAllocationType,
PAGE_4K * n);
if (b == FALSE)
{
PrintToLog("ERROR: WOW64BVT: ReleaseVerifyPages failed - %lx. %lx-%lx-%lx",
GetLastError(), BaseAllocation, Address, TotalAllocation);
break;
}
b = VerifyPages(BaseAllocation,
DeAllocationType,
n * PAGE_4K);
if (b == FALSE)
{
PrintToLog("ERROR: WOW64BVT: Verify released pages from address %p with length = %lx failed\n", BaseAllocation, (n * PAGE_4K));
break;
}
if (TotalAllocation > 0)
{
b = VerifyPages(Address,
AllocationType,
TotalAllocation);
if (b == FALSE)
{
PrintToLog("ERROR: WOW64BVT: Verify pages from address %p with length = %lx failed\n", BaseAllocation, TotalAllocation);
break;
}
}
n += 2;
}
return b;
}
int TestVadSplitOnFree()
{
BOOL b;
SIZE_T AllocationSize = (PAGE_4K * 10);
PrintToLog("WOW64BVT: Testing VAD splitting...\n");
b = TestVadSplitOnFreeHelper(MEM_COMMIT,
MEM_DECOMMIT,
AllocationSize);
if (b)
{
b = TestVadSplitOnFreeHelper(MEM_COMMIT,
MEM_RELEASE,
AllocationSize);
}
if (b)
{
b = TestVadSplitOnFreeHelper(MEM_RESERVE,
MEM_RELEASE,
AllocationSize);
}
if (b != FALSE)
{
PrintToLog("WOW64BVT: Testing VAD splitting...OK\n");
}
else
{
PrintToLog("ERROR: WOW64BVT: Testing VAD splitting\n");
}
return (b == FALSE);
}
PVOID GetReadOnlyBuffer()
{
PVOID pReadOnlyBuffer = NULL;
if (!pReadOnlyBuffer)
{
SYSTEM_INFO SystemInfo;
// Get system info so that we know the page size
GetSystemInfo(&SystemInfo);
// Allocate a whole page. This is optimal.
pReadOnlyBuffer = VirtualAlloc(NULL, SystemInfo.dwPageSize, MEM_COMMIT, PAGE_READWRITE);
if (pReadOnlyBuffer)
{
// Fill it with know patern
FillMemory(pReadOnlyBuffer, SystemInfo.dwPageSize, 0xA5);
// Mark the page readonly
pReadOnlyBuffer = VirtualAlloc(pReadOnlyBuffer, SystemInfo.dwPageSize, MEM_COMMIT, PAGE_READONLY);
}
}
return pReadOnlyBuffer;
}
PVOID GetReadOnlyBuffer2()
{
PVOID pReadOnlyBuffer = NULL;
DWORD OldP;
SYSTEM_INFO SystemInfo;
// Get system info so that we know the page size
GetSystemInfo(&SystemInfo);
// Allocate a whole page. This is optimal.
pReadOnlyBuffer = VirtualAlloc(NULL, SystemInfo.dwPageSize, MEM_COMMIT, PAGE_READWRITE);
if (pReadOnlyBuffer)
{
FillMemory(pReadOnlyBuffer, SystemInfo.dwPageSize, 0xA5);
lstrcpy((PTSTR)pReadOnlyBuffer, TEXT("xxxxxxxxxxxxxxxxxxxx"));
if (!VirtualProtect(pReadOnlyBuffer, SystemInfo.dwPageSize, PAGE_READONLY, &OldP))
{
PrintToLog("ERROR: WOW64BVT: VirtualProtect() failed inside GetReadOnlyBuffer2()\n");
VirtualFree(pReadOnlyBuffer, 0, MEM_RELEASE);
pReadOnlyBuffer = NULL;
}
}
return pReadOnlyBuffer;
}
BOOL TestMmPageProtection()
{
PTSTR String;
BOOL AV = FALSE;
PrintToLog("WOW64BVT: Testing MM Page Protection...\n");
String = (PTSTR) GetReadOnlyBuffer();
if (!String) {
PrintToLog("ERROR: WOW64BVT: GetReadOnlyBuffer() failed\n");
return TRUE;
}
try {
*String = TEXT('S');
} except(EXCEPTION_EXECUTE_HANDLER) {
AV = TRUE;
}
VirtualFree(String, 0, MEM_RELEASE);
if (AV == TRUE) {
AV = FALSE;
String = (PTSTR) GetReadOnlyBuffer2();
if (!String) {
PrintToLog("ERROR: WOW64BVT: GetReadOnlyBuffer2() failed\n");
return TRUE;
}
try {
*String = TEXT('A');
} except(EXCEPTION_EXECUTE_HANDLER) {
AV = TRUE;
}
VirtualFree(String, 0, MEM_RELEASE);
} else {
PrintToLog("ERROR: WOW64BVT: GetReadOnlyBuffer() failed to make 4K pages read only\n");
}
if (AV == TRUE) {
PrintToLog("WOW64BVT: Testing MM Page Protection...OK\n");
} else {
PrintToLog("ERROR: WOW64BVT: Testing MM Page Protection\n");
}
return (AV == FALSE);
}
#define STACK_BUFFER 0x300
BOOL TestX86MisalignedLock()
{
BOOL bError = FALSE;
PrintToLog("WOW64BVT: Testing X86 Lock on misaligned addresses...\n");
__try
{
__asm
{
pushad;
pushfd;
sub esp, STACK_BUFFER;
;;
;; make eax unaliged with respect to an 8-byte cache line
;;
mov eax, esp;
add eax, 10h;
mov ecx, 0xfffffff0;
and eax, ecx;
add eax, 7;
mov ebx, eax;
;;
;; add
;;
mov DWORD PTR [eax], 0x0300;
lock add WORD PTR [eax], 0x0004;
cmp DWORD PTR [eax], 0x0304;
jnz $endwitherrornow;
mov DWORD PTR [eax], 0x0300;
lock add DWORD PTR [eax], 0x10000;
cmp DWORD PTR [eax], 0x10300;
jnz $endwitherrornow;
mov ecx, DWORD PTR [eax+8];
add ecx, 0x10;
lock add DWORD PTR [eax+8], 0x10;
cmp DWORD PTR [eax+8], ecx;
jnz $endwitherrornow;
mov ecx, DWORD PTR fs:[5];
mov esi, 0x30000;
lock add DWORD PTR fs:[5], esi;
add esi, ecx;
cmp DWORD PTR fs:[5], esi;
mov DWORD PTR fs:[5], ecx;
jnz $endwitherrornow;
mov edi, 5;
mov ecx, DWORD PTR fs:[edi];
mov esi, 0x30000;
lock add DWORD PTR fs:[edi], esi;
add esi, ecx;
cmp DWORD PTR fs:[edi], esi;
mov DWORD PTR fs:[edi], ecx;
jnz $endwitherrornow;
mov esi, 0x40;
mov WORD PTR [eax], 0x3000;
lock add WORD PTR [eax], si;
cmp WORD PTR [eax], 0x3040;
jnz $endwitherrornow;
mov edi, 0x40;
mov DWORD PTR [eax], 0x3000;
lock add DWORD PTR [eax], edi;
cmp DWORD PTR [eax], 0x3040;
jnz $endwitherrornow;
;;
;; adc
;;
pushfd;
pop ecx;
or ecx, 1;
push ecx;
popfd;
mov DWORD PTR [eax], 0x030000;
lock adc DWORD PTR [eax], 0x40000;
cmp DWORD PTR [eax], 0x70001;
jnz $endwitherrornow;
pushfd;
pop ecx;
and ecx, 0xfffffffe;
push ecx;
popfd;
mov dx, 0x4000;
mov WORD PTR [eax], 0x03000;
lock adc WORD PTR [eax], dx;
cmp WORD PTR [eax], 0x7000;
jnz $endwitherrornow;
pushfd;
pop ecx;
or ecx, 0x01;
push ecx;
popfd;
mov WORD PTR [eax], 0x03000;
lock adc WORD PTR [eax], 0x04000;
cmp WORD PTR [eax], 0x7001;
jnz $endwitherrornow;
;;
;; and
;;
mov DWORD PTR [eax], 0xffffffff;
lock and DWORD PTR [eax], 0xffff;
cmp DWORD PTR [eax], 0xffff;
jnz $endwitherrornow;
mov DWORD PTR [eax], 0xffffffff;
lock and DWORD PTR [eax], 0xff00ff00;
cmp DWORD PTR [eax], 0xff00ff00;
jnz $endwitherrornow;
mov DWORD PTR [eax], 0xffffffff;
mov esi, 0x00ff00ff
lock and DWORD PTR [eax], esi;
cmp DWORD PTR [eax], esi;
jnz $endwitherrornow;
mov ecx, 4;
mov DWORD PTR [eax+ecx*2], 0xffffffff;
mov esi, 0xffff00ff
lock and DWORD PTR [eax+ecx*2], esi;
cmp DWORD PTR [eax+ecx*2], 0xffff00ff;
jnz $endwitherrornow;
mov WORD PTR [eax], 0xffff;
mov si, 0xff
lock and WORD PTR [eax], si;
cmp WORD PTR [eax], si;
jnz $endwitherrornow;
mov edi, DWORD PTR fs:[5];
mov DWORD PTR fs:[5], 0xffffffff;
mov ebx, 5;
lock and DWORD PTR fs:[ebx], 0xff00ff00;
cmp DWORD PTR fs:[ebx], 0xff00ff00;
mov DWORD PTR fs:[ebx], edi;
jnz $endwitherrornow;
;;
;; or
;;
mov DWORD PTR [eax], 0x00;
lock or DWORD PTR [eax], 0xffff;
cmp DWORD PTR [eax], 0xffff;
jnz $endwitherrornow;
mov DWORD PTR [eax], 0xff00ff00;
lock or DWORD PTR [eax], 0xff00ff;
cmp DWORD PTR [eax], 0xffffffff;
jnz $endwitherrornow;
mov DWORD PTR [eax], 0xff000000;
mov esi, 0x00ff00ff
lock or DWORD PTR [eax], esi;
cmp DWORD PTR [eax], 0xffff00ff;
jnz $endwitherrornow;
mov ecx, 4;
mov DWORD PTR [eax+ecx*2], 0xff000000;
mov esi, 0x00ff00ff
lock or DWORD PTR [eax+ecx*2], esi;
cmp DWORD PTR [eax+ecx*2], 0xffff00ff;
jnz $endwitherrornow;
mov WORD PTR [eax], 0xf000;
mov si, 0xff
lock or WORD PTR [eax], si;
cmp WORD PTR [eax], 0xf0ff;
jnz $endwitherrornow;
mov edi, DWORD PTR fs:[5];
mov DWORD PTR fs:[5], 0x00;
mov ebx, 5;
lock or DWORD PTR fs:[ebx], 0xff00ff00;
cmp DWORD PTR fs:[ebx], 0xff00ff00;
mov DWORD PTR fs:[ebx], edi;
jnz $endwitherrornow;
;;
;; xor
;;
mov DWORD PTR [eax], 0x00ffffff;
lock xor DWORD PTR [eax], 0xffff;
cmp DWORD PTR [eax], 0x00ff0000;
jnz $endwitherrornow;
mov DWORD PTR [eax], 0xff00ff00;
lock xor DWORD PTR [eax], 0xff00ff;
cmp DWORD PTR [eax], 0xffffffff;
jnz $endwitherrornow;
mov DWORD PTR [eax], 0xff0000ff;
mov esi, 0x00ff00ff
lock xor DWORD PTR [eax], esi;
cmp DWORD PTR [eax], 0xffff0000;
jnz $endwitherrornow;
mov ecx, 4;
mov DWORD PTR [eax+ecx*2], 0xff000000;
mov esi, 0xffff00ff
lock xor DWORD PTR [eax+ecx*2], esi;
cmp DWORD PTR [eax+ecx*2], 0x00ff00ff;
jnz $endwitherrornow;
mov WORD PTR [eax], 0xf000;
mov si, 0xf0ff
lock xor WORD PTR [eax], si;
cmp WORD PTR [eax], 0x00ff;
jnz $endwitherrornow;
mov edi, DWORD PTR fs:[5];
mov DWORD PTR fs:[5], 0x0f;
mov ebx, 5;
lock xor DWORD PTR fs:[ebx], 0xff00000f;
cmp DWORD PTR fs:[ebx], 0xff000000;
mov DWORD PTR fs:[ebx], edi;
jnz $endwitherrornow;
;;
;; inc & dec
;;
mov DWORD PTR [eax], 0xffff;
lock inc DWORD PTR [eax];
cmp DWORD PTR [eax], 0x10000;
jnz $endwitherrornow;
lock inc WORD PTR [eax];
cmp WORD PTR [eax], 0x0001;
lock dec WORD PTR [eax];
jnz $endwitherrornow;
cmp WORD PTR [eax], 0x00;
jnz $endwitherrornow;
mov DWORD PTR [eax], 0;
lock dec DWORD PTR [eax];
cmp DWORD PTR [eax], 0xffffffff;
jnz $endwitherrornow;
;;
;; not
;;
mov DWORD PTR [eax], 0x10101010;
lock not DWORD PTR [eax];
cmp DWORD PTR [eax], 0xefefefef;
jnz $endwitherrornow;
mov DWORD PTR [eax+8], 0xffff0000;
lock not DWORD PTR [eax+8];
cmp DWORD PTR [eax+8], 0x0000ffff;
jnz $endwitherrornow;
mov ecx, 2;
mov DWORD PTR [eax+ecx*4], 0xffffffff;
lock not DWORD PTR [eax+ecx*4];
cmp DWORD PTR [eax+ecx*4], 0x00000000;
jnz $endwitherrornow;
;;
;; neg
;;
mov DWORD PTR [eax], 0;
lock neg DWORD PTR [eax];
jc $endwitherrornow;
cmp DWORD PTR [eax], 0;
jnz $endwitherrornow;
mov DWORD PTR [eax], 0xffffffff;
lock neg DWORD PTR [eax];
jnc $endwitherrornow;
cmp DWORD PTR [eax], 0x01;
jnz $endwitherrornow;
mov WORD PTR [eax], 0xff;
lock neg WORD PTR [eax];
jnc $endwitherrornow;
cmp WORD PTR [eax], 0xff01;
jnz $endwitherrornow;
;;
;; bts
;;
mov DWORD PTR [eax], 0x7ffffffe;
lock bts DWORD PTR [eax], 0;
jc $endwitherrornow;
cmp DWORD PTR [eax], 0x7fffffff;
jnz $endwitherrornow;
mov ecx, eax;
sub ecx, 4;
mov edx, 63;
lock bts DWORD PTR [ecx], edx;
jc $endwitherrornow;
cmp DWORD PTR [eax], 0xffffffff
jnz $endwitherrornow;
;;
;; xchg
;;
mov DWORD PTR [eax], 0xf0f0f0f0;
mov edx, 0x11112222;
lock xchg DWORD PTR [eax], edx;
cmp DWORD PTR [eax], 0x11112222;
jnz $endwitherrornow;
cmp edx, 0xf0f0f0f0;
jnz $endwitherrornow;
xchg WORD PTR [eax], dx;
cmp WORD PTR [eax], 0xf0f0;
jnz $endwitherrornow;
cmp dx, 0x2222;
jnz $endwitherrornow;
;;
;; cmpxchg
;;
mov ebx, eax;
mov DWORD PTR [ebx], 0xf0f0f0f0;
mov eax, 0x10101010;
mov edx, 0x22332233;
lock cmpxchg DWORD PTR [ebx], edx;
jz $endwitherrornow;
cmp eax, 0xf0f0f0f0;
jnz $endwitherrornow;
mov DWORD PTR [ebx], 0xf0f0f0f0;
mov eax, 0xf0f0f0f0;
mov edx, 0x12341234;
lock cmpxchg DWORD PTR [ebx], edx;
jnz $endwitherrornow;
cmp DWORD PTR [ebx], 0x12341234;
jnz $endwitherrornow;
;;
;; cmpxchg8b
;;
mov DWORD PTR [ebx], 0x11223344;
mov DWORD PTR [ebx+4], 0x55667788;
mov edx, 0x12341234;
mov eax, 0xff00ff00;
lock cmpxchg8b [ebx];
jz $endwitherrornow;
cmp edx, 0x55667788;
jnz $endwitherrornow;
cmp eax, 0x11223344;
jnz $endwitherrornow;
mov esi, ebx;
mov DWORD PTR [esi], 0x11223344;
mov DWORD PTR [esi+4], 0x55667788;
mov edx, 0x55667788;
mov eax, 0x11223344;
mov ecx, 0x10101010;
mov ebx, 0x20202020;
lock cmpxchg8b [esi];
jnz $endwitherrornow;
cmp DWORD PTR [esi], 0x20202020;
jnz $endwitherrornow;
cmp DWORD PTR [esi+4], 0x10101010;
jnz $endwitherrornow;
mov eax, esi;
mov ebx, eax;
;;
;; sub
;;
mov DWORD PTR [eax], 0x0300;
lock sub WORD PTR [eax], 0x0004;
cmp DWORD PTR [eax], 0x2fc;
jnz $endwitherrornow;
mov DWORD PTR [eax], 0x10000;
lock sub DWORD PTR [eax], 0x0300;
cmp DWORD PTR [eax], 0xfd00;
jnz $endwitherrornow;
mov ecx, DWORD PTR [eax+8];
sub ecx, 0x10;
lock sub DWORD PTR [eax+8], 0x10;
cmp DWORD PTR [eax+8], ecx;
jnz $endwitherrornow;
mov ecx, DWORD PTR fs:[5];
mov esi, 0x3000;
lock sub DWORD PTR fs:[5], esi;
mov edi, ecx;
sub ecx, esi;
cmp DWORD PTR fs:[5], ecx;
mov DWORD PTR fs:[5], edi;
jnz $endwitherrornow;
mov edi, 5;
mov ecx, DWORD PTR fs:[edi];
mov esi, 0x30000;
lock sub DWORD PTR fs:[edi], esi;
mov edx, ecx;
sub ecx, esi;
cmp DWORD PTR fs:[edi], ecx;
mov DWORD PTR fs:[edi], edx;
jnz $endwitherrornow;
mov si, 0x40;
mov WORD PTR [eax], 0x3000;
lock sub WORD PTR [eax], si;
cmp WORD PTR [eax], 0x2fc0;
jnz $endwitherrornow;
mov edi, 0x40;
mov DWORD PTR [eax], 0x3000;
lock sub DWORD PTR [eax], edi;
cmp DWORD PTR [eax], 0x2fc0;
jnz $endwitherrornow;
;;
;; sbb
;;
pushfd;
pop ecx;
or ecx, 1;
push ecx;
popfd;
mov DWORD PTR [eax], 0x030000;
lock sbb DWORD PTR [eax], 0x40000;
cmp DWORD PTR [eax], 0xfffeffff;
jnz $endwitherrornow;
pushfd;
pop ecx;
and ecx, 0xfffffffe;
push ecx;
popfd;
mov dx, 0x4000;
mov WORD PTR [eax], 0x03000;
lock sbb WORD PTR [eax], dx;
cmp WORD PTR [eax], 0xf000;
jnz $endwitherrornow;
pushfd;
pop ecx;
or ecx, 0x01;
push ecx;
popfd;
mov WORD PTR [eax], 0x03000;
lock sbb WORD PTR [eax], 0x04000;
cmp WORD PTR [eax], 0xefff;
jnz $endwitherrornow;
;;
;; xadd
;;
mov DWORD PTR [eax], 0x12345678;
mov ecx, 0x1234;
lock xadd DWORD PTR [eax], ecx;
cmp ecx, 0x12345678;
jnz $endwitherrornow;
mov edx, 0x1234;
add edx, 0x12345678;
cmp DWORD PTR [eax], edx;
jnz $endwitherrornow;
mov WORD PTR [eax], 0x5678;
mov cx, 0x1234;
lock xadd WORD PTR [eax], cx;
cmp cx, 0x5678;
jnz $endwitherrornow;
mov dx, 0x5678;
add dx, 0x1234;
cmp WORD PTR [eax], dx;
jnz $endwitherrornow;
;;
;; Update caller with status
;;
mov bError, 0
jmp $endnow;
$endwitherrornow:
mov bError, 1
$endnow:
add esp, STACK_BUFFER;
popfd;
popad;
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
bError = TRUE;
printf("ERROR: WOW64BVT: Exception %lx\n", GetExceptionCode());
}
if (bError == FALSE) {
PrintToLog("WOW64BVT: Testing X86 Lock on misaligned addresses...OK\n");
} else {
PrintToLog("ERROR: WOW64BVT: Testing X86 Lock on misaligned addresses\n");
}
return bError;
}
//////////// All this code runs in the main test driver thread //////
HANDLE CreateTheChildProcess(char *ProcessName, char *LogFileName)
{
char Buffer[512];
HANDLE h;
STARTUPINFOA si;
PROCESS_INFORMATION pi;
BOOL b;
PrintToLog("WOW64BVT: Creating child process\n");
strcpy(Buffer, ProcessName);
strcat(Buffer, " childprocess");
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
if (fpLogFile) {
// If we're logging, then change the child process' stdout/stderr
// to be the log file handle, so their output is captured to the file.
HANDLE hLog = (HANDLE)_get_osfhandle(_fileno(fpLogFile));
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput = hLog;
si.hStdError = hLog;
}
b = CreateProcessA(NULL, Buffer, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
if (!b) {
PrintToLog("ERROR: WOW64BVT: Error %d creating child process.\n", GetLastError());
exit(1);
}
CloseHandle(pi.hThread);
return pi.hProcess;
}
// This is called from within exit() in the main test driver process
void __cdecl AtExitHandler(void)
{
time_t EndTime;
struct tm *newtime;
OSVERSIONINFOW vi;
BOOL b;
int year, hour;
memset(&vi, 0, sizeof(vi));
vi.dwOSVersionInfoSize = sizeof(vi);
b = GetVersionExW(&vi);
if (!b) {
PrintToLog("\tWARNING: GetVersionExW failed, LastError = %d\n", GetLastError());
vi.dwBuildNumber = 0;
}
// Close the logging bucket.
PrintToLog(("[/TEST LOGGING OUTPUT]\n"));
// Print the required data:
PrintToLog("\tTEST: wow64bvt\n");
PrintToLog("\tBUILD: %d\n", vi.dwBuildNumber);
PrintToLog("\tMACHINE: \\\\%s\n", getenv("COMPUTERNAME"));
PrintToLog("\tRESULT: %s\n", (g_bError) ? "FAIL" : "PASS");
PrintToLog("\tCONTACT: samera\n");
PrintToLog("\tMGR CONTACT: samera\n");
PrintToLog("\tDEV PRIME: samera\n");
PrintToLog("\tDEV ALT: askhalid\n");
PrintToLog("\tTEST PRIME: terryla\n");
PrintToLog("\tTEST ALT: terryla\n");
newtime = localtime(&TestStartTime);
year = (newtime->tm_year >= 100) ? newtime->tm_year-100 : newtime->tm_year;
if (newtime->tm_hour == 0) {
hour = 12;
} else if (newtime->tm_hour > 12) {
hour = newtime->tm_hour-12;
} else {
hour = newtime->tm_hour;
}
PrintToLog("\tSTART TIME: %d/%d/%2.2d %d:%2.2d:%2.2d %s\n", newtime->tm_mon+1,
newtime->tm_mday,
year,
hour,
newtime->tm_min,
newtime->tm_sec,
(newtime->tm_hour < 12) ? "AM" : "PM");
time(&EndTime);
newtime = localtime(&EndTime);
year = (newtime->tm_year >= 100) ? newtime->tm_year-100 : newtime->tm_year;
if (newtime->tm_hour == 0) {
hour = 12;
} else if (newtime->tm_hour > 12) {
hour = newtime->tm_hour-12;
} else {
hour = newtime->tm_hour;
}
PrintToLog("\tEND TIME: %d/%d/%2.2d %d:%2.2d:%2.2d %s\n", newtime->tm_mon+1,
newtime->tm_mday,
year,
hour,
newtime->tm_min,
newtime->tm_sec,
(newtime->tm_hour < 12) ? "AM" : "PM");
PrintToLog("[/TESTRESULT]\n");
}
//
// This is just used to print out failing exception cases
//
void __cdecl ExceptionWoops(HRESULT want, HRESULT got)
{
if (got == 0) {
PrintToLog("==> Exception Skipped. Wanted: 0x%08x, Got: 0x%08x\n", want, got);
}
else {
PrintToLog("==> Unexpected Exception. Wanted: 0x%08x, Got: 0x%08x\n", want, got);
}
}
#define EXCEPTION_LOOP 10000
// Among other things, this does some divife by zero's (as a test)
// so don't have the compiler stop things just because we're causing an error
#pragma warning(disable:4756)
#pragma warning(disable:4723)
//
// Do some exception checks
//
int __cdecl ExceptionCheck(void)
{
int failThis;
int sawException;
int i;
char *p = NULL;
HRESULT code;
PrintToLog("WOW64BVT: Testing Exception Handling\n");
// Assume success
failThis = FALSE;
// Test a privileged instruction
sawException = FALSE;
__try {
__asm {
hlt
}
code = 0;
}
__except((code = GetExceptionCode()), 1 ) {
if (code == STATUS_PRIVILEGED_INSTRUCTION) {
// PrintToLog("Saw privileged instruction\n");
}
else {
PrintToLog("ERROR: Cause a privileged instruction fault\n");
ExceptionWoops(STATUS_PRIVILEGED_INSTRUCTION, code);
failThis = TRUE;
}
sawException = TRUE;
}
if (!sawException) {
PrintToLog("ERROR: Cause a privileged instruction fault\n");
ExceptionWoops(STATUS_PRIVILEGED_INSTRUCTION, code);
failThis = TRUE;
}
// Test an illegal instruction
sawException = FALSE;
__try {
__asm {
__asm _emit 0xff
__asm _emit 0xff
__asm _emit 0xff
__asm _emit 0xff
}
code = 0;
}
__except((code = GetExceptionCode()), 1 ) {
if (code == STATUS_ILLEGAL_INSTRUCTION) {
// PrintToLog("Saw illegal instruction\n");
}
else {
PrintToLog("ERROR: Cause an illegal instruction fault\n");
ExceptionWoops(STATUS_ILLEGAL_INSTRUCTION, code);
failThis = TRUE;
}
sawException = TRUE;
}
if (!sawException) {
PrintToLog("ERROR: Cause an illegal instruction fault\n");
ExceptionWoops(STATUS_ILLEGAL_INSTRUCTION, code);
failThis = TRUE;
}
//
// Testing for an int 3 can be a problem for systems that
// are running checked builds. So, don't bother with this
// test. Perhaps in the future, the code can test for a checked
// build and do appropriate.
//
#if 0
// Test the result of an int 3
sawException = FALSE;
__try {
_asm {
int 3
}
code = 0;
}
__except((code = GetExceptionCode()), 1 ) {
if (code == STATUS_BREAKPOINT) {
// PrintToLog("Saw debugger breakpoint\n");
}
else {
PrintToLog("ERROR: Cause an int 3 debugger breakpoint\n");
ExceptionWoops(STATUS_BREAKPOINT, code);
failThis = TRUE;
}
sawException = TRUE;
}
if (!sawException) {
PrintToLog("ERROR: Cause an int 3 debugger breakpoint\n");
ExceptionWoops(STATUS_BREAKPOINT, code);
failThis = TRUE;
}
#endif
// Test the result of an illegal int XX instruction
sawException = FALSE;
__try {
_asm {
int 66
}
code = 0;
}
__except((code = GetExceptionCode()), 1 ) {
if (code == STATUS_ACCESS_VIOLATION) {
// PrintToLog("Saw access violation\n");
}
else {
PrintToLog("ERROR: Cause an int 66 unknown interrupt (Access violation)\n");
ExceptionWoops(STATUS_ACCESS_VIOLATION, code);
failThis = TRUE;
}
sawException = TRUE;
}
if (!sawException) {
PrintToLog("ERROR: Cause an int 66 unknown interrupt (Access violation)\n");
ExceptionWoops(STATUS_ACCESS_VIOLATION, code);
failThis = TRUE;
}
// Test the result of an int divide by zero
sawException = FALSE;
__try {
int i, j, k;
i = 0;
j = 4;
k = j / i;
code = 0;
}
__except((code = GetExceptionCode()), 1 ) {
if (code == STATUS_INTEGER_DIVIDE_BY_ZERO) {
// PrintToLog("Saw int divide by zero\n");
}
else {
PrintToLog("ERROR: Cause an integer divide by zero\n");
ExceptionWoops(STATUS_INTEGER_DIVIDE_BY_ZERO, code);
failThis = TRUE;
}
sawException = TRUE;
}
if (!sawException) {
PrintToLog("ERROR: Cause an integer divide by zero\n");
ExceptionWoops(STATUS_INTEGER_DIVIDE_BY_ZERO, code);
failThis = TRUE;
}
// Test the result of an fp divide by zero
// PrintToLog("Before div0: Control is 0x%0.4x, Status is 0x%0.4x\n", _control87(0,0), _status87());
sawException = FALSE;
__try {
double x, y;
y = 0.0;
x = 1.0 / y ;
// PrintToLog("x is %lf\n", x);
code = 0;
}
__except((code = GetExceptionCode()), 1 ) {
// Don't actually get a divide by zero error, get
// so we should never hit this exception!
PrintToLog("Try a floating divide by zero\n");
PrintToLog("Woops! Saw an exception when we shouldn't have!\n");
sawException = TRUE;
failThis = TRUE;
}
// So you would think you'd get a float divide by zero error... Nope,
// you get X set to infinity...
if (code != 0) {
PrintToLog("ERROR: Try a floating divide by zero\n");
ExceptionWoops(0, code);
failThis = TRUE;
}
// PrintToLog("After div0: Control is 0x%0.4x, Status is 0x%0.4x\n", _control87(0,0), _status87());
// Test an int overflow (which actually does not cause an exception)
sawException = FALSE;
__try {
__asm {
into
}
// PrintToLog("into doesn't fault\n");
code = 0;
}
__except((code = GetExceptionCode()), 1 ) {
if (code == STATUS_INTEGER_OVERFLOW) {
// PrintToLog("Saw integer overflow\n");
}
else {
PrintToLog("ERROR: Try an into overflow fault\n");
ExceptionWoops(STATUS_INTEGER_OVERFLOW, code);
failThis = TRUE;
}
sawException = TRUE;
}
// Looks like integer overflow is ok... Is this a CRT thing?
if (code != 0) {
PrintToLog("ERROR: Try an into overflow fault\n");
ExceptionWoops(0, code);
failThis = TRUE;
}
// Test an illegal access
sawException = FALSE;
__try {
*p = 1;
code = 0;
}
__except((code = GetExceptionCode()), 1 ) {
if (code == STATUS_ACCESS_VIOLATION) {
// PrintToLog("Saw access violation\n");
}
else {
PrintToLog("ERROR: Cause an access violation\n");
ExceptionWoops(STATUS_ACCESS_VIOLATION, code);
failThis = TRUE;
}
sawException = TRUE;
}
if (!sawException) {
PrintToLog("ERROR: Cause an access violation\n");
ExceptionWoops(STATUS_ACCESS_VIOLATION, code);
failThis = TRUE;
}
//
// Finally, try a lot of exceptions (a loop) and verify we don't overflow
// the stack
//
for (i = 0; i < EXCEPTION_LOOP; i++) {
// Test an illegal access
sawException = FALSE;
__try {
*p = 1;
code = 0;
}
__except((code = GetExceptionCode()), 1 ) {
if (code == STATUS_ACCESS_VIOLATION) {
// PrintToLog("Saw access violation\n");
}
else {
PrintToLog("ERROR: Cause an access violation\n");
ExceptionWoops(STATUS_ACCESS_VIOLATION, code);
failThis = TRUE;
break;
}
sawException = TRUE;
}
if (!sawException) {
PrintToLog("ERROR: Cause an access violation\n");
ExceptionWoops(STATUS_ACCESS_VIOLATION, code);
failThis = TRUE;
break;
}
}
return failThis;
}
// Ok, go back to normal warnings...
#pragma warning(default:4756)
#pragma warning(default:4723)
#if defined(__BUILDMACHINE__)
#if defined(__BUILDDATE__)
#define B2(x, y) "" #x "." #y
#define B1(x, y) B2(x, y)
#define BUILD_MACHINE_TAG B1(__BUILDMACHINE__, __BUILDDATE__)
#else
#define B2(x) "" #x
#define B1(x) B2(x)
#define BUILD_MACHINE_TAG B1(__BUILDMACHINE__)
#endif
#else
#define BUILD_MACHINE_TAG ""
#endif
int __cdecl main(int argc, char *argv[])
{
NTSTATUS st;
HANDLE HandleList[2];
BOOL b;
DWORD dwExitCode;
// Disable buffering of the standard output handle
setvbuf(stdout, NULL, _IONBF, 0);
// Do some minimal command-line checking
if (argc < 2 || argc > 3) {
PrintToLog("Usage: wow64bvt log_file_name\n\n");
return 1;
} else if (strcmp(argv[1], "childprocess") == 0) {
return BeAChildProcess();
}
// We're the main exe
// Record the start time
time(&TestStartTime);
// Open the log file
fpLogFile = fopen(argv[1], "w");
if (!fpLogFile) {
PrintToLog("wow64bvt: Error: unable to create the log file '%s'\n", argv[1]);
return 1;
}
// Disable buffering of the log file handle
setvbuf(fpLogFile, NULL, _IONBF, 0);
// Print the initial banner
PrintToLog("[TESTRESULT]\n");
PrintToLog("[TEST LOGGING OUTPUT]\n");
PrintToLog("%s built on %s at %s by %s\n", argv[0], __DATE__, __TIME__, BUILD_MACHINE_TAG);
// Register the atexit handler - it closes the logging output section
// and prints the success/fail information as the BVT test exits.
atexit(AtExitHandler);
///////////////////////////// Test starts here //////////////////////////
// 32-bit child process creation from 32-bit parent. The parent instance
// (running now) tested 32-bit child from 64-bit parent
HandleList[0] = CreateTheChildProcess(argv[0], argv[1]);
// Create a thread do so some more work
HandleList[1] = CreateTheThread();
// Wait for everything to finish
WaitForMultipleObjects(sizeof(HandleList)/sizeof(HandleList[0]), HandleList, TRUE, INFINITE);
// Get the return code from the child process
b=GetExitCodeProcess(HandleList[0], &dwExitCode);
if (b) {
if (dwExitCode) {
// The child failed. We should fail too.
return (int)dwExitCode;
}
} else {
PrintToLog("ERROR: GetExitCodeProcess failed with LastError = %d\n", GetLastError());
return 1;
}
// Get the return code from the thread
b=GetExitCodeThread(HandleList[1], &dwExitCode);
if (b) {
if (dwExitCode) {
// The child failed. We should fail too.
return (int)dwExitCode;
}
} else {
PrintToLog("ERROR: GetExitCodeThread failed with LastError = %d\n", GetLastError());
return 1;
}
b = ExceptionCheck();
if (b) {
PrintToLog("ERROR: Exception Handling test.\n");
return 1;
}
b = TestGuardPages();
if (b) {
PrintToLog("ERROR: TestGuardPages().\n");
return 1;
}
b = TestMemoryMappedFiles();
if (b) {
PrintToLog("ERROR: TestMemoryMappedFiles().\n");
return 1;
}
b = TestVadSplitOnFree();
if (b) {
PrintToLog("ERROR: TestVadSplitOnFree()\n");
return 1;
}
b = TestMmPageProtection();
if (b) {
PrintToLog("ERROR: TestMmPageProtection()\n");
return 1;
}
b = TestX86MisalignedLock();
if (b) {
PrintToLog("ERROR: TestX86MisalignedLock()\n");
return 1;
}
// Everything finished OK. Clear the error flag and exit. The atexit
// callback function will finish filling out the log if this is the
// main thread.
g_bError = FALSE;
return 0;
}