Commit af8bb2e9 authored by Alexandre Julliard's avatar Alexandre Julliard

ntdll: Add support for virtual write watches.

parent 81b9ca5d
...@@ -757,7 +757,7 @@ static void test_write_watch(void) ...@@ -757,7 +757,7 @@ static void test_write_watch(void)
if (!base && if (!base &&
(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == ERROR_NOT_SUPPORTED)) (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; return;
} }
ok( base != NULL, "VirtualAlloc failed %u\n", GetLastError() ); ok( base != NULL, "VirtualAlloc failed %u\n", GetLastError() );
......
...@@ -178,6 +178,7 @@ static int VIRTUAL_GetUnixProt( BYTE vprot ) ...@@ -178,6 +178,7 @@ static int VIRTUAL_GetUnixProt( BYTE vprot )
if (vprot & VPROT_WRITE) prot |= PROT_WRITE; if (vprot & VPROT_WRITE) prot |= PROT_WRITE;
if (vprot & VPROT_WRITECOPY) prot |= PROT_WRITE; if (vprot & VPROT_WRITECOPY) prot |= PROT_WRITE;
if (vprot & VPROT_EXEC) prot |= PROT_EXEC; if (vprot & VPROT_EXEC) prot |= PROT_EXEC;
if (vprot & VPROT_WRITEWATCH) prot &= ~PROT_WRITE;
} }
if (!prot) prot = PROT_NONE; if (!prot) prot = PROT_NONE;
return prot; return prot;
...@@ -565,18 +566,42 @@ static BOOL VIRTUAL_SetProt( FILE_VIEW *view, /* [in] Pointer to view */ ...@@ -565,18 +566,42 @@ static BOOL VIRTUAL_SetProt( FILE_VIEW *view, /* [in] Pointer to view */
BYTE vprot ) /* [in] Protections to use */ BYTE vprot ) /* [in] Protections to use */
{ {
int unix_prot = VIRTUAL_GetUnixProt(vprot); int unix_prot = VIRTUAL_GetUnixProt(vprot);
BYTE *p = view->prot + (((char *)base - (char *)view->base) >> page_shift);
TRACE("%p-%p %s\n", TRACE("%p-%p %s\n",
base, (char *)base + size - 1, VIRTUAL_GetProtStr( vprot ) ); 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 /* 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 */ * triggered at any point after mprotect and change the permissions again */
if ((vprot & VPROT_GUARD) && if ((vprot & VPROT_GUARD) &&
((char *)base >= (char *)NtCurrentTeb()->DeallocationStack) && ((char *)base >= (char *)NtCurrentTeb()->DeallocationStack) &&
((char *)base < (char *)NtCurrentTeb()->Tib.StackBase)) ((char *)base < (char *)NtCurrentTeb()->Tib.StackBase))
{ {
memset( view->prot + (((char *)base - (char *)view->base) >> page_shift), memset( p, vprot, size >> page_shift );
vprot, size >> page_shift );
mprotect( base, size, unix_prot ); mprotect( base, size, unix_prot );
VIRTUAL_DEBUG_DUMP_VIEW( view ); VIRTUAL_DEBUG_DUMP_VIEW( view );
return TRUE; return TRUE;
...@@ -594,14 +619,41 @@ static BOOL VIRTUAL_SetProt( FILE_VIEW *view, /* [in] Pointer to view */ ...@@ -594,14 +619,41 @@ static BOOL VIRTUAL_SetProt( FILE_VIEW *view, /* [in] Pointer to view */
if (mprotect( base, size, unix_prot )) return FALSE; /* FIXME: last error */ if (mprotect( base, size, unix_prot )) return FALSE; /* FIXME: last error */
done: done:
memset( view->prot + (((char *)base - (char *)view->base) >> page_shift), memset( p, vprot, size >> page_shift );
vprot, size >> page_shift );
VIRTUAL_DEBUG_DUMP_VIEW( view ); VIRTUAL_DEBUG_DUMP_VIEW( view );
return TRUE; 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 * unmap_extra_space
* *
* Release the extra memory while keeping the range starting on the granularity boundary. * Release the extra memory while keeping the range starting on the granularity boundary.
...@@ -1411,12 +1463,18 @@ NTSTATUS virtual_handle_fault( LPCVOID addr, DWORD err ) ...@@ -1411,12 +1463,18 @@ NTSTATUS virtual_handle_fault( LPCVOID addr, DWORD err )
if ((view = VIRTUAL_FindView( addr, 0 ))) if ((view = VIRTUAL_FindView( addr, 0 )))
{ {
void *page = ROUND_ADDR( addr, page_mask ); void *page = ROUND_ADDR( addr, page_mask );
BYTE vprot = view->prot[((const char *)page - (const char *)view->base) >> page_shift]; BYTE *vprot = &view->prot[((const char *)page - (const char *)view->base) >> page_shift];
if (vprot & VPROT_GUARD) 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; 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 ); server_leave_uninterrupted_section( &csVirtual, &sigset );
return ret; return ret;
...@@ -1614,11 +1672,6 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG zero_ ...@@ -1614,11 +1672,6 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG zero_
WARN("called with wrong alloc type flags (%08x) !\n", type); WARN("called with wrong alloc type flags (%08x) !\n", type);
return STATUS_INVALID_PARAMETER; return STATUS_INVALID_PARAMETER;
} }
if (type & MEM_WRITE_WATCH)
{
FIXME("MEM_WRITE_WATCH type not supported\n");
return STATUS_INVALID_PARAMETER;
}
/* Reserve the memory */ /* Reserve the memory */
...@@ -1626,6 +1679,7 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG zero_ ...@@ -1626,6 +1679,7 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG zero_
if ((type & MEM_RESERVE) || !base) 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 ); status = map_view( &view, base, size, mask, type & MEM_TOP_DOWN, vprot );
if (status == STATUS_SUCCESS) base = view->base; if (status == STATUS_SUCCESS) base = view->base;
} }
...@@ -1974,7 +2028,7 @@ NTSTATUS WINAPI NtQueryVirtualMemory( HANDLE process, LPCVOID addr, ...@@ -1974,7 +2028,7 @@ NTSTATUS WINAPI NtQueryVirtualMemory( HANDLE process, LPCVOID addr,
else if (view->protect & VPROT_VALLOC) info->Type = MEM_PRIVATE; else if (view->protect & VPROT_VALLOC) info->Type = MEM_PRIVATE;
else info->Type = MEM_MAPPED; else info->Type = MEM_MAPPED;
for (size = base - alloc_base; size < base + range_size - alloc_base; size += page_size) 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); info->RegionSize = size - (base - alloc_base);
} }
server_leave_uninterrupted_section( &csVirtual, &sigset ); server_leave_uninterrupted_section( &csVirtual, &sigset );
...@@ -2398,8 +2452,44 @@ NTSTATUS WINAPI NtFlushVirtualMemory( HANDLE process, LPCVOID *addr_ptr, ...@@ -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, NTSTATUS WINAPI NtGetWriteWatch( HANDLE process, ULONG flags, PVOID base, SIZE_T size, PVOID *addresses,
ULONG_PTR *count, ULONG *granularity ) ULONG_PTR *count, ULONG *granularity )
{ {
FIXME( "%p %x %p-%p %p %lu\n", process, flags, base, (char *)base + size, addresses, *count ); struct file_view *view;
return STATUS_NOT_IMPLEMENTED; 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 ...@@ -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 ) NTSTATUS WINAPI NtResetWriteWatch( HANDLE process, PVOID base, SIZE_T size )
{ {
FIXME( "%p %p-%p\n", process, base, (char *)base + size ); struct file_view *view;
return STATUS_NOT_IMPLEMENTED; 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 ...@@ -1688,6 +1688,7 @@ struct create_mapping_reply
#define VPROT_GUARD 0x10 #define VPROT_GUARD 0x10
#define VPROT_NOCACHE 0x20 #define VPROT_NOCACHE 0x20
#define VPROT_COMMITTED 0x40 #define VPROT_COMMITTED 0x40
#define VPROT_WRITEWATCH 0x80
#define VPROT_IMAGE 0x0100 #define VPROT_IMAGE 0x0100
#define VPROT_SYSTEM 0x0200 #define VPROT_SYSTEM 0x0200
......
...@@ -1330,6 +1330,7 @@ enum char_info_mode ...@@ -1330,6 +1330,7 @@ enum char_info_mode
#define VPROT_GUARD 0x10 #define VPROT_GUARD 0x10
#define VPROT_NOCACHE 0x20 #define VPROT_NOCACHE 0x20
#define VPROT_COMMITTED 0x40 #define VPROT_COMMITTED 0x40
#define VPROT_WRITEWATCH 0x80
/* per-mapping protection flags */ /* per-mapping protection flags */
#define VPROT_IMAGE 0x0100 /* mapping for an exe image */ #define VPROT_IMAGE 0x0100 /* mapping for an exe image */
#define VPROT_SYSTEM 0x0200 /* system view (underlying mmap not under our control) */ #define VPROT_SYSTEM 0x0200 /* system view (underlying mmap not under our control) */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment