Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/kernel32/tests/Makefile.in | 2 + dlls/kernel32/tests/actctx.c | 2 +- dlls/kernel32/tests/loader.c | 207 +++++++++++++++++++++++++++ dlls/kernel32/tests/locking_dll.c | 45 ++++++ dlls/kernel32/tests/locking_dll.spec | 1 + 5 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 dlls/kernel32/tests/locking_dll.c create mode 100644 dlls/kernel32/tests/locking_dll.spec
diff --git a/dlls/kernel32/tests/Makefile.in b/dlls/kernel32/tests/Makefile.in index e9516603ce9..be3a8fc8dfa 100644 --- a/dlls/kernel32/tests/Makefile.in +++ b/dlls/kernel32/tests/Makefile.in @@ -21,6 +21,8 @@ SOURCES = \ heap.c \ loader.c \ locale.c \ + locking_dll.c \ + locking_dll.spec \ mailslot.c \ module.c \ path.c \ diff --git a/dlls/kernel32/tests/actctx.c b/dlls/kernel32/tests/actctx.c index 163ea405222..f37796a6aba 100644 --- a/dlls/kernel32/tests/actctx.c +++ b/dlls/kernel32/tests/actctx.c @@ -2584,7 +2584,7 @@ static void delete_manifest_file(const char *filename) DeleteFileA(path); }
-static void extract_resource(const char *name, const char *type, const char *path) +void extract_resource(const char *name, const char *type, const char *path) { DWORD written; HANDLE file; diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c index e97c114c5e9..9f502aaf857 100644 --- a/dlls/kernel32/tests/loader.c +++ b/dlls/kernel32/tests/loader.c @@ -92,6 +92,8 @@ static BOOL (WINAPI *pWow64DisableWow64FsRedirection)(void **); static BOOL (WINAPI *pWow64RevertWow64FsRedirection)(void *); static HMODULE (WINAPI *pLoadPackagedLibrary)(LPCWSTR lpwLibFileName, DWORD Reserved);
+void extract_resource(const char *name, const char *type, const char *path); + static PVOID RVAToAddr(DWORD_PTR rva, HMODULE module) { if (rva == 0) @@ -4043,6 +4045,7 @@ static void test_Wow64Transition(void) static BOOL test_loader_lock_repeat_lock; static unsigned int test_loader_notification_count; static const WCHAR *ldr_notify_track_dll; +static HMODULE lock_dll_handle; static HANDLE lock_ready_event, next_test_event; static BOOL test_loader_lock_abort_test; static volatile LONG test_loader_lock_timeout_count; @@ -4061,9 +4064,13 @@ static void CALLBACK test_loader_lock_module_enum_locking_callback(LDR_DATA_TABL
static DWORD WINAPI test_loader_lock_thread(void *param) { + static const WCHAR env_var[] = L"test_wait_reason_value"; ULONG_PTR magic; NTSTATUS status; + HMODULE hmodule; + WCHAR str[32]; DWORD result; + BOOL bret;
WaitForSingleObject(next_test_event, INFINITE); SetEvent(lock_ready_event); @@ -4109,6 +4116,67 @@ static DWORD WINAPI test_loader_lock_thread(void *param) SetEvent(lock_ready_event); WaitForSingleObject(next_test_event, INFINITE);
+ /* 3. Test with the thread blocked in DLL entry point during process attach. */ + swprintf(str, ARRAY_SIZE(str), L"%p", &test_loader_lock_timeout_count); + bret = SetEnvironmentVariableW(L"test_timeout_count_addr", str); + ok(bret, "SetEnvironmentVariableW failed.\n"); + + swprintf(str, ARRAY_SIZE(str), L"%u", DLL_PROCESS_ATTACH); + bret = SetEnvironmentVariableW(env_var, str); + ok(bret, "SetEnvironmentVariableW failed.\n"); + do + { + hmodule = LoadLibraryA("lock.dll"); + ok(!hmodule, "LoadLibrary succeeded.\n"); + ok(GetLastError() == ERROR_DLL_INIT_FAILED, "Got unexpected error %u.\n", GetLastError()); + if (test_loader_lock_timeout_count) + { + WaitForSingleObject(next_test_event, INFINITE); + test_loader_lock_timeout_count = 0; + } + } while (test_loader_lock_repeat_lock); + + /* A reference was added for the dll during process attach. */ + if (lock_dll_handle) + { + bret = FreeLibrary(lock_dll_handle); + ok(bret, "FreeLibrary failed, err %u.\n", GetLastError()); + bret = FreeLibrary(lock_dll_handle); + ok(!bret, "FreeLibrary succeeded.\n"); + ok(GetLastError() == ERROR_MOD_NOT_FOUND, "Got unexpected error %u.\n", GetLastError()); + } + + SetEvent(lock_ready_event); + WaitForSingleObject(next_test_event, INFINITE); + + /* 4. Test with the thread blocked in DLL entry point during process detach. */ + swprintf(str, ARRAY_SIZE(str), L"%u", DLL_PROCESS_DETACH); + bret = SetEnvironmentVariableW(env_var, str); + ok(bret, "SetEnvironmentVariableW failed.\n"); + do + { + lock_dll_handle = hmodule = LoadLibraryA("lock.dll"); + ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError()); + + bret = FreeLibrary(hmodule); + ok(bret, "FreeLibrary failed, err %u.\n", GetLastError()); + + lock_dll_handle = NULL; + + if (test_loader_lock_timeout_count) + { + WaitForSingleObject(next_test_event, INFINITE); + test_loader_lock_timeout_count = 0; + } + } while (test_loader_lock_repeat_lock); + + bret = FreeLibrary(hmodule); + ok(!bret, "FreeLibrary succeeded.\n"); + ok(GetLastError() == ERROR_MOD_NOT_FOUND, "Got unexpected error %u.\n", GetLastError()); + + SetEvent(lock_ready_event); + WaitForSingleObject(next_test_event, INFINITE); + SetEvent(lock_ready_event); return 0; } @@ -4154,13 +4222,18 @@ static void test_loader_lock(void) { static const WCHAR not_loaded_dll_name[] = L"authz.dll"; static const WCHAR preloaded_dll_name[] = L"winmm.dll"; + BOOL blocks_on_load_in_progress_module = FALSE; + BOOL blocks_on_decref_library = FALSE; HMODULE hmodule_preloaded, hmodule; ULONG_PTR magic; NTSTATUS status; HANDLE thread; void *cookie; + void *proc; BOOL bret;
+ extract_resource("locking_dll.dll", "TESTDLL", "lock.dll"); + lock_ready_event = CreateEventA(NULL, FALSE, FALSE, "test_lock_ready_event"); next_test_event = CreateEventA(NULL, FALSE, FALSE, "test_next_test_event");
@@ -4233,6 +4306,10 @@ static void test_loader_lock(void) ok(!status, "Got unexpected status %#x.\n", status); check_timeout(FALSE);
+ hmodule = GetModuleHandleW(preloaded_dll_name); + ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError()); + check_timeout(FALSE); + if (BLOCKING_TESTS_ENABLED) { status = pLdrLockLoaderLock(0, NULL, &magic); @@ -4246,9 +4323,139 @@ static void test_loader_lock(void) LdrUnregisterDllNotification( cookie ); check_timeout(FALSE);
+ /* 3. Test with the thread blocked in DLL entry point during process attach. + * DLL entry point returns failure. */ + trace("Test 3.\n"); test_loader_lock_next_test();
+ status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify, NULL, &cookie); + ok(!status, "Got unexpected status %#x.\n", status); + check_timeout(FALSE); + + bret = GetModuleHandleExW(0, preloaded_dll_name, &hmodule); + check_timeout(FALSE); + ok(bret, "GetModuleHandleEx failed, err %u.\n", GetLastError()); + ok(hmodule == hmodule_preloaded, "Got unexpected hmodule %p, expected %p.\n", hmodule, hmodule_preloaded); + + status = pLdrUnloadDll(hmodule); + ok(!status, "Got unexpected status %#x.\n", status); + if (test_loader_lock_timeout_count) + { + /* Win10 1507. */ + check_timeout(TRUE); + blocks_on_decref_library = TRUE; + win_skip("LdrUnloadDll() timed out for unlrelated library without unload.\n"); + } + else + { + check_timeout(FALSE); + } + + hmodule = GetModuleHandleA("lock.dll"); + ok(!!hmodule || broken(!hmodule && GetLastError() == ERROR_MOD_NOT_FOUND) /* before Win10 1607 */, + "GetModuleHandleA hmodule %p, err %u.\n", hmodule, GetLastError()); + check_timeout(!hmodule); + + if (hmodule) + { + bret = GetModuleHandleExW(0, L"lock.dll", &hmodule); + check_timeout(FALSE); + ok(bret, "GetModuleHandleEx failed, err %u.\n", GetLastError()); + + lock_dll_handle = hmodule; + bret = FreeLibrary(hmodule); + check_timeout(blocks_on_decref_library); + ok(bret, "FreeLibrary failed, err %u.\n", GetLastError()); + + if (BLOCKING_TESTS_ENABLED) + { + ok(!!lock_dll_handle, "Got NULL lock_dll_handle.\n"); + proc = GetProcAddress(lock_dll_handle, "unknown"); + ok(!proc, "GetProcAddress succeeded.\n"); + check_timeout(TRUE); + } + } + else + { + /* Win8. */ + blocks_on_load_in_progress_module = TRUE; + win_skip("GetModuleHandleA failed for DLL being loaded.\n"); + } + + hmodule = LoadLibraryW(preloaded_dll_name); + check_timeout(FALSE); + ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError()); + + if (!blocks_on_decref_library) + { + bret = FreeLibrary(hmodule); + ok(bret, "FreeLibrary failed, err %u.\n", GetLastError()); + check_timeout(FALSE); + } + + if (!blocks_on_load_in_progress_module) + { + status = LdrAddRefDll(0, lock_dll_handle); + check_timeout(FALSE); + ok(!status, "Got unexpected status %#x.\n", status); + /* Leaving an extra reference to lock.dll which will fail in process attach. */ + } + + LdrUnregisterDllNotification( cookie ); + check_timeout(FALSE); + + /* 4. Test with the thread blocked in DLL entry point during process detach. */ + trace("Test 4.\n"); + test_loader_lock_next_test(); + + status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify, NULL, &cookie); + ok(!status, "Got unexpected status %#x.\n", status); + check_timeout(FALSE); + + bret = GetModuleHandleExW(0, preloaded_dll_name, &hmodule); + ok(bret, "GetModuleHandleEx failed, err %u.\n", GetLastError()); + ok(hmodule == hmodule_preloaded, "Got unexpected hmodule %p, expected %p.\n", hmodule, hmodule_preloaded); + check_timeout(FALSE); + + if (!blocks_on_decref_library) + { + bret = FreeLibrary(hmodule); + ok(bret, "FreeLibrary failed, err %u.\n", GetLastError()); + check_timeout(FALSE); + } + + ok(!!lock_dll_handle, "Got NULL lock_dll_handle.\n"); + + status = LdrAddRefDll(0, lock_dll_handle); + check_timeout(FALSE); + ok(status == STATUS_DLL_NOT_FOUND, "Got unexpected status %#x.\n", status); + + if (BLOCKING_TESTS_ENABLED) + { + hmodule = GetModuleHandleW(L"lock.dll"); + check_timeout(!blocks_on_load_in_progress_module); + ok(!hmodule, "GetModuleHandleW succeeded.\n", GetLastError()); + ok(GetLastError() == ERROR_MOD_NOT_FOUND, "Got unexpected error %u.\n", GetLastError()); + } + + LdrUnregisterDllNotification( cookie ); + check_timeout(FALSE); + + test_loader_lock_next_test(); + + if (blocks_on_decref_library) + { + /* Cleanup. */ + bret = FreeLibrary(hmodule_preloaded); + ok(bret, "FreeLibrary failed, err %u.\n", GetLastError()); + + bret = FreeLibrary(hmodule_preloaded); + ok(bret, "FreeLibrary failed, err %u.\n", GetLastError()); + } + done: + DeleteFileA("lock.dll"); + WaitForSingleObject(thread, INFINITE); CloseHandle(thread);
diff --git a/dlls/kernel32/tests/locking_dll.c b/dlls/kernel32/tests/locking_dll.c new file mode 100644 index 00000000000..5384fae2ea3 --- /dev/null +++ b/dlls/kernel32/tests/locking_dll.c @@ -0,0 +1,45 @@ +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" + +BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, LPVOID reserved ) +{ + LONG *test_loader_lock_timeout_count = NULL; + HANDLE lock_ready_event, next_test_event; + DWORD wait_reason = ~0u; + DWORD result; + WCHAR str[32]; + + if (GetEnvironmentVariableW(L"test_wait_reason_value", str, ARRAY_SIZE(str))) + wait_reason = _wtoi(str); + + if (reason != wait_reason) return TRUE; + + lock_ready_event = OpenEventA(EVENT_ALL_ACCESS, FALSE, "test_lock_ready_event"); + next_test_event = OpenEventA(EVENT_ALL_ACCESS, FALSE, "test_next_test_event"); + + if (GetEnvironmentVariableW(L"test_timeout_count_addr", str, ARRAY_SIZE(str))) + swscanf(str, L"%p", &test_loader_lock_timeout_count); + + SetEvent( lock_ready_event ); + result = WaitForSingleObject(next_test_event, 2000); + + if (result == WAIT_TIMEOUT) + { + if (test_loader_lock_timeout_count) + ++*test_loader_lock_timeout_count; + } + + CloseHandle(lock_ready_event); + CloseHandle(next_test_event); + return reason != DLL_PROCESS_ATTACH; +} + +void WINAPI test_proc(void) +{ +} diff --git a/dlls/kernel32/tests/locking_dll.spec b/dlls/kernel32/tests/locking_dll.spec new file mode 100644 index 00000000000..e3121f28aae --- /dev/null +++ b/dlls/kernel32/tests/locking_dll.spec @@ -0,0 +1 @@ +@ stdcall test_proc()