diff --git a/modules/rostests/apitests/ntdll/CMakeLists.txt b/modules/rostests/apitests/ntdll/CMakeLists.txt index a7fdc5a2cd9..2bb13f2d3e8 100644 --- a/modules/rostests/apitests/ntdll/CMakeLists.txt +++ b/modules/rostests/apitests/ntdll/CMakeLists.txt @@ -1,6 +1,12 @@ +add_subdirectory(load_notifications) + +include_directories($) +spec2def(ntdll_apitest.exe ntdll_apitest.spec) + list(APPEND SOURCE LdrEnumResources.c + load_notifications.c NtAcceptConnectPort.c NtAllocateVirtualMemory.c NtApphelpCacheControl.c @@ -62,7 +68,13 @@ if(ARCH STREQUAL "i386") add_asm_files(ntdll_apitest_asm i386/NtContinue.S) endif() -add_executable(ntdll_apitest ${SOURCE} ${ntdll_apitest_asm} testlist.c) +add_rc_deps(testdata.rc ${CMAKE_CURRENT_BINARY_DIR}/load_notifications/load_notifications.dll) +add_executable(ntdll_apitest + ${SOURCE} + ${ntdll_apitest_asm} + testdata.rc + ${CMAKE_CURRENT_BINARY_DIR}/ntdll_apitest.def + testlist.c) target_link_libraries(ntdll_apitest wine uuid ${PSEH_LIB}) set_module_type(ntdll_apitest win32cui) add_importlibs(ntdll_apitest msvcrt advapi32 kernel32 ntdll) diff --git a/modules/rostests/apitests/ntdll/load_notifications.c b/modules/rostests/apitests/ntdll/load_notifications.c new file mode 100644 index 00000000000..430e2fc5cb6 --- /dev/null +++ b/modules/rostests/apitests/ntdll/load_notifications.c @@ -0,0 +1,330 @@ +/* + * PROJECT: ReactOS API Tests + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Test for exception behavior in dll notifications + * COPYRIGHT: Copyright 2018 Mark Jansen (mark.jansen@reactos.org) + */ + +#include "precomp.h" + +WCHAR dllpath[MAX_PATH]; + +LONG g_TlsCalled = 0; +LONG g_DllMainCalled = 0; + +LONG g_TlsExcept = 0xffffff; +LONG g_DllMainExcept = 0xffffff; + +ULONG g_BaseHandlers = 0; + +DWORD g_dwWinVer = 0; + +ULONG CountHandlers(VOID) +{ + EXCEPTION_REGISTRATION_RECORD* exc; + ULONG Count = 0; + + exc = NtCurrentTeb()->NtTib.ExceptionList; + + while (exc && exc != (EXCEPTION_REGISTRATION_RECORD*)~0) + { + Count++; + exc = exc->Next; + } + + return Count; +} + +int g_TLS_ATTACH = 4; +int g_TLS_DETACH = 3; + +VOID WINAPI notify_TlsCallback(IN HINSTANCE hDllHandle, IN DWORD dwReason, IN LPVOID lpvReserved) +{ + ULONG handlers = CountHandlers() - g_BaseHandlers; + + InterlockedIncrement(&g_TlsCalled); + if (dwReason == DLL_PROCESS_ATTACH) + { + ok_int(handlers, g_TLS_ATTACH); + } + else + { + ok_int(handlers, g_TLS_DETACH); + } + + if (InterlockedCompareExchange(&g_TlsExcept, 0xffffff, dwReason) == dwReason) + { + RaiseException(EXCEPTION_DATATYPE_MISALIGNMENT, EXCEPTION_NONCONTINUABLE, 0, NULL); + } +} + +int g_DLL_ATTACH = 3; +int g_DLL_DETACH = 2; + +BOOL WINAPI notify_DllMain(IN HINSTANCE hDllHandle, IN DWORD dwReason, IN LPVOID lpvReserved) +{ + ULONG handlers = CountHandlers() - g_BaseHandlers; + + InterlockedIncrement(&g_DllMainCalled); + if (dwReason == DLL_PROCESS_ATTACH) + { + ok_int(handlers, g_DLL_ATTACH); + } + else + { + ok_int(handlers, g_DLL_DETACH); + } + + if (InterlockedCompareExchange(&g_DllMainExcept, 0xffffff, dwReason) == dwReason) + { + RaiseException(EXCEPTION_DATATYPE_MISALIGNMENT, EXCEPTION_NONCONTINUABLE, 0, NULL); + } + return TRUE; +} + + +static void execute_test(void) +{ + HMODULE mod; + DWORD dwErr; + _SEH2_TRY + { + g_TlsExcept = 0xffffff; + g_DllMainExcept = 0xffffff; + g_DllMainCalled = 0; + g_TlsCalled = 0; + g_BaseHandlers = CountHandlers(); + mod = LoadLibraryW(dllpath); + dwErr = GetLastError(); + ok(GetModuleHandleW(dllpath) != NULL, "Unable to load module (0x%lx)\n", dwErr); + ok_hex(g_DllMainCalled, 1); + if (g_dwWinVer > _WIN32_WINNT_WS03 || g_TlsCalled) + ok_hex(g_TlsCalled, 1); + if (g_TlsCalled == 0) + trace("Tls not active\n"); + g_BaseHandlers = CountHandlers(); + FreeLibrary(mod); + dwErr = GetLastError(); + ok(GetModuleHandleW(dllpath) == NULL, "Unable to unload module (0x%lx)\n", dwErr); + ok_hex(g_DllMainCalled, 2); + if (g_dwWinVer > _WIN32_WINNT_WS03 || g_TlsCalled) + ok_hex(g_TlsCalled, 2); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + ok(0, "Unable to load it normally\n"); + } + _SEH2_END; + + + _SEH2_TRY + { + g_TlsExcept = 0xffffff; + g_DllMainExcept = DLL_PROCESS_ATTACH; + g_DllMainCalled = 0; + g_TlsCalled = 0; + g_BaseHandlers = CountHandlers(); + mod = LoadLibraryW(dllpath); + dwErr = GetLastError(); + ok(GetModuleHandleW(dllpath) == NULL, "Module loaded (0x%lx)\n", dwErr); + if (g_dwWinVer <= _WIN32_WINNT_WIN7) + ok_hex(dwErr, ERROR_NOACCESS); + else + ok_hex(dwErr, ERROR_DLL_INIT_FAILED); + ok_hex(g_DllMainCalled, 1); + if (g_dwWinVer > _WIN32_WINNT_WS03 || g_TlsCalled) + ok_hex(g_TlsCalled, 1); + if (mod) + { + FreeLibrary(mod); + dwErr = GetLastError(); + ok(GetModuleHandleW(dllpath) == NULL, "Unable to unload module (0x%lx)\n", dwErr); + ok_hex(g_DllMainCalled, 1); + if (g_dwWinVer > _WIN32_WINNT_WS03 || g_TlsCalled) + ok_hex(g_TlsCalled, 1); + } + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + ok(0, "Unable to execute test\n"); + } + _SEH2_END; + + _SEH2_TRY + { + g_TlsExcept = 0xffffff; + g_DllMainExcept = DLL_PROCESS_DETACH; + g_DllMainCalled = 0; + g_TlsCalled = 0; + g_BaseHandlers = CountHandlers(); + mod = LoadLibraryW(dllpath); + dwErr = GetLastError(); + ok(GetModuleHandleW(dllpath) != NULL, "Unable to load module (0x%lx)\n", dwErr); + ok_hex(g_DllMainCalled, 1); + if (g_dwWinVer > _WIN32_WINNT_WS03 || g_TlsCalled) + ok_hex(g_TlsCalled, 1); + g_BaseHandlers = CountHandlers(); + FreeLibrary(mod); + dwErr = GetLastError(); + ok(GetModuleHandleW(dllpath) == NULL, "Unable to unload module (0x%lx)\n", dwErr); + ok_hex(g_DllMainCalled, 2); + if (g_dwWinVer > _WIN32_WINNT_WS03 || g_TlsCalled) + ok_hex(g_TlsCalled, 2); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + ok(0, "Unable to execute test\n"); + } + _SEH2_END; + + _SEH2_TRY + { + g_TlsExcept = DLL_PROCESS_ATTACH; + g_DllMainExcept = 0xffffff; + g_DllMainCalled = 0; + g_TlsCalled = 0; + g_BaseHandlers = CountHandlers(); + mod = LoadLibraryW(dllpath); + dwErr = GetLastError(); + ok(GetModuleHandleW(dllpath) != NULL, "Unable to load module (0x%lx)\n", dwErr); + ok_hex(g_DllMainCalled, 1); + if (g_dwWinVer > _WIN32_WINNT_WS03 || g_TlsCalled) + ok_hex(g_TlsCalled, 1); + g_BaseHandlers = CountHandlers(); + FreeLibrary(mod); + dwErr = GetLastError(); + ok(GetModuleHandleW(dllpath) == NULL, "Unable to unload module (0x%lx)\n", dwErr); + ok_hex(g_DllMainCalled, 2); + if (g_dwWinVer > _WIN32_WINNT_WS03 || g_TlsCalled) + ok_hex(g_TlsCalled, 2); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + ok(0, "Unable to execute test\n"); + } + _SEH2_END; + + _SEH2_TRY + { + g_TlsExcept = DLL_PROCESS_DETACH; + g_DllMainExcept = 0xffffff; + g_DllMainCalled = 0; + g_TlsCalled = 0; + g_BaseHandlers = CountHandlers(); + mod = LoadLibraryW(dllpath); + dwErr = GetLastError(); + ok(GetModuleHandleW(dllpath) != NULL, "Unable to load module (0x%lx)\n", dwErr); + ok_hex(g_DllMainCalled, 1); + if (g_dwWinVer > _WIN32_WINNT_WS03 || g_TlsCalled) + ok_hex(g_TlsCalled, 1); + g_BaseHandlers = CountHandlers(); + FreeLibrary(mod); + dwErr = GetLastError(); + ok(GetModuleHandleW(dllpath) == NULL, "Unable to unload module (0x%lx)\n", dwErr); + ok_hex(g_DllMainCalled, 2); + if (g_dwWinVer > _WIN32_WINNT_WS03 || g_TlsCalled) + ok_hex(g_TlsCalled, 2); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + ok(0, "Unable to execute test\n"); + } + _SEH2_END; + +} + + +BOOL extract_resource(const WCHAR* Filename, LPCWSTR ResourceName) +{ + BOOL Success; + DWORD dwWritten, Size; + HGLOBAL hGlobal; + LPVOID pData; + HANDLE Handle; + HRSRC hRsrc = FindResourceW(GetModuleHandleW(NULL), ResourceName, (LPCWSTR)10); + ok(!!hRsrc, "Unable to find %s\n", wine_dbgstr_w(ResourceName)); + if (!hRsrc) + return FALSE; + + hGlobal = LoadResource(GetModuleHandleW(NULL), hRsrc); + Size = SizeofResource(GetModuleHandleW(NULL), hRsrc); + pData = LockResource(hGlobal); + + ok(Size && !!pData, "Unable to load %s\n", wine_dbgstr_w(ResourceName)); + if (!Size || !pData) + return FALSE; + + Handle = CreateFileW(Filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (Handle == INVALID_HANDLE_VALUE) + { + skip("Failed to create temp file %ls, error %lu\n", Filename, GetLastError()); + return FALSE; + } + Success = WriteFile(Handle, pData, Size, &dwWritten, NULL); + ok(Success == TRUE, "WriteFile failed with %lu\n", GetLastError()); + ok(dwWritten == Size, "WriteFile wrote %lu bytes instead of %lu\n", dwWritten, Size); + CloseHandle(Handle); + Success = Success && (dwWritten == Size); + + UnlockResource(pData); + return Success; +} + + +START_TEST(load_notifications) +{ + WCHAR workdir[MAX_PATH]; + BOOL ret; + UINT Length; + PPEB Peb = NtCurrentPeb(); + + g_dwWinVer = (DWORD)(Peb->OSMajorVersion << 8) | Peb->OSMinorVersion; + trace("Winver: 0x%lx\n", g_dwWinVer); + + if (g_dwWinVer <= _WIN32_WINNT_WS03) + { + g_DLL_ATTACH = 4; + g_DLL_DETACH = 1; + } + else if (g_dwWinVer <= _WIN32_WINNT_WS08) + { + g_TLS_ATTACH = 5; + g_DLL_ATTACH = 4; + } + else if (g_dwWinVer <= _WIN32_WINNT_WIN7) + { + g_TLS_ATTACH = 3; + g_DLL_ATTACH = 2; + } + else if (g_dwWinVer <= _WIN32_WINNT_WINBLUE) + { + g_TLS_DETACH = 5; + g_DLL_DETACH = 4; + } + + ret = GetTempPathW(_countof(workdir), workdir); + ok(ret, "GetTempPathW error: %lu\n", GetLastError()); + + Length = GetTempFileNameW(workdir, L"ntdll", 0, dllpath); + ok(Length != 0, "GetTempFileNameW failed with %lu\n", GetLastError()); + + if (extract_resource(dllpath, (LPCWSTR)101)) + { + _SEH2_TRY + { + execute_test(); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + ok(0, "Ldr didnt handle exception\n"); + } + _SEH2_END; + } + else + { + ok(0, "Failed to extract resource\n"); + } + + DeleteFileW(dllpath); +} diff --git a/modules/rostests/apitests/ntdll/load_notifications/CMakeLists.txt b/modules/rostests/apitests/ntdll/load_notifications/CMakeLists.txt new file mode 100644 index 00000000000..75436635e8a --- /dev/null +++ b/modules/rostests/apitests/ntdll/load_notifications/CMakeLists.txt @@ -0,0 +1,6 @@ + +add_library(load_notifications SHARED load_notifications.c) +set_module_type(load_notifications win32dll ENTRYPOINT DllMain 12) +add_importlibs(load_notifications kernel32 ntdll) +add_dependencies(load_notifications psdk) +add_rostests_file(TARGET load_notifications) diff --git a/modules/rostests/apitests/ntdll/load_notifications/load_notifications.c b/modules/rostests/apitests/ntdll/load_notifications/load_notifications.c new file mode 100644 index 00000000000..04578fbc430 --- /dev/null +++ b/modules/rostests/apitests/ntdll/load_notifications/load_notifications.c @@ -0,0 +1,72 @@ +#define WIN32_NO_STATUS +#define _INC_WINDOWS +#define COM_NO_WINDOWS_H + +#include + + +#if defined(_MSC_VER) +#define _CRTALLOC(x) __declspec(allocate(x)) +#elif defined(__GNUC__) +#define _CRTALLOC(x) __attribute__ ((section (x) )) +#else +#error Your compiler is not supported. +#endif + + +static VOID (WINAPI* pTlsCallback)(IN HINSTANCE hDllHandle, IN DWORD dwReason, IN LPVOID lpvReserved); + +VOID WINAPI +TlsCallback(IN HANDLE hDllHandle, + IN DWORD dwReason, + IN LPVOID lpvReserved) +{ + if (!pTlsCallback) + pTlsCallback = (VOID*)GetProcAddress(NULL, "notify_TlsCallback"); + if (pTlsCallback) + { + pTlsCallback(hDllHandle, dwReason, lpvReserved); + return; + } + OutputDebugStringA("WARNING: load_notifications.dll loaded from a process without notify_TlsCallback\n"); +} + +/* Tls magic stolen from sdk/lib/crt/startup/tlssup.c */ + +#if defined(_MSC_VER) +#pragma section(".rdata$T",long,read) +#pragma section(".tls",long,read,write) +#pragma section(".tls$ZZZ",long,read,write) +#endif + +_CRTALLOC(".tls") char _tls_start = 0; +_CRTALLOC(".tls$ZZZ") char _tls_end = 0; + +PIMAGE_TLS_CALLBACK __xl_a[2] = { &TlsCallback, NULL }; + +ULONG _tls_index = 0; + +_CRTALLOC(".rdata$T") const IMAGE_TLS_DIRECTORY _tls_used = { + (ULONG_PTR) &_tls_start+1, (ULONG_PTR) &_tls_end, + (ULONG_PTR) &_tls_index, (ULONG_PTR) (__xl_a), + (ULONG) 0, (ULONG) 0 +}; + + +static BOOL (WINAPI* pDllMain)(IN HINSTANCE hDllHandle, IN DWORD dwReason, IN LPVOID lpvReserved); + + +BOOL WINAPI +DllMain(IN HINSTANCE hDllHandle, + IN DWORD dwReason, + IN LPVOID lpvReserved) +{ + if (!pDllMain) + pDllMain = (VOID*)GetProcAddress(NULL, "notify_DllMain"); + if (pDllMain) + { + return pDllMain(hDllHandle, dwReason, lpvReserved); + } + OutputDebugStringA("WARNING: load_notifications.dll loaded from a process without notify_DllMain\n"); + return TRUE; +} diff --git a/modules/rostests/apitests/ntdll/ntdll_apitest.spec b/modules/rostests/apitests/ntdll/ntdll_apitest.spec new file mode 100644 index 00000000000..7100a7c7ef5 --- /dev/null +++ b/modules/rostests/apitests/ntdll/ntdll_apitest.spec @@ -0,0 +1,2 @@ +@ stdcall notify_TlsCallback(ptr long ptr) +@ stdcall notify_DllMain(ptr long ptr) diff --git a/modules/rostests/apitests/ntdll/testdata.rc b/modules/rostests/apitests/ntdll/testdata.rc new file mode 100644 index 00000000000..3c16769902c --- /dev/null +++ b/modules/rostests/apitests/ntdll/testdata.rc @@ -0,0 +1,2 @@ + +101 10 "load_notifications.dll" diff --git a/modules/rostests/apitests/ntdll/testlist.c b/modules/rostests/apitests/ntdll/testlist.c index c6afd1d3425..cd96ef81427 100644 --- a/modules/rostests/apitests/ntdll/testlist.c +++ b/modules/rostests/apitests/ntdll/testlist.c @@ -4,6 +4,7 @@ #include extern void func_LdrEnumResources(void); +extern void func_load_notifications(void); extern void func_NtAcceptConnectPort(void); extern void func_NtAllocateVirtualMemory(void); extern void func_NtApphelpCacheControl(void); @@ -63,6 +64,7 @@ extern void func_TimerResolution(void); const struct test winetest_testlist[] = { { "LdrEnumResources", func_LdrEnumResources }, + { "load_notifications", func_load_notifications }, { "NtAcceptConnectPort", func_NtAcceptConnectPort }, { "NtAllocateVirtualMemory", func_NtAllocateVirtualMemory }, { "NtApphelpCacheControl", func_NtApphelpCacheControl },