My intention is to make InitializeCriticalSection[Ex] behaviour compatible with modern Windows which doesn't allocate debug info without RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO flag. Before this can be done it requires fixing all the Wine modules currently relying on the old behaviour. That will also make some of those Wine modules (e. g., winhttp) work on modern Windows instead of crashing on debug info access. Besides the latter, the motivation under the planned change is spare heap alloc / free during InitializeCriticalSection / DeleteCriticalSection by default as some games do quite a lot of those. Also avoiding it helps Witch on the Holy Night to survive use after free like it does on Windows.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernel32/tests/sync.c | 32 +++++++++++++++++++++++++++++++- include/winnt.h | 1 + 2 files changed, 32 insertions(+), 1 deletion(-)
diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c index 56a9d6e4859..25cf75cd686 100644 --- a/dlls/kernel32/tests/sync.c +++ b/dlls/kernel32/tests/sync.c @@ -2741,7 +2741,8 @@ static void test_crit_section(void) to override that. */ memset(&cs, 0, sizeof(cs)); InitializeCriticalSection(&cs); - ok(cs.DebugInfo != NULL, "Unexpected debug info pointer %p.\n", cs.DebugInfo); + todo_wine ok(cs.DebugInfo == (void *)(ULONG_PTR)-1 || broken(!!cs.DebugInfo) /* before Win8 */, + "Unexpected debug info pointer %p.\n", cs.DebugInfo); DeleteCriticalSection(&cs); ok(cs.DebugInfo == NULL, "Unexpected debug info pointer %p.\n", cs.DebugInfo);
@@ -2751,10 +2752,39 @@ static void test_crit_section(void) return; }
+ memset(&cs, 0, sizeof(cs)); + ret = pInitializeCriticalSectionEx(&cs, 0, 0); + ok(ret, "Failed to initialize critical section.\n"); + todo_wine ok(cs.DebugInfo == (void *)(ULONG_PTR)-1 || broken(!!cs.DebugInfo) /* before Win8 */, + "Unexpected debug info pointer %p.\n", cs.DebugInfo); + DeleteCriticalSection(&cs); + ok(cs.DebugInfo == NULL, "Unexpected debug info pointer %p.\n", cs.DebugInfo); + memset(&cs, 0, sizeof(cs)); ret = pInitializeCriticalSectionEx(&cs, 0, CRITICAL_SECTION_NO_DEBUG_INFO); ok(ret, "Failed to initialize critical section.\n"); ok(cs.DebugInfo == (void *)(ULONG_PTR)-1, "Unexpected debug info pointer %p.\n", cs.DebugInfo); + DeleteCriticalSection(&cs); + todo_wine ok(cs.DebugInfo == NULL, "Unexpected debug info pointer %p.\n", cs.DebugInfo); + + memset(&cs, 0, sizeof(cs)); + ret = pInitializeCriticalSectionEx(&cs, 0, 0); + ok(ret, "Failed to initialize critical section.\n"); + todo_wine ok(cs.DebugInfo == (void *)(ULONG_PTR)-1 || broken(!!cs.DebugInfo) /* before Win8 */, + "Unexpected debug info pointer %p.\n", cs.DebugInfo); + DeleteCriticalSection(&cs); + ok(cs.DebugInfo == NULL, "Unexpected debug info pointer %p.\n", cs.DebugInfo); + + memset(&cs, 0, sizeof(cs)); + ret = pInitializeCriticalSectionEx(&cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO); + ok(ret || broken(GetLastError() == ERROR_INVALID_PARAMETER) /* before Win8 */, + "Failed to initialize critical section, error %lu.\n", GetLastError()); + if (!ret) + { + ret = pInitializeCriticalSectionEx(&cs, 0, 0); + ok(ret, "Failed to initialize critical section.\n"); + } + ok(cs.DebugInfo && cs.DebugInfo != (void *)(ULONG_PTR)-1, "Unexpected debug info pointer %p.\n", cs.DebugInfo);
ret = TryEnterCriticalSection(&cs); ok(ret, "Failed to enter critical section.\n"); diff --git a/include/winnt.h b/include/winnt.h index 0e0d1cb5170..3266e89fe3e 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -6115,6 +6115,7 @@ typedef struct _RTL_CRITICAL_SECTION { #define RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO 0x1000000 #define RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN 0x2000000 #define RTL_CRITICAL_SECTION_FLAG_STATIC_INIT 0x4000000 +#define RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO 0x10000000 #define RTL_CRITICAL_SECTION_ALL_FLAG_BITS 0xFF000000 #define RTL_CRITICAL_SECTION_FLAG_RESERVED (RTL_CRITICAL_SECTION_ALL_FLAG_BITS & ~0x7000000)