ntdll: Add support for virtual write watches.
This commit is contained in:
parent
81b9ca5dbd
commit
af8bb2e922
|
@ -757,7 +757,7 @@ static void test_write_watch(void)
|
|||
if (!base &&
|
||||
(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == ERROR_NOT_SUPPORTED))
|
||||
{
|
||||
todo_wine win_skip( "MEM_WRITE_WATCH not supported\n" );
|
||||
win_skip( "MEM_WRITE_WATCH not supported\n" );
|
||||
return;
|
||||
}
|
||||
ok( base != NULL, "VirtualAlloc failed %u\n", GetLastError() );
|
||||
|
|
|
@ -178,6 +178,7 @@ static int VIRTUAL_GetUnixProt( BYTE vprot )
|
|||
if (vprot & VPROT_WRITE) prot |= PROT_WRITE;
|
||||
if (vprot & VPROT_WRITECOPY) prot |= PROT_WRITE;
|
||||
if (vprot & VPROT_EXEC) prot |= PROT_EXEC;
|
||||
if (vprot & VPROT_WRITEWATCH) prot &= ~PROT_WRITE;
|
||||
}
|
||||
if (!prot) prot = PROT_NONE;
|
||||
return prot;
|
||||
|
@ -565,18 +566,42 @@ static BOOL VIRTUAL_SetProt( FILE_VIEW *view, /* [in] Pointer to view */
|
|||
BYTE vprot ) /* [in] Protections to use */
|
||||
{
|
||||
int unix_prot = VIRTUAL_GetUnixProt(vprot);
|
||||
BYTE *p = view->prot + (((char *)base - (char *)view->base) >> page_shift);
|
||||
|
||||
TRACE("%p-%p %s\n",
|
||||
base, (char *)base + size - 1, VIRTUAL_GetProtStr( vprot ) );
|
||||
|
||||
if (view->protect & VPROT_WRITEWATCH)
|
||||
{
|
||||
/* each page may need different protections depending on write watch flag */
|
||||
UINT i, count;
|
||||
char *addr = base;
|
||||
int prot;
|
||||
|
||||
p[0] = vprot | (p[0] & VPROT_WRITEWATCH);
|
||||
unix_prot = VIRTUAL_GetUnixProt( p[0] );
|
||||
for (count = i = 1; i < size >> page_shift; i++, count++)
|
||||
{
|
||||
p[i] = vprot | (p[i] & VPROT_WRITEWATCH);
|
||||
prot = VIRTUAL_GetUnixProt( p[i] );
|
||||
if (prot == unix_prot) continue;
|
||||
mprotect( addr, count << page_shift, unix_prot );
|
||||
addr += count << page_shift;
|
||||
unix_prot = prot;
|
||||
count = 0;
|
||||
}
|
||||
if (count) mprotect( addr, count << page_shift, unix_prot );
|
||||
VIRTUAL_DEBUG_DUMP_VIEW( view );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* if setting stack guard pages, store the permissions first, as the guard may be
|
||||
* triggered at any point after mprotect and change the permissions again */
|
||||
if ((vprot & VPROT_GUARD) &&
|
||||
((char *)base >= (char *)NtCurrentTeb()->DeallocationStack) &&
|
||||
((char *)base < (char *)NtCurrentTeb()->Tib.StackBase))
|
||||
{
|
||||
memset( view->prot + (((char *)base - (char *)view->base) >> page_shift),
|
||||
vprot, size >> page_shift );
|
||||
memset( p, vprot, size >> page_shift );
|
||||
mprotect( base, size, unix_prot );
|
||||
VIRTUAL_DEBUG_DUMP_VIEW( view );
|
||||
return TRUE;
|
||||
|
@ -594,13 +619,40 @@ static BOOL VIRTUAL_SetProt( FILE_VIEW *view, /* [in] Pointer to view */
|
|||
if (mprotect( base, size, unix_prot )) return FALSE; /* FIXME: last error */
|
||||
|
||||
done:
|
||||
memset( view->prot + (((char *)base - (char *)view->base) >> page_shift),
|
||||
vprot, size >> page_shift );
|
||||
memset( p, vprot, size >> page_shift );
|
||||
VIRTUAL_DEBUG_DUMP_VIEW( view );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* reset_write_watches
|
||||
*
|
||||
* Reset write watches in a memory range.
|
||||
*/
|
||||
static void reset_write_watches( struct file_view *view, void *base, SIZE_T size )
|
||||
{
|
||||
SIZE_T i, count;
|
||||
int prot, unix_prot;
|
||||
char *addr = base;
|
||||
BYTE *p = view->prot + ((addr - (char *)view->base) >> page_shift);
|
||||
|
||||
p[0] |= VPROT_WRITEWATCH;
|
||||
unix_prot = VIRTUAL_GetUnixProt( p[0] );
|
||||
for (count = i = 1; i < size >> page_shift; i++, count++)
|
||||
{
|
||||
p[i] |= VPROT_WRITEWATCH;
|
||||
prot = VIRTUAL_GetUnixProt( p[i] );
|
||||
if (prot == unix_prot) continue;
|
||||
mprotect( addr, count << page_shift, unix_prot );
|
||||
addr += count << page_shift;
|
||||
unix_prot = prot;
|
||||
count = 0;
|
||||
}
|
||||
if (count) mprotect( addr, count << page_shift, unix_prot );
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* unmap_extra_space
|
||||
*
|
||||
|
@ -1411,12 +1463,18 @@ NTSTATUS virtual_handle_fault( LPCVOID addr, DWORD err )
|
|||
if ((view = VIRTUAL_FindView( addr, 0 )))
|
||||
{
|
||||
void *page = ROUND_ADDR( addr, page_mask );
|
||||
BYTE vprot = view->prot[((const char *)page - (const char *)view->base) >> page_shift];
|
||||
if (vprot & VPROT_GUARD)
|
||||
BYTE *vprot = &view->prot[((const char *)page - (const char *)view->base) >> page_shift];
|
||||
if (*vprot & VPROT_GUARD)
|
||||
{
|
||||
VIRTUAL_SetProt( view, page, page_size, vprot & ~VPROT_GUARD );
|
||||
VIRTUAL_SetProt( view, page, page_size, *vprot & ~VPROT_GUARD );
|
||||
ret = STATUS_GUARD_PAGE_VIOLATION;
|
||||
}
|
||||
if ((err & EXCEPTION_WRITE_FAULT) && (*vprot & VPROT_WRITEWATCH))
|
||||
{
|
||||
*vprot &= ~VPROT_WRITEWATCH;
|
||||
VIRTUAL_SetProt( view, page, page_size, *vprot );
|
||||
ret = STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
server_leave_uninterrupted_section( &csVirtual, &sigset );
|
||||
return ret;
|
||||
|
@ -1614,11 +1672,6 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG zero_
|
|||
WARN("called with wrong alloc type flags (%08x) !\n", type);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
if (type & MEM_WRITE_WATCH)
|
||||
{
|
||||
FIXME("MEM_WRITE_WATCH type not supported\n");
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Reserve the memory */
|
||||
|
||||
|
@ -1626,6 +1679,7 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG zero_
|
|||
|
||||
if ((type & MEM_RESERVE) || !base)
|
||||
{
|
||||
if (type & MEM_WRITE_WATCH) vprot |= VPROT_WRITEWATCH;
|
||||
status = map_view( &view, base, size, mask, type & MEM_TOP_DOWN, vprot );
|
||||
if (status == STATUS_SUCCESS) base = view->base;
|
||||
}
|
||||
|
@ -1974,7 +2028,7 @@ NTSTATUS WINAPI NtQueryVirtualMemory( HANDLE process, LPCVOID addr,
|
|||
else if (view->protect & VPROT_VALLOC) info->Type = MEM_PRIVATE;
|
||||
else info->Type = MEM_MAPPED;
|
||||
for (size = base - alloc_base; size < base + range_size - alloc_base; size += page_size)
|
||||
if (view->prot[size >> page_shift] != vprot) break;
|
||||
if ((view->prot[size >> page_shift] ^ vprot) & ~VPROT_WRITEWATCH) break;
|
||||
info->RegionSize = size - (base - alloc_base);
|
||||
}
|
||||
server_leave_uninterrupted_section( &csVirtual, &sigset );
|
||||
|
@ -2398,8 +2452,44 @@ NTSTATUS WINAPI NtFlushVirtualMemory( HANDLE process, LPCVOID *addr_ptr,
|
|||
NTSTATUS WINAPI NtGetWriteWatch( HANDLE process, ULONG flags, PVOID base, SIZE_T size, PVOID *addresses,
|
||||
ULONG_PTR *count, ULONG *granularity )
|
||||
{
|
||||
FIXME( "%p %x %p-%p %p %lu\n", process, flags, base, (char *)base + size, addresses, *count );
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
struct file_view *view;
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
sigset_t sigset;
|
||||
|
||||
size = ROUND_SIZE( base, size );
|
||||
base = ROUND_ADDR( base, page_mask );
|
||||
|
||||
if (!count || !granularity) return STATUS_ACCESS_VIOLATION;
|
||||
if (!*count || !size) return STATUS_INVALID_PARAMETER;
|
||||
if (flags & ~WRITE_WATCH_FLAG_RESET) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
if (!addresses) return STATUS_ACCESS_VIOLATION;
|
||||
|
||||
TRACE( "%p %x %p-%p %p %lu\n", process, flags, base, (char *)base + size,
|
||||
addresses, count ? *count : 0 );
|
||||
|
||||
server_enter_uninterrupted_section( &csVirtual, &sigset );
|
||||
|
||||
if ((view = VIRTUAL_FindView( base, size )) && (view->protect & VPROT_WRITEWATCH))
|
||||
{
|
||||
ULONG_PTR pos = 0;
|
||||
char *addr = base;
|
||||
char *end = addr + size;
|
||||
|
||||
while (pos < *count && addr < end)
|
||||
{
|
||||
BYTE prot = view->prot[(addr - (char *)view->base) >> page_shift];
|
||||
if (!(prot & VPROT_WRITEWATCH)) addresses[pos++] = addr;
|
||||
addr += page_size;
|
||||
}
|
||||
if (flags & WRITE_WATCH_FLAG_RESET) reset_write_watches( view, base, addr - (char *)base );
|
||||
*count = pos;
|
||||
*granularity = page_size;
|
||||
}
|
||||
else status = STATUS_INVALID_PARAMETER;
|
||||
|
||||
server_leave_uninterrupted_section( &csVirtual, &sigset );
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2409,8 +2499,26 @@ NTSTATUS WINAPI NtGetWriteWatch( HANDLE process, ULONG flags, PVOID base, SIZE_T
|
|||
*/
|
||||
NTSTATUS WINAPI NtResetWriteWatch( HANDLE process, PVOID base, SIZE_T size )
|
||||
{
|
||||
FIXME( "%p %p-%p\n", process, base, (char *)base + size );
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
struct file_view *view;
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
sigset_t sigset;
|
||||
|
||||
size = ROUND_SIZE( base, size );
|
||||
base = ROUND_ADDR( base, page_mask );
|
||||
|
||||
TRACE( "%p %p-%p\n", process, base, (char *)base + size );
|
||||
|
||||
if (!size) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
server_enter_uninterrupted_section( &csVirtual, &sigset );
|
||||
|
||||
if ((view = VIRTUAL_FindView( base, size )) && (view->protect & VPROT_WRITEWATCH))
|
||||
reset_write_watches( view, base, size );
|
||||
else
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
|
||||
server_leave_uninterrupted_section( &csVirtual, &sigset );
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1688,6 +1688,7 @@ struct create_mapping_reply
|
|||
#define VPROT_GUARD 0x10
|
||||
#define VPROT_NOCACHE 0x20
|
||||
#define VPROT_COMMITTED 0x40
|
||||
#define VPROT_WRITEWATCH 0x80
|
||||
|
||||
#define VPROT_IMAGE 0x0100
|
||||
#define VPROT_SYSTEM 0x0200
|
||||
|
|
|
@ -1330,6 +1330,7 @@ enum char_info_mode
|
|||
#define VPROT_GUARD 0x10
|
||||
#define VPROT_NOCACHE 0x20
|
||||
#define VPROT_COMMITTED 0x40
|
||||
#define VPROT_WRITEWATCH 0x80
|
||||
/* per-mapping protection flags */
|
||||
#define VPROT_IMAGE 0x0100 /* mapping for an exe image */
|
||||
#define VPROT_SYSTEM 0x0200 /* system view (underlying mmap not under our control) */
|
||||
|
|
Loading…
Reference in New Issue