Added patch to fix issues with execute permissions on pages with guard / write watch flags.

This commit is contained in:
Sebastian Lackner 2014-10-08 21:41:19 +02:00
parent aada22c826
commit 9fb8420891
8 changed files with 637 additions and 0 deletions

1
debian/changelog vendored
View File

@ -1,6 +1,7 @@
wine-compholio (1.7.29) UNRELEASED; urgency=low
* Updated DOS Attributes patch to better detect XATTR functions.
* Added patch to support IDF_CHECKFIRST in SetupPromptForDisk.
* Added patch to fix issues when executing pages with guard page / write watch permissions.
* Removed patch to fix issues with drag image in ImageLists (accepted upstream).
* Removed patch to set ldr.EntryPoint for main executable (accepted upstream).
* Partially removed patches for WRITECOPY memory protection (accepted upstream).

View File

@ -38,6 +38,7 @@ PATCHLIST := \
kernel32-Named_Pipe.ok \
kernel32-SystemFileCacheSize.ok \
libs-Unicode_Collation.ok \
ntdll-ATL_Thunk.ok \
ntdll-DOS_Attributes.ok \
ntdll-Dynamic_DST.ok \
ntdll-Exception.ok \
@ -516,6 +517,25 @@ libs-Unicode_Collation.ok:
echo '+ { "libs-Unicode_Collation", "Dmitry Timoshkov", "Fix comparison of punctuation characters." },'; \
) > libs-Unicode_Collation.ok
# Patchset ntdll-ATL_Thunk
# |
# | Included patches:
# | * Fix several issues with execute permissions in guard page / write watch handling. [by Sebastian Lackner]
# |
# | Modified files:
# | * dlls/kernel32/tests/virtual.c, dlls/ntdll/signal_i386.c, dlls/ntdll/virtual.c, include/winternl.h
# |
.INTERMEDIATE: ntdll-ATL_Thunk.ok
ntdll-ATL_Thunk.ok:
$(call APPLY_FILE,ntdll-ATL_Thunk/0001-kernel32-tests-Add-tests-for-DEP-combined-with-guard.patch)
$(call APPLY_FILE,ntdll-ATL_Thunk/0002-ntdll-Avoid-recursive-exception-handler-calls-when-h.patch)
$(call APPLY_FILE,ntdll-ATL_Thunk/0003-ntdll-Ensure-force_exec_prot-is-also-used-for-views-.patch)
$(call APPLY_FILE,ntdll-ATL_Thunk/0004-ntdll-reset_write_watches-shouldn-t-remove-enforced-.patch)
$(call APPLY_FILE,ntdll-ATL_Thunk/0005-ntdll-Only-check-for-ATL-thunk-if-DEP-is-not-enabled.patch)
@( \
echo '+ { "ntdll-ATL_Thunk", "Sebastian Lackner", "Fix several issues with execute permissions in guard page / write watch handling." },'; \
) > ntdll-ATL_Thunk.ok
# Patchset ntdll-DOS_Attributes
# |
# | Included patches:

View File

@ -0,0 +1,336 @@
From c672ac883d5b2c6f00fb6cff0abd70887dbbcef1 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Wed, 8 Oct 2014 20:54:50 +0200
Subject: kernel32/tests: Add tests for DEP combined with guard pages and write
watches.
---
dlls/kernel32/tests/virtual.c | 293 ++++++++++++++++++++++++++++++++++++++++++
include/winternl.h | 1 +
2 files changed, 294 insertions(+)
diff --git a/dlls/kernel32/tests/virtual.c b/dlls/kernel32/tests/virtual.c
index 0fa2b99..ecb5d2f 100644
--- a/dlls/kernel32/tests/virtual.c
+++ b/dlls/kernel32/tests/virtual.c
@@ -1797,6 +1797,295 @@ static void test_guard_page(void)
VirtualFree( base, 0, MEM_FREE );
}
+DWORD num_execute_fault_calls;
+
+static DWORD execute_fault_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_ACCESS_VIOLATION || rec->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION,
+ "ExceptionCode is %08x instead of STATUS_ACCESS_VIOLATION or STATUS_GUARD_PAGE_VIOLATION\n", rec->ExceptionCode );
+
+ if (rec->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION)
+ num_guard_page_calls++;
+ else if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION)
+ {
+ DWORD err, old_prot;
+ BOOL success;
+ ULONG flags;
+
+ NtQueryInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &flags, sizeof(flags), NULL );
+ err = (flags & MEM_EXECUTE_OPTION_DISABLE) ? EXCEPTION_EXECUTE_FAULT : EXCEPTION_READ_FAULT;
+ ok( rec->ExceptionInformation[0] == err, "ExceptionInformation[0] is %d instead of %d\n",
+ (DWORD)rec->ExceptionInformation[0], err );
+
+ success = VirtualProtect( (void *)rec->ExceptionInformation[1], 16, PAGE_EXECUTE_READWRITE, &old_prot );
+ ok( success, "VirtualProtect failed %u\n", GetLastError() );
+ ok( old_prot == PAGE_READWRITE, "wrong old prot %x\n", old_prot );
+
+ num_execute_fault_calls++;
+ }
+
+ return ExceptionContinueExecution;
+}
+
+static DWORD atl_test_func( DWORD arg )
+{
+ ok( arg == 0x11223344, "arg is 0x%08x instead of 0x11223344\n", arg );
+ return 43;
+}
+
+static void test_data_execution_prevention( ULONG dep_flags )
+{
+ static const char code_ret[] = {0xB8, 0x2A, 0x00, 0x00, 0x00, 0xC3};
+ static const char code_atl[] = {0xC7, 0x44, 0x24, 0x04, 0x44, 0x33, 0x22, 0x11, 0xE9, 0x00, 0x00, 0x00, 0x00};
+ EXCEPTION_REGISTRATION_RECORD frame;
+ DWORD (*code)(DWORD);
+ DWORD ret, size, old_prot;
+ ULONG old_flags = MEM_EXECUTE_OPTION_ENABLE;
+ void *results[64];
+ ULONG_PTR count;
+ ULONG pagesize;
+ BOOL success;
+ char *base;
+
+ if (!pNtCurrentTeb)
+ {
+ win_skip( "NtCurrentTeb not supported\n" );
+ return;
+ }
+
+ trace( "Running DEP tests with ProcessExecuteFlags = %d\n", dep_flags );
+
+ /* Adjust ProcessExecuteFlags if necessary */
+ NtQueryInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &old_flags, sizeof(old_flags), NULL );
+ if (old_flags != dep_flags)
+ {
+ ret = NtSetInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &dep_flags, sizeof(dep_flags) );
+ if (ret == STATUS_INVALID_INFO_CLASS) /* Windows 2000 */
+ {
+ win_skip( "Skipping DEP tests with ProcessExecuteFlags = %d\n", dep_flags );
+ return;
+ }
+ ok( !ret, "NtSetInformationProcess failed with status %08x\n", ret );
+ }
+
+ size = 0x1000;
+ base = VirtualAlloc( 0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE );
+ ok( base != NULL, "VirtualAlloc failed %u\n", GetLastError() );
+
+ /* write some instructions into the memory, and try to execute it */
+ memcpy( base, code_ret, sizeof(code_ret) );
+ code = (void *)base;
+
+ frame.Handler = execute_fault_handler;
+ frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
+ pNtCurrentTeb()->Tib.ExceptionList = &frame;
+
+ num_guard_page_calls = num_execute_fault_calls = 0;
+ ret = code( 0xdeadbeef );
+ ok( ret == 42, "call returned wrong result, expected 42, got %d\n", ret );
+ ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls );
+ if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
+ ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
+ else
+ ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
+
+ pNtCurrentTeb()->Tib.ExceptionList = frame.Prev;
+
+ success = VirtualProtect( base, size, PAGE_READWRITE | PAGE_GUARD, &old_prot );
+ ok( success, "VirtualProtect failed %u\n", GetLastError() );
+ if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
+ ok( old_prot == PAGE_EXECUTE_READWRITE, "wrong old prot %x\n", old_prot );
+ else
+ ok( old_prot == PAGE_READWRITE, "wrong old prot %x\n", old_prot );
+
+ /* the same, but with PAGE_GUARD set */
+ frame.Handler = execute_fault_handler;
+ frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
+ pNtCurrentTeb()->Tib.ExceptionList = &frame;
+
+ num_guard_page_calls = num_execute_fault_calls = 0;
+ ret = code( 0xdeadbeef );
+ ok( ret == 42, "call returned wrong result, expected 42, got %d\n", ret );
+ ok( num_guard_page_calls == 1, "expected one STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls );
+ if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
+ ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
+ else
+ todo_wine
+ ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
+
+ pNtCurrentTeb()->Tib.ExceptionList = frame.Prev;
+
+ /* special case, test with ATL thunk */
+ memcpy( base, code_atl, sizeof(code_atl) );
+ *(DWORD *)(base + 9) = (DWORD_PTR)atl_test_func - (DWORD_PTR)(base + 13);
+
+ success = VirtualProtect( base, size, PAGE_READWRITE | PAGE_GUARD, &old_prot );
+ ok( success, "VirtualProtect failed %u\n", GetLastError() );
+ if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
+ ok( old_prot == PAGE_EXECUTE_READWRITE, "wrong old prot %x\n", old_prot );
+ else
+ todo_wine
+ ok( old_prot == PAGE_READWRITE, "wrong old prot %x\n", old_prot );
+
+ frame.Handler = execute_fault_handler;
+ frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
+ pNtCurrentTeb()->Tib.ExceptionList = &frame;
+
+ num_guard_page_calls = num_execute_fault_calls = 0;
+ ret = code( 0xdeadbeef );
+ ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret );
+ ok( num_guard_page_calls == 1, "expected one STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls );
+ if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
+ todo_wine
+ ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
+ else
+ ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
+
+ num_guard_page_calls = num_execute_fault_calls = 0;
+ ret = code( 0xdeadbeef );
+ ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret );
+ ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls );
+ ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
+
+ pNtCurrentTeb()->Tib.ExceptionList = frame.Prev;
+
+ VirtualFree( base, 0, MEM_FREE );
+
+ /* same as above, but using memory with a write watch */
+ base = VirtualAlloc( 0, size, MEM_RESERVE | MEM_COMMIT | MEM_WRITE_WATCH, PAGE_READWRITE );
+ if (!base && (GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == ERROR_NOT_SUPPORTED))
+ {
+ win_skip( "MEM_WRITE_WATCH not supported\n" );
+ goto out;
+ }
+ ok( base != NULL, "VirtualAlloc failed %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 );
+
+ /* write some instructions into the memory, and try to execute it */
+ memcpy( base, code_ret, sizeof(code_ret) );
+ code = (void *)base;
+
+ 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] );
+
+ frame.Handler = execute_fault_handler;
+ frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
+ pNtCurrentTeb()->Tib.ExceptionList = &frame;
+
+ num_guard_page_calls = num_execute_fault_calls = 0;
+ ret = code( 0xdeadbeef );
+ ok( ret == 42, "call returned wrong result, expected 42, got %d\n", ret );
+ ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls );
+ if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
+ ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
+ else
+ todo_wine
+ ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_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 == 0, "wrong count %lu\n", count );
+
+ success = VirtualProtect( base, size, PAGE_READWRITE | PAGE_GUARD, &old_prot );
+ ok( success, "VirtualProtect failed %u\n", GetLastError() );
+ if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
+ ok( old_prot == PAGE_EXECUTE_READWRITE, "wrong old prot %x\n", old_prot );
+ else
+ todo_wine
+ ok( old_prot == PAGE_READWRITE, "wrong old prot %x\n", old_prot );
+
+ /* the same, but with PAGE_GUARD set */
+ frame.Handler = execute_fault_handler;
+ frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
+ pNtCurrentTeb()->Tib.ExceptionList = &frame;
+
+ num_guard_page_calls = num_execute_fault_calls = 0;
+ ret = code( 0xdeadbeef );
+ ok( ret == 42, "call returned wrong result, expected 42, got %d\n", ret );
+ ok( num_guard_page_calls == 1, "expected one STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls );
+ if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
+ ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
+ else
+ todo_wine
+ ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_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 == 0 || broken(count == 1) /* < Windows 8 */, "wrong count %lu\n", count );
+
+ /* special case, test with ATL thunk */
+ memcpy( base, code_atl, sizeof(code_atl) );
+ *(DWORD *)(base + 9) = (DWORD_PTR)atl_test_func - (DWORD_PTR)(base + 13);
+
+ 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() );
+ if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
+ ok( old_prot == PAGE_EXECUTE_READWRITE, "wrong old prot %x\n", old_prot );
+ else
+ todo_wine
+ ok( old_prot == PAGE_READWRITE, "wrong old prot %x\n", old_prot );
+
+ frame.Handler = execute_fault_handler;
+ frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
+ pNtCurrentTeb()->Tib.ExceptionList = &frame;
+
+ num_guard_page_calls = num_execute_fault_calls = 0;
+ ret = code( 0xdeadbeef );
+ ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret );
+ ok( num_guard_page_calls == 1, "expected one STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls );
+ if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
+ todo_wine
+ ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
+ else
+ ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
+
+ num_guard_page_calls = num_execute_fault_calls = 0;
+ ret = code( 0xdeadbeef );
+ ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret );
+ ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls );
+ ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
+
+ count = 64;
+ ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize );
+ ok( !ret, "GetWriteWatch failed %u\n", GetLastError() );
+ ok( count == 0 || broken(count == 1) /* < Windows 8 */, "wrong count %lu\n", count );
+
+ pNtCurrentTeb()->Tib.ExceptionList = frame.Prev;
+
+ VirtualFree( base, 0, MEM_FREE );
+
+out:
+ if (old_flags != dep_flags)
+ {
+ ret = NtSetInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &old_flags, sizeof(old_flags) );
+ ok( !ret, "NtSetInformationProcess failed with status %08x\n", ret );
+ }
+}
+
#endif /* __i386__ */
static void test_VirtualProtect(void)
@@ -2859,5 +3148,9 @@ START_TEST(virtual)
test_write_watch();
#ifdef __i386__
test_guard_page();
+ test_data_execution_prevention( MEM_EXECUTE_OPTION_ENABLE );
+ test_data_execution_prevention( MEM_EXECUTE_OPTION_ENABLE | MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION );
+ test_data_execution_prevention( MEM_EXECUTE_OPTION_DISABLE );
+ test_data_execution_prevention( MEM_EXECUTE_OPTION_DISABLE | MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION );
#endif
}
diff --git a/include/winternl.h b/include/winternl.h
index 95951e2..7f9a85d 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -728,6 +728,7 @@ typedef enum _PROCESSINFOCLASS {
#define MEM_EXECUTE_OPTION_DISABLE 0x01
#define MEM_EXECUTE_OPTION_ENABLE 0x02
+#define MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION 0x04
#define MEM_EXECUTE_OPTION_PERMANENT 0x08
typedef enum _SECTION_INHERIT {
--
2.1.2

View File

@ -0,0 +1,68 @@
From 5dc9f7a909b8d0e7bee4be1c0b846a21b7e78240 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Wed, 8 Oct 2014 21:11:55 +0200
Subject: ntdll: Avoid recursive exception handler calls when handling guard
pages.
The ATL check leads to problems when a page is protected with guard page protection.
raise_segv_exception is called with EXCEPTION_EXECUTE_FAULT. The ATL check tries to
read the memory, and triggers another exception handler. This time the virtual_handle_fault
check is executed, and removes the guard page protection. Afterwards, when the ATL
check returns, the exception is _not_ catched by virtual_handle_fault, but instead
passed to the application.
---
dlls/kernel32/tests/virtual.c | 2 --
dlls/ntdll/signal_i386.c | 10 ++++++----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/dlls/kernel32/tests/virtual.c b/dlls/kernel32/tests/virtual.c
index ecb5d2f..90a5578 100644
--- a/dlls/kernel32/tests/virtual.c
+++ b/dlls/kernel32/tests/virtual.c
@@ -1915,7 +1915,6 @@ static void test_data_execution_prevention( ULONG dep_flags )
if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
else
- todo_wine
ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
pNtCurrentTeb()->Tib.ExceptionList = frame.Prev;
@@ -1929,7 +1928,6 @@ static void test_data_execution_prevention( ULONG dep_flags )
if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
ok( old_prot == PAGE_EXECUTE_READWRITE, "wrong old prot %x\n", old_prot );
else
- todo_wine
ok( old_prot == PAGE_READWRITE, "wrong old prot %x\n", old_prot );
frame.Handler = execute_fault_handler;
diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index 12aa5a4..b9b45a8 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -1821,17 +1821,19 @@ static void WINAPI raise_segv_exception( EXCEPTION_RECORD *rec, CONTEXT *context
case EXCEPTION_ACCESS_VIOLATION:
if (rec->NumberParameters == 2)
{
- if (rec->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT && check_atl_thunk( rec, context ))
- goto done;
if (rec->ExceptionInformation[1] == 0xffffffff && check_invalid_gs( context ))
goto done;
if (!(rec->ExceptionCode = virtual_handle_fault( (void *)rec->ExceptionInformation[1],
rec->ExceptionInformation[0] )))
goto done;
- /* send EXCEPTION_EXECUTE_FAULT only if data execution prevention is enabled */
- if (rec->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT)
+ if (rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION &&
+ rec->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT)
{
ULONG flags;
+ if (check_atl_thunk( rec, context ))
+ goto done;
+
+ /* send EXCEPTION_EXECUTE_FAULT only if data execution prevention is enabled */
NtQueryInformationProcess( GetCurrentProcess(), ProcessExecuteFlags,
&flags, sizeof(flags), NULL );
if (!(flags & MEM_EXECUTE_OPTION_DISABLE))
--
2.1.2

View File

@ -0,0 +1,100 @@
From 94360ac47e9300f85ab0f63c49ba8044107ac1c1 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Wed, 8 Oct 2014 21:26:28 +0200
Subject: ntdll: Ensure force_exec_prot is also used for views with write watch
permissions.
---
dlls/kernel32/tests/virtual.c | 2 --
dlls/ntdll/virtual.c | 36 +++++++++++++++++++++++-------------
2 files changed, 23 insertions(+), 15 deletions(-)
diff --git a/dlls/kernel32/tests/virtual.c b/dlls/kernel32/tests/virtual.c
index 90a5578..a233b37 100644
--- a/dlls/kernel32/tests/virtual.c
+++ b/dlls/kernel32/tests/virtual.c
@@ -2019,7 +2019,6 @@ static void test_data_execution_prevention( ULONG dep_flags )
if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
else
- todo_wine
ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
pNtCurrentTeb()->Tib.ExceptionList = frame.Prev;
@@ -2044,7 +2043,6 @@ static void test_data_execution_prevention( ULONG dep_flags )
if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
ok( old_prot == PAGE_EXECUTE_READWRITE, "wrong old prot %x\n", old_prot );
else
- todo_wine
ok( old_prot == PAGE_READWRITE, "wrong old prot %x\n", old_prot );
frame.Handler = execute_fault_handler;
diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c
index f8a5dd3..3c9a4b5 100644
--- a/dlls/ntdll/virtual.c
+++ b/dlls/ntdll/virtual.c
@@ -591,6 +591,25 @@ static NTSTATUS get_vprot_flags( DWORD protect, unsigned int *vprot, BOOL image
/***********************************************************************
+ * mprotect_exec
+ *
+ * Wrapper for mprotect, adds PROT_EXEC if forced by force_exec_prot
+ */
+static inline int mprotect_exec( void *base, size_t size, int unix_prot, unsigned int view_protect )
+{
+ if (force_exec_prot && !(view_protect & VPROT_NOEXEC) &&
+ (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 )) return 0;
+ /* exec + write may legitimately fail, in that case fall back to write only */
+ if (!(unix_prot & PROT_WRITE)) return -1;
+ }
+
+ return mprotect( base, size, unix_prot );
+}
+
+/***********************************************************************
* VIRTUAL_SetProt
*
* Change the protection of a range of pages.
@@ -624,12 +643,12 @@ static BOOL VIRTUAL_SetProt( struct file_view *view, /* [in] Pointer to view */
p[i] = vprot | (p[i] & VPROT_WRITEWATCH);
prot = VIRTUAL_GetUnixProt( p[i] );
if (prot == unix_prot) continue;
- mprotect( addr, count << page_shift, unix_prot );
+ mprotect_exec( addr, count << page_shift, unix_prot, view->protect );
addr += count << page_shift;
unix_prot = prot;
count = 0;
}
- if (count) mprotect( addr, count << page_shift, unix_prot );
+ if (count) mprotect_exec( addr, count << page_shift, unix_prot, view->protect );
VIRTUAL_DEBUG_DUMP_VIEW( view );
return TRUE;
}
@@ -646,18 +665,9 @@ static BOOL VIRTUAL_SetProt( struct file_view *view, /* [in] Pointer to view */
return TRUE;
}
- if (force_exec_prot && !(view->protect & VPROT_NOEXEC) &&
- (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 */
+ if (mprotect_exec( base, size, unix_prot, view->protect )) /* FIXME: last error */
+ return FALSE;
-done:
memset( p, vprot, size >> page_shift );
VIRTUAL_DEBUG_DUMP_VIEW( view );
return TRUE;
--
2.1.2

View File

@ -0,0 +1,53 @@
From db098e8367cb2f01a7f5a4d6309e0ea476821e60 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Wed, 8 Oct 2014 21:28:25 +0200
Subject: ntdll: reset_write_watches shouldn't remove enforced exec
permissions.
---
dlls/kernel32/tests/virtual.c | 2 --
dlls/ntdll/virtual.c | 4 ++--
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/dlls/kernel32/tests/virtual.c b/dlls/kernel32/tests/virtual.c
index a233b37..f236edb 100644
--- a/dlls/kernel32/tests/virtual.c
+++ b/dlls/kernel32/tests/virtual.c
@@ -1989,7 +1989,6 @@ static void test_data_execution_prevention( ULONG dep_flags )
if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
else
- todo_wine
ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
pNtCurrentTeb()->Tib.ExceptionList = frame.Prev;
@@ -2004,7 +2003,6 @@ static void test_data_execution_prevention( ULONG dep_flags )
if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
ok( old_prot == PAGE_EXECUTE_READWRITE, "wrong old prot %x\n", old_prot );
else
- todo_wine
ok( old_prot == PAGE_READWRITE, "wrong old prot %x\n", old_prot );
/* the same, but with PAGE_GUARD set */
diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c
index 3c9a4b5..d2bb152 100644
--- a/dlls/ntdll/virtual.c
+++ b/dlls/ntdll/virtual.c
@@ -693,12 +693,12 @@ static void reset_write_watches( struct file_view *view, void *base, SIZE_T size
p[i] |= VPROT_WRITEWATCH;
prot = VIRTUAL_GetUnixProt( p[i] );
if (prot == unix_prot) continue;
- mprotect( addr, count << page_shift, unix_prot );
+ mprotect_exec( addr, count << page_shift, unix_prot, view->protect );
addr += count << page_shift;
unix_prot = prot;
count = 0;
}
- if (count) mprotect( addr, count << page_shift, unix_prot );
+ if (count) mprotect_exec( addr, count << page_shift, unix_prot, view->protect );
}
--
2.1.2

View File

@ -0,0 +1,56 @@
From 7df7b751e99689d72cf41095df1d697ccc41a1ab Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Wed, 8 Oct 2014 21:32:46 +0200
Subject: ntdll: Only check for ATL thunk if DEP is not enabled.
---
dlls/kernel32/tests/virtual.c | 2 --
dlls/ntdll/signal_i386.c | 8 ++++----
2 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/dlls/kernel32/tests/virtual.c b/dlls/kernel32/tests/virtual.c
index f236edb..1bed9bc 100644
--- a/dlls/kernel32/tests/virtual.c
+++ b/dlls/kernel32/tests/virtual.c
@@ -1939,7 +1939,6 @@ static void test_data_execution_prevention( ULONG dep_flags )
ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret );
ok( num_guard_page_calls == 1, "expected one STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls );
if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
- todo_wine
ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
else
ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
@@ -2052,7 +2051,6 @@ static void test_data_execution_prevention( ULONG dep_flags )
ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret );
ok( num_guard_page_calls == 1, "expected one STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls );
if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
- todo_wine
ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
else
ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index b9b45a8..f854dec 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -1830,14 +1830,14 @@ static void WINAPI raise_segv_exception( EXCEPTION_RECORD *rec, CONTEXT *context
rec->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT)
{
ULONG flags;
- if (check_atl_thunk( rec, context ))
- goto done;
-
- /* send EXCEPTION_EXECUTE_FAULT only if data execution prevention is enabled */
NtQueryInformationProcess( GetCurrentProcess(), ProcessExecuteFlags,
&flags, sizeof(flags), NULL );
if (!(flags & MEM_EXECUTE_OPTION_DISABLE))
+ {
+ if (check_atl_thunk( rec, context ))
+ goto done;
rec->ExceptionInformation[0] = EXCEPTION_READ_FAULT;
+ }
}
}
break;
--
2.1.2

View File

@ -0,0 +1,3 @@
Author: Sebastian Lackner
Subject: Fix several issues with execute permissions in guard page / write watch handling.
Revision: 1