kernel32/tests: Add tests for PAGE_GUARD memory protection.
This commit is contained in:
parent
89e9d6db33
commit
53611dfbab
|
@ -25,8 +25,10 @@
|
||||||
#define WIN32_NO_STATUS
|
#define WIN32_NO_STATUS
|
||||||
#include "windef.h"
|
#include "windef.h"
|
||||||
#include "winbase.h"
|
#include "winbase.h"
|
||||||
|
#include "winnt.h"
|
||||||
#include "winternl.h"
|
#include "winternl.h"
|
||||||
#include "winerror.h"
|
#include "winerror.h"
|
||||||
|
#include "excpt.h"
|
||||||
#include "wine/test.h"
|
#include "wine/test.h"
|
||||||
|
|
||||||
#define NUM_THREADS 4
|
#define NUM_THREADS 4
|
||||||
|
@ -40,6 +42,7 @@ static UINT (WINAPI *pResetWriteWatch)(LPVOID,SIZE_T);
|
||||||
static NTSTATUS (WINAPI *pNtAreMappedFilesTheSame)(PVOID,PVOID);
|
static NTSTATUS (WINAPI *pNtAreMappedFilesTheSame)(PVOID,PVOID);
|
||||||
static NTSTATUS (WINAPI *pNtMapViewOfSection)(HANDLE, HANDLE, PVOID *, ULONG, SIZE_T, const LARGE_INTEGER *, SIZE_T *, ULONG, ULONG, ULONG);
|
static NTSTATUS (WINAPI *pNtMapViewOfSection)(HANDLE, HANDLE, PVOID *, ULONG, SIZE_T, const LARGE_INTEGER *, SIZE_T *, ULONG, ULONG, ULONG);
|
||||||
static DWORD (WINAPI *pNtUnmapViewOfSection)(HANDLE, PVOID);
|
static DWORD (WINAPI *pNtUnmapViewOfSection)(HANDLE, PVOID);
|
||||||
|
static struct _TEB * (WINAPI *pNtCurrentTeb)(void);
|
||||||
|
|
||||||
/* ############################### */
|
/* ############################### */
|
||||||
|
|
||||||
|
@ -1543,6 +1546,260 @@ static void test_write_watch(void)
|
||||||
VirtualFree( base, 0, MEM_FREE );
|
VirtualFree( base, 0, MEM_FREE );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __i386__
|
||||||
|
|
||||||
|
static DWORD num_guard_page_calls;
|
||||||
|
|
||||||
|
static DWORD guard_page_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
|
||||||
|
CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
|
||||||
|
{
|
||||||
|
trace( "exception: %08x flags:%x addr:%p\n",
|
||||||
|
rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
|
||||||
|
|
||||||
|
ok( rec->NumberParameters == 2, "NumberParameters is %d instead of 2\n", rec->NumberParameters );
|
||||||
|
ok( rec->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION, "ExceptionCode is %08x instead of %08x\n",
|
||||||
|
rec->ExceptionCode, STATUS_GUARD_PAGE_VIOLATION );
|
||||||
|
|
||||||
|
num_guard_page_calls++;
|
||||||
|
*(int *)rec->ExceptionInformation[1] += 0x100;
|
||||||
|
|
||||||
|
return ExceptionContinueExecution;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_guard_page(void)
|
||||||
|
{
|
||||||
|
EXCEPTION_REGISTRATION_RECORD frame;
|
||||||
|
MEMORY_BASIC_INFORMATION info;
|
||||||
|
DWORD ret, size, old_prot;
|
||||||
|
int *value, old_value;
|
||||||
|
void *results[64];
|
||||||
|
ULONG_PTR count;
|
||||||
|
ULONG pagesize;
|
||||||
|
BOOL success;
|
||||||
|
char *base;
|
||||||
|
|
||||||
|
if (!pNtCurrentTeb)
|
||||||
|
{
|
||||||
|
win_skip( "NtCurrentTeb not supported\n" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = 0x1000;
|
||||||
|
base = VirtualAlloc( 0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE | PAGE_GUARD );
|
||||||
|
ok( base != NULL, "VirtualAlloc failed %u\n", GetLastError() );
|
||||||
|
value = (int *)base;
|
||||||
|
|
||||||
|
/* verify info structure */
|
||||||
|
ret = VirtualQuery( base, &info, sizeof(info) );
|
||||||
|
ok( ret, "VirtualQuery failed %u\n", GetLastError());
|
||||||
|
ok( info.BaseAddress == base, "BaseAddress %p instead of %p\n", info.BaseAddress, base );
|
||||||
|
ok( info.AllocationProtect == (PAGE_READWRITE | PAGE_GUARD), "wrong AllocationProtect %x\n", info.AllocationProtect );
|
||||||
|
ok( info.RegionSize == size, "wrong RegionSize 0x%lx\n", info.RegionSize );
|
||||||
|
ok( info.State == MEM_COMMIT, "wrong State 0x%x\n", info.State );
|
||||||
|
ok( info.Protect == (PAGE_READWRITE | PAGE_GUARD), "wrong Protect 0x%x\n", info.Protect );
|
||||||
|
ok( info.Type == MEM_PRIVATE, "wrong Type 0x%x\n", info.Type );
|
||||||
|
|
||||||
|
/* put some initial value into the memory */
|
||||||
|
success = VirtualProtect( base, size, PAGE_READWRITE, &old_prot );
|
||||||
|
ok( success, "VirtualProtect failed %u\n", GetLastError() );
|
||||||
|
ok( old_prot == (PAGE_READWRITE | PAGE_GUARD), "wrong old prot %x\n", old_prot );
|
||||||
|
|
||||||
|
*value = 1;
|
||||||
|
*(value + 1) = 2;
|
||||||
|
|
||||||
|
success = VirtualProtect( base, size, PAGE_READWRITE | PAGE_GUARD, &old_prot );
|
||||||
|
ok( success, "VirtualProtect failed %u\n", GetLastError() );
|
||||||
|
ok( old_prot == PAGE_READWRITE, "wrong old prot %x\n", old_prot );
|
||||||
|
|
||||||
|
/* test behaviour of VirtualLock - first attempt should fail */
|
||||||
|
SetLastError( 0xdeadbeef );
|
||||||
|
success = VirtualLock( base, size );
|
||||||
|
ok( !success, "VirtualLock unexpectedly succeded\n" );
|
||||||
|
todo_wine
|
||||||
|
ok( GetLastError() == STATUS_GUARD_PAGE_VIOLATION, "wrong error %u\n", GetLastError() );
|
||||||
|
|
||||||
|
success = VirtualLock( base, size );
|
||||||
|
todo_wine
|
||||||
|
ok( success, "VirtualLock failed %u\n", GetLastError() );
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
ok( *value == 1, "memory block contains wrong value, expected 1, got 0x%x\n", *value );
|
||||||
|
success = VirtualUnlock( base, size );
|
||||||
|
ok( success, "VirtualUnlock failed %u\n", GetLastError() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check info structure again, PAGE_GUARD should be removed now */
|
||||||
|
ret = VirtualQuery( base, &info, sizeof(info) );
|
||||||
|
ok( ret, "VirtualQuery failed %u\n", GetLastError());
|
||||||
|
ok( info.BaseAddress == base, "BaseAddress %p instead of %p\n", info.BaseAddress, base );
|
||||||
|
ok( info.AllocationProtect == (PAGE_READWRITE | PAGE_GUARD), "wrong AllocationProtect %x\n", info.AllocationProtect );
|
||||||
|
ok( info.RegionSize == size, "wrong RegionSize 0x%lx\n", info.RegionSize );
|
||||||
|
ok( info.State == MEM_COMMIT, "wrong State 0x%x\n", info.State );
|
||||||
|
todo_wine
|
||||||
|
ok( info.Protect == PAGE_READWRITE, "wrong Protect 0x%x\n", info.Protect );
|
||||||
|
ok( info.Type == MEM_PRIVATE, "wrong Type 0x%x\n", info.Type );
|
||||||
|
|
||||||
|
success = VirtualProtect( base, size, PAGE_READWRITE | PAGE_GUARD, &old_prot );
|
||||||
|
ok( success, "VirtualProtect failed %u\n", GetLastError() );
|
||||||
|
todo_wine
|
||||||
|
ok( old_prot == PAGE_READWRITE, "wrong old prot %x\n", old_prot );
|
||||||
|
|
||||||
|
/* test directly accessing the memory - we need to setup an exception handler first */
|
||||||
|
frame.Handler = guard_page_handler;
|
||||||
|
frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
|
||||||
|
pNtCurrentTeb()->Tib.ExceptionList = &frame;
|
||||||
|
|
||||||
|
num_guard_page_calls = 0;
|
||||||
|
old_value = *value; /* exception handler increments value by 0x100 */
|
||||||
|
*value = 2;
|
||||||
|
ok( old_value == 0x101, "memory block contains wrong value, expected 0x101, got 0x%x\n", old_value );
|
||||||
|
ok( num_guard_page_calls == 1, "expected one callback of guard page handler, got %d calls\n", num_guard_page_calls );
|
||||||
|
|
||||||
|
pNtCurrentTeb()->Tib.ExceptionList = frame.Prev;
|
||||||
|
|
||||||
|
/* check info structure again, PAGE_GUARD should be removed now */
|
||||||
|
ret = VirtualQuery( base, &info, sizeof(info) );
|
||||||
|
ok( ret, "VirtualQuery failed %u\n", GetLastError());
|
||||||
|
ok( info.Protect == PAGE_READWRITE, "wrong Protect 0x%x\n", info.Protect );
|
||||||
|
|
||||||
|
success = VirtualProtect( base, size, PAGE_READWRITE | PAGE_GUARD, &old_prot );
|
||||||
|
ok( success, "VirtualProtect failed %u\n", GetLastError() );
|
||||||
|
ok( old_prot == PAGE_READWRITE, "wrong old prot %x\n", old_prot );
|
||||||
|
|
||||||
|
/* test accessing second integer in memory */
|
||||||
|
frame.Handler = guard_page_handler;
|
||||||
|
frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
|
||||||
|
pNtCurrentTeb()->Tib.ExceptionList = &frame;
|
||||||
|
|
||||||
|
num_guard_page_calls = 0;
|
||||||
|
old_value = *(value + 1);
|
||||||
|
ok( old_value == 0x102, "memory block contains wrong value, expected 0x102, got 0x%x\n", old_value );
|
||||||
|
ok( *value == 2, "memory block contains wrong value, expected 2, got 0x%x\n", *value );
|
||||||
|
ok( num_guard_page_calls == 1, "expected one callback of guard page handler, got %d calls\n", num_guard_page_calls );
|
||||||
|
|
||||||
|
pNtCurrentTeb()->Tib.ExceptionList = frame.Prev;
|
||||||
|
|
||||||
|
success = VirtualLock( base, size );
|
||||||
|
ok( success, "VirtualLock failed %u\n", GetLastError() );
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
ok( *value == 2, "memory block contains wrong value, expected 2, got 0x%x\n", *value );
|
||||||
|
success = VirtualUnlock( base, size );
|
||||||
|
ok( success, "VirtualUnlock failed %u\n", GetLastError() );
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFree( base, 0, MEM_FREE );
|
||||||
|
|
||||||
|
/* combined guard page / write watch tests */
|
||||||
|
if (!pGetWriteWatch || !pResetWriteWatch)
|
||||||
|
{
|
||||||
|
win_skip( "GetWriteWatch not supported, skipping combined guard page / write watch tests\n" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base = VirtualAlloc( 0, size, MEM_RESERVE | MEM_COMMIT | MEM_WRITE_WATCH, PAGE_READWRITE | PAGE_GUARD );
|
||||||
|
if (!base && (GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == ERROR_NOT_SUPPORTED))
|
||||||
|
{
|
||||||
|
win_skip( "MEM_WRITE_WATCH not supported\n" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ok( base != NULL, "VirtualAlloc failed %u\n", GetLastError() );
|
||||||
|
value = (int *)base;
|
||||||
|
|
||||||
|
ret = VirtualQuery( base, &info, sizeof(info) );
|
||||||
|
ok( ret, "VirtualQuery failed %u\n", GetLastError() );
|
||||||
|
ok( info.BaseAddress == base, "BaseAddress %p instead of %p\n", info.BaseAddress, base );
|
||||||
|
ok( info.AllocationProtect == (PAGE_READWRITE | PAGE_GUARD), "wrong AllocationProtect %x\n", info.AllocationProtect );
|
||||||
|
ok( info.RegionSize == size, "wrong RegionSize 0x%lx\n", info.RegionSize );
|
||||||
|
ok( info.State == MEM_COMMIT, "wrong State 0x%x\n", info.State );
|
||||||
|
ok( info.Protect == (PAGE_READWRITE | PAGE_GUARD), "wrong Protect 0x%x\n", info.Protect );
|
||||||
|
ok( info.Type == MEM_PRIVATE, "wrong Type 0x%x\n", info.Type );
|
||||||
|
|
||||||
|
count = 64;
|
||||||
|
ret = pGetWriteWatch( 0, base, size, results, &count, &pagesize );
|
||||||
|
ok( !ret, "GetWriteWatch failed %u\n", GetLastError() );
|
||||||
|
ok( count == 0, "wrong count %lu\n", count );
|
||||||
|
|
||||||
|
/* writing to a page should trigger should trigger guard page, even if write watch is set */
|
||||||
|
frame.Handler = guard_page_handler;
|
||||||
|
frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
|
||||||
|
pNtCurrentTeb()->Tib.ExceptionList = &frame;
|
||||||
|
|
||||||
|
num_guard_page_calls = 0;
|
||||||
|
*value = 1;
|
||||||
|
*(value + 1) = 2;
|
||||||
|
todo_wine
|
||||||
|
ok( num_guard_page_calls == 1, "expected one callback of guard page handler, got %d calls\n", num_guard_page_calls );
|
||||||
|
|
||||||
|
pNtCurrentTeb()->Tib.ExceptionList = frame.Prev;
|
||||||
|
|
||||||
|
count = 64;
|
||||||
|
ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize );
|
||||||
|
ok( !ret, "GetWriteWatch failed %u\n", GetLastError() );
|
||||||
|
ok( count == 1, "wrong count %lu\n", count );
|
||||||
|
ok( results[0] == base, "wrong result %p\n", results[0] );
|
||||||
|
|
||||||
|
success = VirtualProtect( base, size, PAGE_READWRITE | PAGE_GUARD, &old_prot );
|
||||||
|
ok( success, "VirtualProtect failed %u\n", GetLastError() );
|
||||||
|
|
||||||
|
/* write watch is triggered from inside of the guard page handler */
|
||||||
|
frame.Handler = guard_page_handler;
|
||||||
|
frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
|
||||||
|
pNtCurrentTeb()->Tib.ExceptionList = &frame;
|
||||||
|
|
||||||
|
num_guard_page_calls = 0;
|
||||||
|
old_value = *(value + 1); /* doesn't trigger write watch */
|
||||||
|
ok( old_value == 0x102, "memory block contains wrong value, expected 0x102, got 0x%x\n", old_value );
|
||||||
|
ok( *value == 1, "memory block contains wrong value, expected 1, got 0x%x\n", *value );
|
||||||
|
ok( num_guard_page_calls == 1, "expected one callback of guard page handler, got %d calls\n", num_guard_page_calls );
|
||||||
|
|
||||||
|
pNtCurrentTeb()->Tib.ExceptionList = frame.Prev;
|
||||||
|
|
||||||
|
count = 64;
|
||||||
|
ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize );
|
||||||
|
ok( !ret, "GetWriteWatch failed %u\n", GetLastError() );
|
||||||
|
ok( count == 1, "wrong count %lu\n", count );
|
||||||
|
ok( results[0] == base, "wrong result %p\n", results[0] );
|
||||||
|
|
||||||
|
success = VirtualProtect( base, size, PAGE_READWRITE | PAGE_GUARD, &old_prot );
|
||||||
|
ok( success, "VirtualProtect failed %u\n", GetLastError() );
|
||||||
|
|
||||||
|
/* test behaviour of VirtualLock - first attempt should fail without triggering write watches */
|
||||||
|
SetLastError( 0xdeadbeef );
|
||||||
|
success = VirtualLock( base, size );
|
||||||
|
ok( !success, "VirtualLock unexpectedly succeded\n" );
|
||||||
|
todo_wine
|
||||||
|
ok( GetLastError() == STATUS_GUARD_PAGE_VIOLATION, "wrong error %u\n", GetLastError() );
|
||||||
|
|
||||||
|
count = 64;
|
||||||
|
ret = pGetWriteWatch( 0, base, size, results, &count, &pagesize );
|
||||||
|
ok( !ret, "GetWriteWatch failed %u\n", GetLastError() );
|
||||||
|
ok( count == 0, "wrong count %lu\n", count );
|
||||||
|
|
||||||
|
success = VirtualLock( base, size );
|
||||||
|
todo_wine
|
||||||
|
ok( success, "VirtualLock failed %u\n", GetLastError() );
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
ok( *value == 1, "memory block contains wrong value, expected 1, got 0x%x\n", *value );
|
||||||
|
success = VirtualUnlock( base, size );
|
||||||
|
ok( success, "VirtualUnlock failed %u\n", GetLastError() );
|
||||||
|
}
|
||||||
|
|
||||||
|
count = 64;
|
||||||
|
results[0] = (void *)0xdeadbeef;
|
||||||
|
ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize );
|
||||||
|
ok( !ret, "GetWriteWatch failed %u\n", GetLastError() );
|
||||||
|
todo_wine
|
||||||
|
ok( count == 1 || broken(count == 0) /* Windows 8 */, "wrong count %lu\n", count );
|
||||||
|
todo_wine
|
||||||
|
ok( results[0] == base || broken(results[0] == (void *)0xdeadbeef) /* Windows 8 */, "wrong result %p\n", results[0] );
|
||||||
|
|
||||||
|
VirtualFree( base, 0, MEM_FREE );
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __i386__ */
|
||||||
|
|
||||||
static void test_VirtualProtect(void)
|
static void test_VirtualProtect(void)
|
||||||
{
|
{
|
||||||
static const struct test_data
|
static const struct test_data
|
||||||
|
@ -2581,6 +2838,7 @@ START_TEST(virtual)
|
||||||
"NtAreMappedFilesTheSame" );
|
"NtAreMappedFilesTheSame" );
|
||||||
pNtMapViewOfSection = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtMapViewOfSection");
|
pNtMapViewOfSection = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtMapViewOfSection");
|
||||||
pNtUnmapViewOfSection = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtUnmapViewOfSection");
|
pNtUnmapViewOfSection = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtUnmapViewOfSection");
|
||||||
|
pNtCurrentTeb = (void *)GetProcAddress( GetModuleHandleA("ntdll.dll"), "NtCurrentTeb" );
|
||||||
|
|
||||||
test_shared_memory(FALSE);
|
test_shared_memory(FALSE);
|
||||||
test_shared_memory_ro(FALSE, FILE_MAP_READ|FILE_MAP_WRITE);
|
test_shared_memory_ro(FALSE, FILE_MAP_READ|FILE_MAP_WRITE);
|
||||||
|
@ -2600,4 +2858,7 @@ START_TEST(virtual)
|
||||||
test_IsBadWritePtr();
|
test_IsBadWritePtr();
|
||||||
test_IsBadCodePtr();
|
test_IsBadCodePtr();
|
||||||
test_write_watch();
|
test_write_watch();
|
||||||
|
#ifdef __i386__
|
||||||
|
test_guard_page();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue