From 822be6c90fd8312e4a0689d964e7c9fb895503b1 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 5 Dec 2006 15:42:29 +0100 Subject: [PATCH] ntdll: Force exec permissions on all mmaps unless the app is marked NX-compatible. --- dlls/ntdll/loader.c | 2 + dlls/ntdll/ntdll_misc.h | 1 + dlls/ntdll/virtual.c | 113 +++++++++++++++++++++++++++++++++------- 3 files changed, 96 insertions(+), 20 deletions(-) diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 99c90c4e8c9..4d15779847d 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -2126,6 +2126,8 @@ void WINAPI LdrInitializeThunk( ULONG unknown1, ULONG unknown2, ULONG unknown3, peb->ProcessParameters->ImagePathName = wm->ldr.FullDllName; version_init( wm->ldr.FullDllName.Buffer ); + if (!(nt->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NX_COMPAT)) + VIRTUAL_SetForceExec( TRUE ); /* the main exe needs to be the first in the load order list */ RemoveEntryList( &wm->ldr.InLoadOrderModuleList ); diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index 781afa58021..a7ef8df7e80 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -112,6 +112,7 @@ extern NTSTATUS DIR_get_unix_cwd( char **cwd ); /* virtual memory */ extern NTSTATUS VIRTUAL_HandleFault(LPCVOID addr); extern BOOL VIRTUAL_HasMapping( LPCVOID addr ); +extern void VIRTUAL_SetForceExec( BOOL enable ); extern void VIRTUAL_UseLargeAddressSpace(void); extern BOOL is_current_process( HANDLE handle ); diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c index 573072e2391..54193ffbd43 100644 --- a/dlls/ntdll/virtual.c +++ b/dlls/ntdll/virtual.c @@ -143,6 +143,7 @@ static void *user_space_limit = USER_SPACE_LIMIT; static void *preload_reserve_start; static void *preload_reserve_end; static int use_locks; +static int force_exec_prot; /* whether to force PROT_EXEC on all PROT_READ mmaps */ /*********************************************************************** @@ -161,6 +162,26 @@ static const char *VIRTUAL_GetProtStr( BYTE prot ) } +/*********************************************************************** + * VIRTUAL_GetUnixProt + * + * Convert page protections to protection for mmap/mprotect. + */ +static int VIRTUAL_GetUnixProt( BYTE vprot ) +{ + int prot = 0; + if ((vprot & VPROT_COMMITTED) && !(vprot & VPROT_GUARD)) + { + if (vprot & VPROT_READ) prot |= PROT_READ; + if (vprot & VPROT_WRITE) prot |= PROT_WRITE; + if (vprot & VPROT_WRITECOPY) prot |= PROT_WRITE; + if (vprot & VPROT_EXEC) prot |= PROT_EXEC; + } + if (!prot) prot = PROT_NONE; + return prot; +} + + /*********************************************************************** * VIRTUAL_DumpView */ @@ -391,6 +412,7 @@ static NTSTATUS create_view( struct file_view **view_ret, void *base, size_t siz { struct file_view *view; struct list *ptr; + int unix_prot = VIRTUAL_GetUnixProt( vprot ); assert( !((UINT_PTR)base & page_mask) ); assert( !(size & page_mask) ); @@ -446,27 +468,13 @@ static NTSTATUS create_view( struct file_view **view_ret, void *base, size_t siz *view_ret = view; VIRTUAL_DEBUG_DUMP_VIEW( view ); - return STATUS_SUCCESS; -} - -/*********************************************************************** - * VIRTUAL_GetUnixProt - * - * Convert page protections to protection for mmap/mprotect. - */ -static int VIRTUAL_GetUnixProt( BYTE vprot ) -{ - int prot = 0; - if ((vprot & VPROT_COMMITTED) && !(vprot & VPROT_GUARD)) + if (force_exec_prot && (unix_prot & PROT_READ) && !(unix_prot & PROT_EXEC)) { - if (vprot & VPROT_READ) prot |= PROT_READ; - if (vprot & VPROT_WRITE) prot |= PROT_WRITE; - if (vprot & VPROT_WRITECOPY) prot |= PROT_WRITE; - if (vprot & VPROT_EXEC) prot |= PROT_EXEC; + TRACE( "forcing exec permission on %p-%p\n", base, (char *)base + size - 1 ); + mprotect( base, size, unix_prot | PROT_EXEC ); } - if (!prot) prot = PROT_NONE; - return prot; + return STATUS_SUCCESS; } @@ -555,12 +563,22 @@ static BOOL VIRTUAL_SetProt( FILE_VIEW *view, /* [in] Pointer to view */ size_t size, /* [in] Size in bytes */ BYTE vprot ) /* [in] Protections to use */ { + int unix_prot = VIRTUAL_GetUnixProt(vprot); + TRACE("%p-%p %s\n", base, (char *)base + size - 1, VIRTUAL_GetProtStr( vprot ) ); - if (mprotect( base, size, VIRTUAL_GetUnixProt(vprot) )) - return FALSE; /* FIXME: last error */ + if (force_exec_prot && (unix_prot & PROT_READ) && !(unix_prot & PROT_EXEC)) + { + TRACE( "forcing exec permission on %p-%p\n", base, (char *)base + size - 1 ); + if (!mprotect( base, size, unix_prot | PROT_EXEC )) goto done; + /* exec + write may legitimately fail, in that case fall back to write only */ + if (!(unix_prot & PROT_WRITE)) return FALSE; + } + 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 ); VIRTUAL_DEBUG_DUMP_VIEW( view ); @@ -1284,6 +1302,61 @@ BOOL VIRTUAL_HasMapping( LPCVOID addr ) } +/*********************************************************************** + * VIRTUAL_SetForceExec + * + * Whether to force exec prot on all views. + */ +void VIRTUAL_SetForceExec( BOOL enable ) +{ + struct file_view *view; + + RtlEnterCriticalSection( &csVirtual ); + if (!force_exec_prot != !enable) /* change all existing views */ + { + force_exec_prot = enable; + + LIST_FOR_EACH_ENTRY( view, &views_list, struct file_view, entry ) + { + UINT i, count; + int unix_prot; + char *addr = view->base; + BYTE prot = view->prot[0]; + + for (count = i = 1; i < view->size >> page_shift; i++, count++) + { + if (view->prot[i] == prot) continue; + unix_prot = VIRTUAL_GetUnixProt( prot ); + if ((unix_prot & PROT_READ) && !(unix_prot & PROT_EXEC)) + { + TRACE( "%s exec prot for %p-%p\n", + force_exec_prot ? "enabling" : "disabling", + addr, addr + (count << page_shift) - 1 ); + mprotect( addr, count << page_shift, + unix_prot | (force_exec_prot ? PROT_EXEC : 0) ); + } + addr += (count << page_shift); + prot = view->prot[i]; + count = 0; + } + if (count) + { + unix_prot = VIRTUAL_GetUnixProt( prot ); + if ((unix_prot & PROT_READ) && !(unix_prot & PROT_EXEC)) + { + TRACE( "%s exec prot for %p-%p\n", + force_exec_prot ? "enabling" : "disabling", + addr, addr + (count << page_shift) - 1 ); + mprotect( addr, count << page_shift, + unix_prot | (force_exec_prot ? PROT_EXEC : 0) ); + } + } + } + } + RtlLeaveCriticalSection( &csVirtual ); +} + + /*********************************************************************** * VIRTUAL_UseLargeAddressSpace *