You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1951 lines
53 KiB
1951 lines
53 KiB
// 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.
|
|
SYSTEM_INFO SysInfo;
|
|
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;
|
|
}
|
|
|
|
|
|
//
|
|
// this routine is used by the two FP tests below
|
|
//
|
|
int WINAPI FpExcpFilter(LPEXCEPTION_POINTERS lper, int contType)
|
|
{
|
|
lper->ContextRecord->FloatSave.ControlWord = 0x33F;
|
|
|
|
return contType;
|
|
}
|
|
BOOL TestFPContext()
|
|
{
|
|
BOOL bError = FALSE;
|
|
INT i;
|
|
INT j;
|
|
|
|
EXCEPTION_POINTERS *exceptPtrs;
|
|
|
|
// 8 registers * 10 bytes per register
|
|
// Plus a 9th register to make sure we overflow when we should
|
|
//
|
|
char fpArray[90];
|
|
short controlWord = 0x0300;
|
|
|
|
PrintToLog("WOW64BVT: Testing X86 FP Context...\n");
|
|
|
|
__try
|
|
{
|
|
for (i = 0; i < 90; i++) {
|
|
fpArray[i] = (char) i;
|
|
}
|
|
|
|
__try
|
|
{
|
|
_asm {
|
|
fninit
|
|
fldcw word ptr controlWord
|
|
fld tbyte ptr fpArray
|
|
fld tbyte ptr fpArray + 10
|
|
fld tbyte ptr fpArray + 20
|
|
fld tbyte ptr fpArray + 30
|
|
fld tbyte ptr fpArray + 40
|
|
fld tbyte ptr fpArray + 50
|
|
fld tbyte ptr fpArray + 60
|
|
fld tbyte ptr fpArray + 70
|
|
fwait
|
|
|
|
// This next push should cause an overflow exception
|
|
fld tbyte ptr fpArray + 80
|
|
fwait
|
|
}
|
|
|
|
bError = TRUE;
|
|
printf("ERROR: WOW64BVT: Didn't see stack overflow\n");
|
|
}
|
|
__except(exceptPtrs = GetExceptionInformation(), EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
EXCEPTION_RECORD *exceptionRecord;
|
|
CONTEXT *context;
|
|
char *tmpPtr;
|
|
|
|
|
|
//
|
|
// Execute the error handling in a known good fp state
|
|
//
|
|
_asm fninit
|
|
|
|
exceptionRecord = exceptPtrs->ExceptionRecord;
|
|
context = exceptPtrs->ContextRecord;
|
|
|
|
// Now make sure the exception we saw was the one we expected
|
|
if (exceptionRecord->ExceptionCode != STATUS_FLOAT_STACK_CHECK) {
|
|
bError = TRUE;
|
|
printf("ERROR: WOW64BVT: Didn't see stack overflow STATUS. Saw 0x%08x\n", exceptionRecord->ExceptionCode);
|
|
goto failFpContent;
|
|
}
|
|
|
|
if ((context->FloatSave.StatusWord & 0x77f) != 0x241) {
|
|
bError = TRUE;
|
|
printf("ERROR: WOW64BVT: Didn't see stack overflow status bits\n");
|
|
goto failFpContent;
|
|
}
|
|
|
|
tmpPtr = &(context->FloatSave.RegisterArea[0]);
|
|
|
|
// And verify the register contents
|
|
for (i = 7; i >= 0; i--) {
|
|
for (j = i * 10; j < ((i+1) * 10); j++) {
|
|
if (*tmpPtr++ != (char) j) {
|
|
bError = TRUE;
|
|
printf("ERROR: WOW64BVT: Didn't see correct fp context\n");
|
|
goto failFpContent;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Unfortunately, for the Merced Processor, the IIP has the
|
|
// address of the wait intstruction following the fld that
|
|
// overflowed the stack. For an x86 processor, the EIP
|
|
// has the address of the fld instruction. Both architectures
|
|
// put the fld instruction in the fir register, so lets hope
|
|
// people use that to look for FP error addresses...
|
|
//
|
|
// Thus, we cannot do the following style of test
|
|
//
|
|
// ASSERT(((ULONG) context->FloatSave.ErrorOffset) ==
|
|
// ((ULONG) exceptionRecord->ExceptionAddress));
|
|
//
|
|
|
|
failFpContent:
|
|
;
|
|
}
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
bError = TRUE;
|
|
printf("ERROR: WOW64BVT: Exception %lx\n", GetExceptionCode());
|
|
}
|
|
|
|
if (bError == FALSE) {
|
|
PrintToLog("WOW64BVT: Testing X86 FP Context...OK\n");
|
|
} else {
|
|
PrintToLog("ERROR: WOW64BVT: Testing X86 FP Context\n");
|
|
}
|
|
|
|
return bError;
|
|
}
|
|
|
|
|
|
BOOL TestMMXException()
|
|
{
|
|
LONG bError = TRUE;
|
|
|
|
unsigned short op1[5] = {0x0000, 0x0000, 0x0000, 0x8000, 0x7ffe};
|
|
unsigned short res[5];
|
|
short controlWord = 0x0308;
|
|
|
|
|
|
PrintToLog("WOW64BVT: Testing X86 MMX exception...\n");
|
|
|
|
__try
|
|
{
|
|
_asm {
|
|
fninit
|
|
fld tbyte ptr op1
|
|
fld tbyte ptr op1
|
|
fldcw word ptr controlWord
|
|
}
|
|
|
|
__try
|
|
{
|
|
_asm faddp ST(1),ST(0)
|
|
_asm fstp tbyte ptr res
|
|
|
|
}
|
|
__except(FpExcpFilter(GetExceptionInformation(), EXCEPTION_CONTINUE_EXECUTION)) {
|
|
// Do nothing
|
|
}
|
|
|
|
|
|
_asm {
|
|
|
|
cmp DWORD PTR [res + 6],0x7FFF8000
|
|
jne ADD_FAILED
|
|
mov bError,0
|
|
ADD_FAILED:
|
|
}
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
bError = TRUE;
|
|
printf("ERROR: WOW64BVT: Exception %lx\n", GetExceptionCode());
|
|
}
|
|
|
|
if (bError == FALSE) {
|
|
PrintToLog("WOW64BVT: Testing X86 MMX exception...OK\n");
|
|
} else {
|
|
PrintToLog("ERROR: WOW64BVT: Testing X86 MMX exception\n");
|
|
}
|
|
|
|
return (BOOL) bError;
|
|
}
|
|
|
|
BOOL TestX86SelectorLoad()
|
|
{
|
|
|
|
BOOL bError = FALSE;
|
|
|
|
PrintToLog("WOW64BVT: Testing X86 selector-load...\n");
|
|
|
|
__try
|
|
{
|
|
__asm
|
|
{
|
|
mov ax, ss;
|
|
mov ss, ax;
|
|
|
|
mov ax, ds;
|
|
mov ds, ax;
|
|
|
|
mov ax, es;
|
|
mov es, ax;
|
|
|
|
mov ax, fs;
|
|
mov fs, ax;
|
|
}
|
|
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
bError = TRUE;
|
|
printf("ERROR: WOW64BVT: Exception %lx\n", GetExceptionCode());
|
|
}
|
|
|
|
if (bError == FALSE) {
|
|
PrintToLog("WOW64BVT: Testing X86 selector-load...OK\n");
|
|
} else {
|
|
PrintToLog("ERROR: WOW64BVT: Testing X86 selector-load\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 ) {
|
|
ULONG checkCode;
|
|
|
|
if (SysInfo.dwProcessorType == PROCESSOR_AMD_X8664) {
|
|
checkCode = STATUS_ACCESS_VIOLATION;
|
|
} else {
|
|
checkCode = STATUS_PRIVILEGED_INSTRUCTION;
|
|
}
|
|
|
|
if (code == checkCode) {
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
if (failThis) {
|
|
PrintToLog("ERROR: Testing Exception Handling\n");
|
|
}
|
|
else {
|
|
PrintToLog("WOW64BVT: Testing Exception Handling... OK\n");
|
|
}
|
|
|
|
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;
|
|
|
|
// get native system information
|
|
GetNativeSystemInfo (&SysInfo);
|
|
|
|
// 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;
|
|
}
|
|
|
|
b = TestFPContext();
|
|
if (b) {
|
|
PrintToLog("ERROR: TestFPContext()\n");
|
|
return 1;
|
|
}
|
|
|
|
b = TestMMXException();
|
|
if (b) {
|
|
PrintToLog("ERROR: TestMMXException()\n");
|
|
return 1;
|
|
}
|
|
|
|
b = TestX86SelectorLoad();
|
|
if (b) {
|
|
PrintToLog("ERROR: TestX86SelectorLoad()\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.
|
|
RtlZeroMemory (&g_bError, sizeof (g_bError));
|
|
|
|
return 0;
|
|
}
|