Signed-off-by: Paul Gofman pgofman@codeweavers.com --- - fixed RtlIsCriticalSectionLocked() -> RtlIsCriticalSectionLockedByThread().
dlls/kernel32/tests/loader.c | 97 +++++++++++++++++++++++++++++++++++- dlls/ntdll/loader.c | 17 +++++++ 2 files changed, 113 insertions(+), 1 deletion(-)
diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c index 5a706fadc48..8532eca2633 100644 --- a/dlls/kernel32/tests/loader.c +++ b/dlls/kernel32/tests/loader.c @@ -4049,6 +4049,7 @@ 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; +static BOOL blocks_on_load_in_progress_module;
#define BLOCKING_TESTS_ENABLED 0
@@ -4062,6 +4063,53 @@ static void CALLBACK test_loader_lock_module_enum_locking_callback(LDR_DATA_TABL *stop = TRUE; }
+static void CALLBACK test_loader_lock_dummy_callback(LDR_DATA_TABLE_ENTRY *mod, + void *context, BOOLEAN *stop) +{ + *stop = TRUE; +} + +static void CALLBACK test_loader_lock_ldr_notify_locking(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context) +{ + NTSTATUS status; + HMODULE hmodule; + void *cookie; + BOOL bret; + + if (reason != LDR_DLL_NOTIFICATION_REASON_UNLOADED) return; + + status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify_locking, NULL, &cookie); + ok(!status, "Got unexpected status %#x.\n", status); + status = LdrUnregisterDllNotification( cookie ); + ok(!status, "Got unexpected status %#x.\n", status); + + hmodule = LoadLibraryW(L"winmm.dll"); + ok(!!hmodule, "GetModuleHandleW failed, err %u.\n", GetLastError()); + bret = FreeLibrary(hmodule); + ok(bret, "FreeLibrary failed, err %u.\n", GetLastError()); + + if (!blocks_on_load_in_progress_module) + { + bret = GetModuleHandleExW(0, L"lock.dll", &hmodule); + ok(bret, "GetModuleHandleExW failed, err %u.\n", GetLastError()); + ok(!!hmodule, "GetModuleHandleW failed, err %u.\n", GetLastError()); + bret = FreeLibrary(hmodule); + ok(bret, "FreeLibrary failed, err %u.\n", GetLastError()); + } + + hmodule = LoadLibraryW(L"authz.dll"); + ok(!hmodule, "LoadLibraryW succeeded.\n"); + ok(GetLastError() == ERROR_NOT_FOUND || broken(blocks_on_load_in_progress_module + && GetLastError() == ERROR_MOD_NOT_FOUND), "Got unexpected error %u.\n", GetLastError()); + + status = pLdrEnumerateLoadedModules(NULL, test_loader_lock_dummy_callback, NULL); + ok(!status, "Got unexpected status %#x.\n", status); + + SetEvent(lock_ready_event); + if (WaitForSingleObject(next_test_event, 3000) == WAIT_TIMEOUT) + ++test_loader_lock_timeout_count; +} + static DWORD WINAPI test_loader_lock_thread(void *param) { static const WCHAR env_var[] = L"test_wait_reason_value"; @@ -4069,6 +4117,7 @@ static DWORD WINAPI test_loader_lock_thread(void *param) NTSTATUS status; HMODULE hmodule; WCHAR str[32]; + void *cookie; DWORD result; BOOL bret;
@@ -4177,6 +4226,28 @@ static DWORD WINAPI test_loader_lock_thread(void *param) SetEvent(lock_ready_event); WaitForSingleObject(next_test_event, INFINITE);
+ /* 5. Test with the thread blocked in LDR notification callback. */ + LdrRegisterDllNotification(0, test_loader_lock_ldr_notify_locking, NULL, &cookie); + /* lock.dll will return FALSE from the DLL entry point. */ + bret = SetEnvironmentVariableW(env_var, L"-1"); + ok(bret, "SetEnvironmentVariableW failed.\n"); + do + { + hmodule = LoadLibraryA("lock.dll"); + ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError()); + bret = FreeLibrary(hmodule); + ok(bret, "FreeLibrary failed, err %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); + LdrUnregisterDllNotification( cookie ); + + SetEvent(lock_ready_event); + WaitForSingleObject(next_test_event, INFINITE); + SetEvent(lock_ready_event); return 0; } @@ -4222,7 +4293,6 @@ 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; WCHAR buffer[MAX_PATH]; @@ -4484,6 +4554,31 @@ static void test_loader_lock(void) LdrUnregisterDllNotification( cookie ); check_timeout(FALSE);
+ /* 5. Test with the thread blocked in LDR notification callback. */ + trace("Test 5.\n"); + test_loader_lock_next_test(); + + hmodule = GetModuleHandleW(preloaded_dll_name); + ok(!!hmodule, "LoadLibrary failed, err %u.\n", GetLastError()); + check_timeout(FALSE); + + if (BLOCKING_TESTS_ENABLED) + { + status = LdrRegisterDllNotification(0, test_loader_lock_ldr_notify, NULL, &cookie); + check_timeout(TRUE); + ok(!status, "Got unexpected status %#x.\n", status); + status = LdrUnregisterDllNotification( cookie ); + check_timeout(TRUE); + ok(!status, "Got unexpected status %#x.\n", status); + + /* This doesn't block in load notifications on Windows, only unload. */ + status = pLdrLockLoaderLock(0, NULL, &magic); + ok(!status, "Got unexpected status %#x.\n", status); + status = pLdrUnlockLoaderLock(0, magic); + ok(!status, "Got unexpected status %#x.\n", status); + check_timeout(TRUE); + } + test_loader_lock_next_test();
if (blocks_on_decref_library) diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 65cf618626b..c1a826914ea 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -2121,6 +2121,12 @@ NTSTATUS WINAPI LdrGetProcedureAddress( HMODULE module, const ANSI_STRING *name,
if (ret != STATUS_NOT_FOUND) return ret;
+ if (RtlIsCriticalSectionLockedByThread( &ldr_notifications_section )) + { + WARN( "Attempt to unload a library from notification callback.\n" ); + return STATUS_NOT_FOUND; + } + lock_loader_exclusive(); ret = get_procedure_address( module, name, ord, address ); unlock_loader(); @@ -3351,6 +3357,11 @@ NTSTATUS WINAPI DECLSPEC_HOTPATCH LdrLoadDll(LPCWSTR path_name, DWORD flags,
if (!LdrGetDllHandleEx( 0, path_name, NULL, libname, hModule )) return STATUS_SUCCESS;
+ if (RtlIsCriticalSectionLockedByThread( &ldr_notifications_section )) + { + WARN( "Attempt to load a new library from notification callback.\n" ); + return STATUS_NOT_FOUND; + } lock_loader_exclusive();
nts = load_dll( path_name, libname->Buffer, L".dll", flags, &wm ); @@ -4027,6 +4038,12 @@ NTSTATUS WINAPI LdrUnloadDll( HMODULE hModule ) unlock_loader(); if (!need_exclusive) return retv;
+ if (RtlIsCriticalSectionLockedByThread( &ldr_notifications_section )) + { + WARN( "Attempt to unload a library from notification callback.\n" ); + return STATUS_NOT_FOUND; + } + lock_loader_exclusive();
free_lib_count++;