Alexandre Julliard : ntdll: Add helper functions for verifying write access to a memory range.
Alexandre Julliard
julliard at winehq.org
Fri Sep 22 15:49:06 CDT 2017
Module: wine
Branch: master
Commit: af045a52e294be2e2515491292e88483658a59f3
URL: http://source.winehq.org/git/wine.git/?a=commit;h=af045a52e294be2e2515491292e88483658a59f3
Author: Alexandre Julliard <julliard at winehq.org>
Date: Fri Sep 22 14:56:56 2017 +0200
ntdll: Add helper functions for verifying write access to a memory range.
Signed-off-by: Alexandre Julliard <julliard at winehq.org>
---
dlls/ntdll/virtual.c | 62 ++++++++++++++++++++++++++++++++++++----------------
1 file changed, 43 insertions(+), 19 deletions(-)
diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c
index 1e6b8a7..d36dd7d 100644
--- a/dlls/ntdll/virtual.c
+++ b/dlls/ntdll/virtual.c
@@ -838,9 +838,10 @@ static inline int mprotect_exec( void *base, size_t size, int unix_prot )
static void mprotect_range( void *base, size_t size, BYTE set, BYTE clear )
{
size_t i, count;
- char *addr = base;
+ char *addr = ROUND_ADDR( base, page_mask );
int prot, next;
+ size = ROUND_SIZE( base, size );
prot = VIRTUAL_GetUnixProt( (get_page_vprot( addr ) & ~clear ) | set );
for (count = i = 1; i < size >> page_shift; i++, count++)
{
@@ -925,6 +926,19 @@ static NTSTATUS set_protection( struct file_view *view, void *base, SIZE_T size,
/***********************************************************************
+ * update_write_watches
+ */
+static void update_write_watches( void *base, size_t size, size_t accessed_size )
+{
+ TRACE( "updating watch %p-%p-%p\n", base, (char *)base + accessed_size, (char *)base + size );
+ /* clear write watch flag on accessed pages */
+ set_page_vprot_bits( base, accessed_size, 0, VPROT_WRITEWATCH );
+ /* restore page protections on the entire range */
+ mprotect_range( base, size, 0, 0 );
+}
+
+
+/***********************************************************************
* reset_write_watches
*
* Reset write watches in a memory range.
@@ -1800,6 +1814,30 @@ NTSTATUS virtual_handle_fault( LPCVOID addr, DWORD err, BOOL on_signal_stack )
}
+/***********************************************************************
+ * check_write_access
+ *
+ * Check if the memory range is writable, temporarily disabling write watches if necessary.
+ */
+static NTSTATUS check_write_access( void *base, size_t size, BOOL *has_write_watch )
+{
+ size_t i;
+ char *addr = ROUND_ADDR( base, page_mask );
+
+ size = ROUND_SIZE( base, size );
+ for (i = 0; i < size; i += page_size)
+ {
+ BYTE vprot = get_page_vprot( addr + i );
+ if (vprot & VPROT_WRITEWATCH) *has_write_watch = TRUE;
+ if (!(VIRTUAL_GetUnixProt( vprot & ~VPROT_WRITEWATCH ) & PROT_WRITE))
+ return STATUS_INVALID_USER_BUFFER;
+ }
+ if (*has_write_watch)
+ mprotect_range( addr, size, 0, VPROT_WRITEWATCH ); /* temporarily enable write access */
+ return STATUS_SUCCESS;
+}
+
+
/***********************************************************************
* virtual_is_valid_code_address
@@ -1963,32 +2001,18 @@ SIZE_T virtual_uninterrupted_read_memory( const void *addr, void *buffer, SIZE_T
*/
NTSTATUS virtual_uninterrupted_write_memory( void *addr, const void *buffer, SIZE_T size )
{
- struct file_view *view;
+ BOOL has_write_watch = FALSE;
sigset_t sigset;
- NTSTATUS ret = STATUS_ACCESS_VIOLATION;
+ NTSTATUS ret;
if (!size) return STATUS_SUCCESS;
server_enter_uninterrupted_section( &csVirtual, &sigset );
- if ((view = VIRTUAL_FindView( addr, size )) && !(view->protect & VPROT_SYSTEM))
+ if (!(ret = check_write_access( addr, size, &has_write_watch )))
{
- char *page = ROUND_ADDR( addr, page_mask );
- size_t i, total = ROUND_SIZE( addr, size );
-
- for (i = 0; i < total; i += page_size)
- {
- int prot = VIRTUAL_GetUnixProt( get_page_vprot( page + i ) & ~VPROT_WRITEWATCH );
- if (!(prot & PROT_WRITE)) goto done;
- }
- if (view->protect & VPROT_WRITEWATCH) /* enable write access by clearing write watches */
- {
- set_page_vprot_bits( addr, size, 0, VPROT_WRITEWATCH );
- mprotect_range( addr, size, 0, 0 );
- }
memcpy( addr, buffer, size );
- ret = STATUS_SUCCESS;
+ if (has_write_watch) update_write_watches( addr, size, size );
}
-done:
server_leave_uninterrupted_section( &csVirtual, &sigset );
return ret;
}
More information about the wine-cvs
mailing list