[PATCH v2 22/22] ntdll: Deny library load or unload from LDR notification callbacks.
Paul Gofman
pgofman at codeweavers.com
Tue Oct 5 17:49:28 CDT 2021
Signed-off-by: Paul Gofman <pgofman at 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++;
--
2.31.1
More information about the wine-devel
mailing list