From 3ee976389fb5de4832e8d770fc90710e07fa552b Mon Sep 17 00:00:00 2001 From: Marijn van der Werf Date: Sat, 6 Jun 2026 06:03:12 +0200 Subject: [PATCH 1/8] Add kernel32 lstrcpynA --- CMakeLists.txt | 1 + dll/kernel32/winbase.cpp | 24 ++++++++++++ dll/kernel32/winbase.h | 1 + test/test_lstrcpyn.c | 79 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 test/test_lstrcpyn.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 50cae49..554e4b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -488,6 +488,7 @@ if (WIBO_ENABLE_FIXTURE_TESTS) wibo_add_fixture_bin(NAME test_findfile SOURCES test/test_findfile.c) wibo_add_fixture_bin(NAME test_getfileattributesex SOURCES test/test_getfileattributesex.c) wibo_add_fixture_bin(NAME test_locale SOURCES test/test_locale.c) + wibo_add_fixture_bin(NAME test_lstrcpyn SOURCES test/test_lstrcpyn.c) wibo_add_fixture_bin(NAME test_critical_section SOURCES test/test_critical_section.c) wibo_add_fixture_bin(NAME test_synchapi SOURCES test/test_synchapi.c) wibo_add_fixture_bin(NAME test_processes SOURCES test/test_processes.c) diff --git a/dll/kernel32/winbase.cpp b/dll/kernel32/winbase.cpp index 91db64c..e260087 100644 --- a/dll/kernel32/winbase.cpp +++ b/dll/kernel32/winbase.cpp @@ -386,6 +386,30 @@ void ensureDefaultActivationContext() { namespace kernel32 { +LPSTR WINAPI lstrcpynA(LPSTR lpString1, LPCSTR lpString2, int iMaxLength) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("lstrcpynA(%p, %p, %d)\n", lpString1, lpString2, iMaxLength); + + UINT count = static_cast(iMaxLength); + if (!lpString1) { + return nullptr; + } + if (!lpString2 && count > 1) { + return nullptr; + } + + LPSTR destination = lpString1; + LPCSTR source = lpString2; + while (count > 1 && *source) { + count--; + *destination++ = *source++; + } + if (count) { + *destination = '\0'; + } + return lpString1; +} + ATOM WINAPI AddAtomA(LPCSTR lpString) { HOST_CONTEXT_GUARD(); ATOM atom = 0; diff --git a/dll/kernel32/winbase.h b/dll/kernel32/winbase.h index 0803910..62e6ba6 100644 --- a/dll/kernel32/winbase.h +++ b/dll/kernel32/winbase.h @@ -74,6 +74,7 @@ namespace kernel32 { BOOL WINAPI IsBadReadPtr(LPCVOID lp, UINT_PTR ucb); BOOL WINAPI IsBadWritePtr(LPVOID lp, UINT_PTR ucb); +LPSTR WINAPI lstrcpynA(LPSTR lpString1, LPCSTR lpString2, int iMaxLength); ATOM WINAPI FindAtomA(LPCSTR lpString); ATOM WINAPI FindAtomW(LPCWSTR lpString); ATOM WINAPI AddAtomA(LPCSTR lpString); diff --git a/test/test_lstrcpyn.c b/test/test_lstrcpyn.c new file mode 100644 index 0000000..f5adc38 --- /dev/null +++ b/test/test_lstrcpyn.c @@ -0,0 +1,79 @@ +#include + +#include "test_assert.h" + +static void test_full_copy(void) { + char buffer[8]; + memset(buffer, 'x', sizeof(buffer)); + + LPSTR ret = lstrcpynA(buffer, "abc", sizeof(buffer)); + + TEST_CHECK(ret == buffer); + TEST_CHECK_STR_EQ("abc", buffer); + TEST_CHECK_EQ('x', buffer[4]); +} + +static void test_truncation(void) { + char buffer[5]; + memset(buffer, 'x', sizeof(buffer)); + + LPSTR ret = lstrcpynA(buffer, "abcdef", 4); + + TEST_CHECK(ret == buffer); + TEST_CHECK_STR_EQ("abc", buffer); + TEST_CHECK_EQ('x', buffer[4]); +} + +static void test_exact_fit(void) { + char buffer[5]; + memset(buffer, 'x', sizeof(buffer)); + + LPSTR ret = lstrcpynA(buffer, "abc", 4); + + TEST_CHECK(ret == buffer); + TEST_CHECK_STR_EQ("abc", buffer); + TEST_CHECK_EQ('x', buffer[4]); +} + +static void test_one_character_buffer(void) { + char buffer[2] = {'x', 'y'}; + + LPSTR ret = lstrcpynA(buffer, "abc", 1); + + TEST_CHECK(ret == buffer); + TEST_CHECK_EQ('\0', buffer[0]); + TEST_CHECK_EQ('y', buffer[1]); +} + +static void test_zero_length(void) { + char buffer[4] = {'a', 'b', 'c', 'd'}; + + LPSTR ret = lstrcpynA(buffer, "xyz", 0); + + TEST_CHECK(ret == buffer); + TEST_CHECK_EQ('a', buffer[0]); + TEST_CHECK_EQ('b', buffer[1]); + TEST_CHECK_EQ('c', buffer[2]); + TEST_CHECK_EQ('d', buffer[3]); +} + +static void test_negative_length(void) { + char buffer[4]; + memset(buffer, 'x', sizeof(buffer)); + + LPSTR ret = lstrcpynA(buffer, "ab", -1); + + TEST_CHECK(ret == buffer); + TEST_CHECK_STR_EQ("ab", buffer); + TEST_CHECK_EQ('x', buffer[3]); +} + +int main(void) { + test_full_copy(); + test_truncation(); + test_exact_fit(); + test_one_character_buffer(); + test_zero_length(); + test_negative_length(); + return 0; +} From 57c0e3f4355a75f063ba0c7188a9f3e4c42818c5 Mon Sep 17 00:00:00 2001 From: Marijn van der Werf Date: Sat, 6 Jun 2026 06:09:44 +0200 Subject: [PATCH 2/8] Fix kernel32 GetTempPathA --- CMakeLists.txt | 1 + dll/kernel32/fileapi.cpp | 3 +++ test/test_temppath.c | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+) create mode 100644 test/test_temppath.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 554e4b7..3e52de8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -482,6 +482,7 @@ if (WIBO_ENABLE_FIXTURE_TESTS) wibo_add_fixture_bin(NAME test_ws2 SOURCES test/test_ws2.c COMPILE_OPTIONS -lws2_32) wibo_add_fixture_bin(NAME test_resources SOURCES test/test_resources.c ${WIBO_TEST_BIN_DIR}/test_resources_res.o COMPILE_OPTIONS -lversion) wibo_add_fixture_bin(NAME test_threading SOURCES test/test_threading.c) + wibo_add_fixture_bin(NAME test_temppath SOURCES test/test_temppath.c) wibo_add_fixture_bin(NAME test_tls SOURCES test/test_tls.c) wibo_add_fixture_bin(NAME test_tls_reloc SOURCES test/test_tls_reloc.c) wibo_add_fixture_bin(NAME test_handleapi SOURCES test/test_handleapi.c) diff --git a/dll/kernel32/fileapi.cpp b/dll/kernel32/fileapi.cpp index 6147472..9a00a8c 100644 --- a/dll/kernel32/fileapi.cpp +++ b/dll/kernel32/fileapi.cpp @@ -1803,6 +1803,9 @@ DWORD WINAPI GetTempPathA(DWORD nBufferLength, LPSTR lpBuffer) { } else { pathStr = "Z:\\tmp\\"; } + if (!pathStr.empty() && pathStr.back() != '\\') { + pathStr.push_back('\\'); + } size_t len = pathStr.length(); if (len + 1 > nBufferLength) { setLastError(ERROR_INSUFFICIENT_BUFFER); diff --git a/test/test_temppath.c b/test/test_temppath.c new file mode 100644 index 0000000..1579748 --- /dev/null +++ b/test/test_temppath.c @@ -0,0 +1,19 @@ +#include "test_assert.h" + +#include +#include + +int main(void) { + char buffer[MAX_PATH]; + DWORD len = GetTempPathA(sizeof(buffer), buffer); + TEST_CHECK(len > 0 && len < sizeof(buffer)); + TEST_CHECK_EQ(len, strlen(buffer)); + TEST_CHECK_EQ('\\', buffer[len - 1]); + + char too_small[MAX_PATH]; + memset(too_small, 0xCC, sizeof(too_small)); + DWORD required = GetTempPathA(len, too_small); + TEST_CHECK_EQ(len + 1, required); + + return 0; +} From cf4f0c67e9099a0f3591faa6c2630b29c44f7ee4 Mon Sep 17 00:00:00 2001 From: Marijn van der Werf Date: Sat, 6 Jun 2026 06:23:21 +0200 Subject: [PATCH 3/8] Fix kernel32 GetTempFileNameA --- CMakeLists.txt | 1 + dll/kernel32/fileapi.cpp | 58 +++++++++++++++++++++++++++---------- test/test_gettempfilename.c | 51 ++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 15 deletions(-) create mode 100644 test/test_gettempfilename.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e52de8..e17b976 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -483,6 +483,7 @@ if (WIBO_ENABLE_FIXTURE_TESTS) wibo_add_fixture_bin(NAME test_resources SOURCES test/test_resources.c ${WIBO_TEST_BIN_DIR}/test_resources_res.o COMPILE_OPTIONS -lversion) wibo_add_fixture_bin(NAME test_threading SOURCES test/test_threading.c) wibo_add_fixture_bin(NAME test_temppath SOURCES test/test_temppath.c) + wibo_add_fixture_bin(NAME test_gettempfilename SOURCES test/test_gettempfilename.c) wibo_add_fixture_bin(NAME test_tls SOURCES test/test_tls.c) wibo_add_fixture_bin(NAME test_tls_reloc SOURCES test/test_tls_reloc.c) wibo_add_fixture_bin(NAME test_handleapi SOURCES test/test_handleapi.c) diff --git a/dll/kernel32/fileapi.cpp b/dll/kernel32/fileapi.cpp index 9a00a8c..b65ce90 100644 --- a/dll/kernel32/fileapi.cpp +++ b/dll/kernel32/fileapi.cpp @@ -6,6 +6,7 @@ #include "context.h" #include "errors.h" #include "files.h" +#include "handleapi.h" #include "handles.h" #include "internal.h" #include "namedpipeapi.h" @@ -1755,33 +1756,60 @@ UINT WINAPI GetTempFileNameA(LPCSTR lpPathName, LPCSTR lpPrefixString, UINT uUni setLastError(ERROR_BUFFER_OVERFLOW); return 0; } - char uniqueStr[20]; - std::filesystem::path path; + + auto makeTempFileName = [&](UINT unique) { + char filename[12]; + snprintf(filename, sizeof(filename), "%.3s%04X.TMP", lpPrefixString, unique & 0xFFFF); + + std::string path = lpPathName; + if (!path.empty() && path.back() != '\\' && path.back() != '/') { + path.push_back('\\'); + } + path += filename; + return path; + }; if (uUnique == 0) { std::random_device rd; random_shorts_engine rse(rd()); - while (true) { - uUnique = rse(); + UINT startUnique = rse(); + if (startUnique == 0) { + startUnique = 1; + } + bool created = false; + for (UINT attempt = 0; attempt < 0xFFFF; ++attempt) { + uUnique = (startUnique + attempt) & 0xFFFF; if (uUnique == 0) { continue; } - snprintf(uniqueStr, sizeof(uniqueStr), "%.3s%X.TMP", lpPrefixString, uUnique); - path = files::pathFromWindows(lpPathName) / uniqueStr; - int fd = open(path.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0644); - if (fd >= 0) { - close(fd); + std::string path = makeTempFileName(uUnique); + HANDLE handle = CreateFileA(path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_NEW, + FILE_ATTRIBUTE_TEMPORARY, NO_HANDLE); + if (handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle); + strncpy(lpTempFileName, path.c_str(), MAX_PATH); + lpTempFileName[MAX_PATH - 1] = '\0'; + DEBUG_LOG(" -> %s\n", lpTempFileName); + created = true; break; } + DWORD error = getLastError(); + if (error != ERROR_FILE_EXISTS && error != ERROR_ALREADY_EXISTS) { + DEBUG_LOG(" -> error %u\n", error); + return 0; + } + } + if (!created) { + setLastError(ERROR_FILE_EXISTS); + DEBUG_LOG(" -> ERROR_FILE_EXISTS\n"); + return 0; } } else { - snprintf(uniqueStr, sizeof(uniqueStr), "%.3s%X.TMP", lpPrefixString, uUnique & 0xFFFF); - path = files::pathFromWindows(lpPathName) / uniqueStr; + std::string path = makeTempFileName(uUnique); + strncpy(lpTempFileName, path.c_str(), MAX_PATH); + lpTempFileName[MAX_PATH - 1] = '\0'; + DEBUG_LOG(" -> %s\n", lpTempFileName); } - std::string str = files::pathToWindows(path); - DEBUG_LOG(" -> %s\n", str.c_str()); - strncpy(lpTempFileName, str.c_str(), MAX_PATH); - lpTempFileName[MAX_PATH - 1] = '\0'; return uUnique; } diff --git a/test/test_gettempfilename.c b/test/test_gettempfilename.c new file mode 100644 index 0000000..776a4d6 --- /dev/null +++ b/test/test_gettempfilename.c @@ -0,0 +1,51 @@ +#include "test_assert.h" + +#include +#include +#include + +static const char *basename_of(const char *path) { + const char *slash = strrchr(path, '\\'); + return slash ? slash + 1 : path; +} + +static void expect_fixed_unique_name(const char *temp_path) { + char temp_file[MAX_PATH]; + + UINT result = GetTempFileNameA(temp_path, "wboX", 0x12345, temp_file); + TEST_CHECK_EQ(0x12345, result); + DeleteFileA(temp_file); + + result = GetTempFileNameA(temp_path, "wboX", 0x12345, temp_file); + TEST_CHECK_EQ(0x12345, result); + TEST_CHECK_STR_EQ("wbo2345.TMP", basename_of(temp_file)); + TEST_CHECK_EQ(INVALID_FILE_ATTRIBUTES, GetFileAttributesA(temp_file)); +} + +static void expect_created_file_reopens_like_link(const char *temp_path) { + char temp_file[MAX_PATH]; + + UINT result = GetTempFileNameA(temp_path, "GDI32", 0, temp_file); + TEST_CHECK(result != 0); + TEST_CHECK_EQ(11, strlen(basename_of(temp_file))); + TEST_CHECK_EQ(0, strncmp("GDI", basename_of(temp_file), 3)); + TEST_CHECK_STR_EQ(".TMP", basename_of(temp_file) + 7); + TEST_CHECK(GetFileAttributesA(temp_file) != INVALID_FILE_ATTRIBUTES); + + HANDLE handle = CreateFileA(temp_file, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL); + TEST_CHECK_MSG(handle != INVALID_HANDLE_VALUE, "CreateFileA(temp, OPEN_EXISTING|DELETE_ON_CLOSE) failed: %lu", + GetLastError()); + TEST_CHECK(CloseHandle(handle)); +} + +int main(void) { + char temp_path[MAX_PATH]; + DWORD len = GetTempPathA(sizeof(temp_path), temp_path); + TEST_CHECK(len > 0 && len < sizeof(temp_path)); + + expect_fixed_unique_name(temp_path); + expect_created_file_reopens_like_link(temp_path); + + return 0; +} From c641f20cb349030f573697479a8f2070484d377d Mon Sep 17 00:00:00 2001 From: Marijn van der Werf Date: Sat, 6 Jun 2026 06:29:42 +0200 Subject: [PATCH 4/8] Fix kernel32 GetProcAddress missing exports --- CMakeLists.txt | 1 + dll/kernel32/libloaderapi.cpp | 4 ++-- src/modules.cpp | 33 +++++++++++++++++++++++++-------- src/modules.h | 2 ++ test/test_getprocaddress.c | 19 +++++++++++++++++++ 5 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 test/test_getprocaddress.c diff --git a/CMakeLists.txt b/CMakeLists.txt index e17b976..48df9a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -484,6 +484,7 @@ if (WIBO_ENABLE_FIXTURE_TESTS) wibo_add_fixture_bin(NAME test_threading SOURCES test/test_threading.c) wibo_add_fixture_bin(NAME test_temppath SOURCES test/test_temppath.c) wibo_add_fixture_bin(NAME test_gettempfilename SOURCES test/test_gettempfilename.c) + wibo_add_fixture_bin(NAME test_getprocaddress SOURCES test/test_getprocaddress.c) wibo_add_fixture_bin(NAME test_tls SOURCES test/test_tls.c) wibo_add_fixture_bin(NAME test_tls_reloc SOURCES test/test_tls_reloc.c) wibo_add_fixture_bin(NAME test_handleapi SOURCES test/test_handleapi.c) diff --git a/dll/kernel32/libloaderapi.cpp b/dll/kernel32/libloaderapi.cpp index dfc2e08..ea26ec2 100644 --- a/dll/kernel32/libloaderapi.cpp +++ b/dll/kernel32/libloaderapi.cpp @@ -283,10 +283,10 @@ FARPROC WINAPI GetProcAddress(HMODULE hModule, LPCSTR lpProcName) { const auto proc = reinterpret_cast(lpProcName); if (proc & ~0xFFFFu) { DEBUG_LOG("GetProcAddress(%s, %s) ", info->normalizedName.c_str(), lpProcName); - result = wibo::resolveFuncByName(info, lpProcName); + result = wibo::findExportByName(info, lpProcName); } else { DEBUG_LOG("GetProcAddress(%s, %u) ", info->normalizedName.c_str(), proc); - result = wibo::resolveFuncByOrdinal(info, static_cast(proc)); + result = wibo::findExportByOrdinal(info, static_cast(proc)); } DEBUG_LOG("-> %p\n", result); if (!result) { diff --git a/src/modules.cpp b/src/modules.cpp index 5c930d2..10defa3 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -1397,8 +1397,8 @@ void freeModule(ModuleInfo *info) { } } -void *resolveFuncByName(ModuleInfo *info, const char *funcName) { - if (!info) { +void *findExportByName(ModuleInfo *info, const char *funcName) { + if (!info || !funcName) { return nullptr; } if (info->moduleStub && info->moduleStub->byName) { @@ -1410,15 +1410,12 @@ void *resolveFuncByName(ModuleInfo *info, const char *funcName) { ensureExportsInitialized(*info); auto it = info->exportNameToOrdinal.find(funcName); if (it != info->exportNameToOrdinal.end()) { - return resolveFuncByOrdinal(info, it->second); - } - if (info->moduleStub) { - return reinterpret_cast(resolveMissingFuncName(info->originalName.c_str(), funcName)); + return findExportByOrdinal(info, it->second); } return nullptr; } -void *resolveFuncByOrdinal(ModuleInfo *info, uint16_t ordinal) { +void *findExportByOrdinal(ModuleInfo *info, uint16_t ordinal) { if (!info) { return nullptr; } @@ -1438,7 +1435,27 @@ void *resolveFuncByOrdinal(ModuleInfo *info, uint16_t ordinal) { } } } - if (info->moduleStub) { + return nullptr; +} + +void *resolveFuncByName(ModuleInfo *info, const char *funcName) { + void *func = findExportByName(info, funcName); + if (func) { + return func; + } + if (info && info->moduleStub) { + const char *safeFunc = funcName ? funcName : ""; + return reinterpret_cast(resolveMissingFuncName(info->originalName.c_str(), safeFunc)); + } + return nullptr; +} + +void *resolveFuncByOrdinal(ModuleInfo *info, uint16_t ordinal) { + void *func = findExportByOrdinal(info, ordinal); + if (func) { + return func; + } + if (info && info->moduleStub) { return reinterpret_cast(resolveMissingFuncOrdinal(info->originalName.c_str(), ordinal)); } return nullptr; diff --git a/src/modules.h b/src/modules.h index ca3c0fe..3e652ae 100644 --- a/src/modules.h +++ b/src/modules.h @@ -128,6 +128,8 @@ void releaseModuleTls(ModuleInfo &module); ModuleInfo *loadModule(const char *name); void freeModule(ModuleInfo *info); +void *findExportByName(ModuleInfo *info, const char *funcName); +void *findExportByOrdinal(ModuleInfo *info, uint16_t ordinal); void *resolveFuncByName(ModuleInfo *info, const char *funcName); void *resolveFuncByOrdinal(ModuleInfo *info, uint16_t ordinal); void *resolveMissingImportByName(const char *dllName, const char *funcName); diff --git a/test/test_getprocaddress.c b/test/test_getprocaddress.c new file mode 100644 index 0000000..928253b --- /dev/null +++ b/test/test_getprocaddress.c @@ -0,0 +1,19 @@ +#include + +#include "test_assert.h" + +int main(void) { + HMODULE kernel32 = GetModuleHandleA("kernel32.dll"); + TEST_CHECK_MSG(kernel32 != NULL, "GetModuleHandleA(kernel32.dll) failed: %lu", (unsigned long)GetLastError()); + + SetLastError(0xdeadbeef); + FARPROC present = GetProcAddress(kernel32, "GetModuleHandleA"); + TEST_CHECK_MSG(present != NULL, "GetProcAddress(GetModuleHandleA) failed: %lu", (unsigned long)GetLastError()); + + SetLastError(0xdeadbeef); + FARPROC missing = GetProcAddress(kernel32, "IsTNT"); + TEST_CHECK(missing == NULL); + TEST_CHECK_EQ(ERROR_PROC_NOT_FOUND, GetLastError()); + + return 0; +} From 7ca29f889d4733c07962d99253eef8d3be6368a0 Mon Sep 17 00:00:00 2001 From: Marijn van der Werf Date: Sat, 6 Jun 2026 11:21:11 +0200 Subject: [PATCH 5/8] Fix default ACTCTX DLL redirections --- dll/kernel32/winbase.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/dll/kernel32/winbase.cpp b/dll/kernel32/winbase.cpp index e260087..7dba258 100644 --- a/dll/kernel32/winbase.cpp +++ b/dll/kernel32/winbase.cpp @@ -366,16 +366,25 @@ void ensureDefaultActivationContext() { std::call_once(initFlag, [] { ActivationContext *ctx = currentActivationContext(); auto addDll = [ctx](const std::string &name) { + std::string lowerName = stringToLower(name); + for (const auto &entry : ctx->dllRedirections) { + if (entry.nameLower == lowerName) { + return; + } + } + DllRedirectionEntry entry; - entry.nameLower = stringToLower(name); + entry.nameLower = std::move(lowerName); entry.dllData = wibo::heap::make_guest_unique(); - entry.dllData->Size = sizeof(entry.dllData); + entry.dllData->Size = sizeof(ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION); entry.dllData->Flags = ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_OMITS_ASSEMBLY_ROOT; entry.dllData->TotalPathLength = 0; entry.dllData->PathSegmentCount = 0; entry.dllData->PathSegmentOffset = 0; ctx->dllRedirections.emplace_back(std::move(entry)); }; + addDll("msvcr80.dll"); + addDll("msvcp80.dll"); for (const auto &[key, module] : wibo::allLoadedModules()) { if (!module->moduleStub) { addDll(module->normalizedName); From 314caadb1243cf4f0fe277191acc0e15ce28cf92 Mon Sep 17 00:00:00 2001 From: Marijn van der Werf Date: Sat, 6 Jun 2026 11:38:27 +0200 Subject: [PATCH 6/8] Fix kernel32 CreateFileMappingA file growth --- CMakeLists.txt | 1 + dll/kernel32/memoryapi.cpp | 64 ++++++++++++++++++++++++++++-- src/errors.cpp | 2 + src/errors.h | 2 + test/test_createfilemapping.c | 75 +++++++++++++++++++++++++++++++++++ 5 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 test/test_createfilemapping.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 48df9a0..a5755b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -502,6 +502,7 @@ if (WIBO_ENABLE_FIXTURE_TESTS) wibo_add_fixture_bin(NAME test_ntdll_time SOURCES test/test_ntdll_time.c) wibo_add_fixture_bin(NAME test_virtualalloc SOURCES test/test_virtualalloc.c) wibo_add_fixture_bin(NAME test_virtualquery SOURCES test/test_virtualquery.c) + wibo_add_fixture_bin(NAME test_createfilemapping SOURCES test/test_createfilemapping.c) wibo_add_fixture_bin(NAME test_clsids SOURCES test/test_clsids.c COMPILE_OPTIONS -lole32) wibo_add_fixture_bin(NAME test_rtl SOURCES test/test_rtl.c) wibo_add_fixture_bin(NAME test_rtl_bitmap SOURCES test/test_rtl_bitmap.c) diff --git a/dll/kernel32/memoryapi.cpp b/dll/kernel32/memoryapi.cpp index 48a7907..39a0fa6 100644 --- a/dll/kernel32/memoryapi.cpp +++ b/dll/kernel32/memoryapi.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -174,6 +175,49 @@ bool mappedViewRegionForAddress(uintptr_t request, uintptr_t pageBase, MEMORY_BA return false; } +bool protectAllowsFileGrowth(DWORD protect) { return protect == PAGE_READWRITE || protect == PAGE_EXECUTE_READWRITE; } + +bool fileSizeFromFd(int fd, uint64_t &size) { + struct stat st{}; + if (fstat(fd, &st) != 0) { + kernel32::setLastErrorFromErrno(); + return false; + } + if (st.st_size < 0) { + kernel32::setLastError(ERROR_INVALID_PARAMETER); + return false; + } + size = static_cast(st.st_size); + return true; +} + +DWORD fileGrowthErrorFromErrno(int err) { + switch (err) { + case ENOSPC: +#ifdef EDQUOT + case EDQUOT: +#endif +#ifdef EFBIG + case EFBIG: +#endif + return ERROR_DISK_FULL; + default: + return wibo::winErrorFromErrno(err); + } +} + +bool growFileForMapping(int fd, uint64_t size) { + if (size > static_cast(std::numeric_limits::max())) { + kernel32::setLastError(ERROR_INVALID_PARAMETER); + return false; + } + if (ftruncate(fd, static_cast(size)) != 0) { + kernel32::setLastError(fileGrowthErrorFromErrno(errno)); + return false; + } + return true; +} + } // namespace namespace kernel32 { @@ -216,12 +260,26 @@ HANDLE WINAPI CreateFileMappingA(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappi return NO_HANDLE; } mapping->fd = dupFd; + uint64_t fileSize = 0; + if (!fileSizeFromFd(dupFd, fileSize)) { + return NO_HANDLE; + } if (size == 0) { - off_t fileSize = lseek(dupFd, 0, SEEK_END); - if (fileSize < 0) { + if (fileSize == 0) { + setLastError(ERROR_FILE_INVALID); + return NO_HANDLE; + } + size = fileSize; + } else if (size > fileSize && protectAllowsFileGrowth(flProtect)) { + if (!growFileForMapping(dupFd, size)) { return NO_HANDLE; } - size = static_cast(fileSize); + DEBUG_LOG("CreateFileMappingA: grew backing file from %llu to %llu bytes\n", + static_cast(fileSize), static_cast(size)); + } + if (size > static_cast(std::numeric_limits::max())) { + setLastError(ERROR_INVALID_PARAMETER); + return NO_HANDLE; } mapping->maxSize = size; } diff --git a/src/errors.cpp b/src/errors.cpp index b442612..d984e13 100644 --- a/src/errors.cpp +++ b/src/errors.cpp @@ -20,6 +20,8 @@ DWORD winErrorFromErrno(int err) { return ERROR_PATH_NOT_FOUND; case ENOMEM: return ERROR_NOT_ENOUGH_MEMORY; + case ENOSPC: + return ERROR_DISK_FULL; case EINVAL: return ERROR_INVALID_PARAMETER; case EINTR: diff --git a/src/errors.h b/src/errors.h index 422db2d..06638fe 100644 --- a/src/errors.h +++ b/src/errors.h @@ -22,6 +22,7 @@ #define ERROR_ENVVAR_NOT_FOUND 203 #define ERROR_CALL_NOT_IMPLEMENTED 120 #define ERROR_BUFFER_OVERFLOW 111 +#define ERROR_DISK_FULL 112 #define ERROR_INSUFFICIENT_BUFFER 122 #define ERROR_INVALID_NAME 123 #define ERROR_IO_INCOMPLETE 996 @@ -48,6 +49,7 @@ #define ERROR_TOO_MANY_POSTS 298 #define ERROR_SEM_TIMEOUT 121 #define ERROR_SXS_KEY_NOT_FOUND 14007 +#define ERROR_FILE_INVALID 1006 #define INVALID_SET_FILE_POINTER ((DWORD) - 1) #define INVALID_HANDLE_VALUE ((HANDLE) - 1) diff --git a/test/test_createfilemapping.c b/test/test_createfilemapping.c new file mode 100644 index 0000000..fbcf0bd --- /dev/null +++ b/test/test_createfilemapping.c @@ -0,0 +1,75 @@ +#include + +#include +#include + +#include "test_assert.h" + +static const char kTempFileName[] = "test_createfilemapping.tmp"; + +static DWORD checked_file_size(HANDLE file) { + DWORD high = 0; + DWORD low = GetFileSize(file, &high); + TEST_CHECK_MSG(low != INVALID_FILE_SIZE || GetLastError() == NO_ERROR, "GetFileSize failed: %lu", GetLastError()); + TEST_CHECK_EQ(0u, high); + return low; +} + +static HANDLE create_temp_file(void) { + HANDLE file = CreateFileA(kTempFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); + TEST_CHECK_MSG(file != INVALID_HANDLE_VALUE, "CreateFileA failed: %lu", GetLastError()); + return file; +} + +static void test_readwrite_mapping_extends_file(void) { + const DWORD mapping_size = 262144; + HANDLE file = create_temp_file(); + TEST_CHECK_EQ(0u, checked_file_size(file)); + + HANDLE mapping = CreateFileMappingA(file, NULL, PAGE_READWRITE, 0, mapping_size, NULL); + TEST_CHECK_MSG(mapping != NULL, "CreateFileMappingA(PAGE_READWRITE) failed: %lu", GetLastError()); + TEST_CHECK_EQ(mapping_size, checked_file_size(file)); + + uint8_t *view = (uint8_t *)MapViewOfFileEx(mapping, FILE_MAP_ALL_ACCESS, 0, 0, mapping_size, NULL); + TEST_CHECK_MSG(view != NULL, "MapViewOfFileEx failed: %lu", GetLastError()); + view[0] = 0x11; + view[mapping_size - 1] = 0x5a; + TEST_CHECK(FlushViewOfFile(view, mapping_size)); + TEST_CHECK(UnmapViewOfFile(view)); + TEST_CHECK(CloseHandle(mapping)); + + TEST_CHECK(SetFilePointer(file, mapping_size - 1, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER); + uint8_t value = 0; + DWORD bytes_read = 0; + TEST_CHECK(ReadFile(file, &value, sizeof(value), &bytes_read, NULL)); + TEST_CHECK_EQ(1u, bytes_read); + TEST_CHECK_EQ(0x5au, value); + + TEST_CHECK(CloseHandle(file)); + TEST_CHECK(GetFileAttributesA(kTempFileName) == INVALID_FILE_ATTRIBUTES); +} + +static void test_zero_size_file_mapping_fails(void) { + HANDLE file = create_temp_file(); + SetLastError(0xdeadbeef); + HANDLE mapping = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, NULL); + TEST_CHECK(mapping == NULL); + TEST_CHECK_EQ(ERROR_FILE_INVALID, GetLastError()); + TEST_CHECK(CloseHandle(file)); + TEST_CHECK(GetFileAttributesA(kTempFileName) == INVALID_FILE_ATTRIBUTES); +} + +static void test_zero_size_pagefile_mapping_fails(void) { + SetLastError(0xdeadbeef); + HANDLE mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 0, NULL); + TEST_CHECK(mapping == NULL); + TEST_CHECK_EQ(ERROR_INVALID_PARAMETER, GetLastError()); +} + +int main(void) { + test_readwrite_mapping_extends_file(); + test_zero_size_file_mapping_fails(); + test_zero_size_pagefile_mapping_fails(); + return 0; +} From 45ee01a29656236f486903163e652ed08ac957db Mon Sep 17 00:00:00 2001 From: Marijn van der Werf Date: Sat, 6 Jun 2026 11:45:22 +0200 Subject: [PATCH 7/8] Fix kernel32 MapViewOfFile all-access writes --- dll/kernel32/memoryapi.cpp | 83 +++++++++++++++++++---------------- test/test_createfilemapping.c | 48 +++++++++++++++++--- 2 files changed, 86 insertions(+), 45 deletions(-) diff --git a/dll/kernel32/memoryapi.cpp b/dll/kernel32/memoryapi.cpp index 39a0fa6..b43badd 100644 --- a/dll/kernel32/memoryapi.cpp +++ b/dll/kernel32/memoryapi.cpp @@ -78,53 +78,64 @@ uintptr_t alignUp(uintptr_t value, size_t alignment) { return (value + mask) & ~mask; } +struct FileMapAccess { + bool read = false; + bool write = false; + bool execute = false; + bool copy = false; +}; + +FileMapAccess fileMapAccessFromDesiredAccess(DWORD desiredAccess) { + // FILE_MAP_ALL_ACCESS includes FILE_MAP_COPY's bit, but maps as a writable shared view. + const bool allAccess = (desiredAccess & FILE_MAP_ALL_ACCESS) == FILE_MAP_ALL_ACCESS; + FileMapAccess access{}; + access.read = allAccess || (desiredAccess & (FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_COPY)) != 0; + access.write = allAccess || (desiredAccess & FILE_MAP_WRITE) != 0; + access.execute = (desiredAccess & FILE_MAP_EXECUTE) != 0; + access.copy = !allAccess && (desiredAccess & FILE_MAP_COPY) != 0; + if (access.copy) { + access.write = true; + } + return access; +} + DWORD desiredAccessToProtect(DWORD desiredAccess, DWORD mappingProtect) { - DWORD access = desiredAccess; - if ((access & FILE_MAP_ALL_ACCESS) == FILE_MAP_ALL_ACCESS) { - access |= FILE_MAP_READ | FILE_MAP_WRITE; - } - bool wantExecute = (access & FILE_MAP_EXECUTE) != 0; - bool wantWrite = (access & FILE_MAP_WRITE) != 0; - bool wantCopy = (access & FILE_MAP_COPY) != 0; - bool wantRead = (access & (FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_COPY)) != 0; - if (wantCopy) { - wantWrite = true; - } + FileMapAccess access = fileMapAccessFromDesiredAccess(desiredAccess); const bool supportsWrite = mappingProtect == PAGE_READWRITE || mappingProtect == PAGE_EXECUTE_READWRITE || mappingProtect == PAGE_WRITECOPY || mappingProtect == PAGE_EXECUTE_WRITECOPY; const bool supportsCopy = mappingProtect == PAGE_WRITECOPY || mappingProtect == PAGE_EXECUTE_WRITECOPY; - if (wantCopy && !supportsCopy) { - wantCopy = false; + if (access.copy && !supportsCopy) { + access.copy = false; } - if (wantWrite && !supportsWrite) { + if (access.write && !supportsWrite) { if (supportsCopy) { - wantCopy = true; - wantWrite = false; + access.copy = true; + access.write = false; } else { - wantWrite = false; + access.write = false; } } - if (!wantRead && (mappingProtect == PAGE_READONLY || mappingProtect == PAGE_EXECUTE_READ || - mappingProtect == PAGE_WRITECOPY || mappingProtect == PAGE_EXECUTE_WRITECOPY)) { - wantRead = true; + if (!access.read && (mappingProtect == PAGE_READONLY || mappingProtect == PAGE_EXECUTE_READ || + mappingProtect == PAGE_WRITECOPY || mappingProtect == PAGE_EXECUTE_WRITECOPY)) { + access.read = true; } DWORD protect = PAGE_NOACCESS; - if (wantCopy && supportsCopy) { - protect = wantExecute ? PAGE_EXECUTE_WRITECOPY : PAGE_WRITECOPY; - } else if (wantExecute) { - if (wantWrite) { + if (access.copy && supportsCopy) { + protect = access.execute ? PAGE_EXECUTE_WRITECOPY : PAGE_WRITECOPY; + } else if (access.execute) { + if (access.write) { protect = PAGE_EXECUTE_READWRITE; - } else if (wantRead) { + } else if (access.read) { protect = PAGE_EXECUTE_READ; } else { protect = PAGE_EXECUTE; } } else { - if (wantWrite) { + if (access.write) { protect = PAGE_READWRITE; - } else if (wantRead) { + } else if (access.read) { protect = PAGE_READONLY; } } @@ -328,32 +339,26 @@ static LPVOID mapViewOfFileInternal(Pin mapping, DWORD dwDesiredA return nullptr; } - bool wantWrite = (dwDesiredAccess & FILE_MAP_WRITE) != 0; - bool wantExecute = (dwDesiredAccess & FILE_MAP_EXECUTE) != 0; - bool wantCopy = (dwDesiredAccess & FILE_MAP_COPY) != 0; - bool wantAllAccess = (dwDesiredAccess & FILE_MAP_ALL_ACCESS) == FILE_MAP_ALL_ACCESS; - if (wantAllAccess) { - wantWrite = true; - } + FileMapAccess access = fileMapAccessFromDesiredAccess(dwDesiredAccess); int prot = PROT_READ; if (mapping->protect == PAGE_READWRITE) { - if (wantWrite || wantCopy) { + if (access.write || access.copy) { prot |= PROT_WRITE; } } else { - if (wantWrite && !wantCopy) { + if (access.write && !access.copy) { setLastError(ERROR_ACCESS_DENIED); return nullptr; } - if (wantCopy) { + if (access.copy) { prot |= PROT_WRITE; } } - if (wantExecute) { + if (access.execute) { prot |= PROT_EXEC; } - int flags = (mapping->anonymous ? MAP_ANONYMOUS : 0) | (wantCopy ? MAP_PRIVATE : MAP_SHARED); + int flags = (mapping->anonymous ? MAP_ANONYMOUS : 0) | (access.copy ? MAP_PRIVATE : MAP_SHARED); const size_t pageSize = wibo::heap::systemPageSize(); off_t alignedOffset = mapping->anonymous ? 0 : static_cast(offset & ~static_cast(pageSize - 1)); size_t offsetDelta = static_cast(offset - static_cast(alignedOffset)); diff --git a/test/test_createfilemapping.c b/test/test_createfilemapping.c index fbcf0bd..9875687 100644 --- a/test/test_createfilemapping.c +++ b/test/test_createfilemapping.c @@ -22,6 +22,22 @@ static HANDLE create_temp_file(void) { return file; } +static void write_byte_at(HANDLE file, DWORD offset, uint8_t value) { + DWORD bytes_written = 0; + TEST_CHECK(SetFilePointer(file, offset, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER); + TEST_CHECK(WriteFile(file, &value, sizeof(value), &bytes_written, NULL)); + TEST_CHECK_EQ(1u, bytes_written); +} + +static uint8_t read_byte_at(HANDLE file, DWORD offset) { + uint8_t value = 0; + DWORD bytes_read = 0; + TEST_CHECK(SetFilePointer(file, offset, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER); + TEST_CHECK(ReadFile(file, &value, sizeof(value), &bytes_read, NULL)); + TEST_CHECK_EQ(1u, bytes_read); + return value; +} + static void test_readwrite_mapping_extends_file(void) { const DWORD mapping_size = 262144; HANDLE file = create_temp_file(); @@ -39,12 +55,31 @@ static void test_readwrite_mapping_extends_file(void) { TEST_CHECK(UnmapViewOfFile(view)); TEST_CHECK(CloseHandle(mapping)); - TEST_CHECK(SetFilePointer(file, mapping_size - 1, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER); - uint8_t value = 0; - DWORD bytes_read = 0; - TEST_CHECK(ReadFile(file, &value, sizeof(value), &bytes_read, NULL)); - TEST_CHECK_EQ(1u, bytes_read); - TEST_CHECK_EQ(0x5au, value); + TEST_CHECK_EQ(0x11u, read_byte_at(file, 0)); + TEST_CHECK_EQ(0x5au, read_byte_at(file, mapping_size - 1)); + + TEST_CHECK(CloseHandle(file)); + TEST_CHECK(GetFileAttributesA(kTempFileName) == INVALID_FILE_ATTRIBUTES); +} + +static void test_copy_mapping_does_not_write_file(void) { + const DWORD mapping_size = 4096; + HANDLE file = create_temp_file(); + write_byte_at(file, 0, 0x24); + + HANDLE mapping = CreateFileMappingA(file, NULL, PAGE_READWRITE, 0, mapping_size, NULL); + TEST_CHECK_MSG(mapping != NULL, "CreateFileMappingA(PAGE_READWRITE) failed: %lu", GetLastError()); + + uint8_t *view = (uint8_t *)MapViewOfFileEx(mapping, FILE_MAP_COPY, 0, 0, mapping_size, NULL); + TEST_CHECK_MSG(view != NULL, "MapViewOfFileEx(FILE_MAP_COPY) failed: %lu", GetLastError()); + TEST_CHECK_EQ(0x24u, view[0]); + view[0] = 0xa5; + view[mapping_size - 1] = 0x5c; + TEST_CHECK(UnmapViewOfFile(view)); + TEST_CHECK(CloseHandle(mapping)); + + TEST_CHECK_EQ(0x24u, read_byte_at(file, 0)); + TEST_CHECK_EQ(0x00u, read_byte_at(file, mapping_size - 1)); TEST_CHECK(CloseHandle(file)); TEST_CHECK(GetFileAttributesA(kTempFileName) == INVALID_FILE_ATTRIBUTES); @@ -69,6 +104,7 @@ static void test_zero_size_pagefile_mapping_fails(void) { int main(void) { test_readwrite_mapping_extends_file(); + test_copy_mapping_does_not_write_file(); test_zero_size_file_mapping_fails(); test_zero_size_pagefile_mapping_fails(); return 0; From cf8a7e2aee3b861f1b77d3e92c240c0e3886ea5c Mon Sep 17 00:00:00 2001 From: Marijn van der Werf Date: Sat, 6 Jun 2026 11:54:19 +0200 Subject: [PATCH 8/8] Add kernel32 GetModuleHandleExW --- CMakeLists.txt | 1 + dll/kernel32/libloaderapi.cpp | 45 +++++++++++++++++++++++++++++ dll/kernel32/libloaderapi.h | 1 + test/test_getmodulehandleex.c | 54 +++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 test/test_getmodulehandleex.c diff --git a/CMakeLists.txt b/CMakeLists.txt index a5755b8..9d5e789 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -485,6 +485,7 @@ if (WIBO_ENABLE_FIXTURE_TESTS) wibo_add_fixture_bin(NAME test_temppath SOURCES test/test_temppath.c) wibo_add_fixture_bin(NAME test_gettempfilename SOURCES test/test_gettempfilename.c) wibo_add_fixture_bin(NAME test_getprocaddress SOURCES test/test_getprocaddress.c) + wibo_add_fixture_bin(NAME test_getmodulehandleex SOURCES test/test_getmodulehandleex.c) wibo_add_fixture_bin(NAME test_tls SOURCES test/test_tls.c) wibo_add_fixture_bin(NAME test_tls_reloc SOURCES test/test_tls_reloc.c) wibo_add_fixture_bin(NAME test_handleapi SOURCES test/test_handleapi.c) diff --git a/dll/kernel32/libloaderapi.cpp b/dll/kernel32/libloaderapi.cpp index ea26ec2..08a94df 100644 --- a/dll/kernel32/libloaderapi.cpp +++ b/dll/kernel32/libloaderapi.cpp @@ -10,12 +10,20 @@ #include #include +#include #include #include #include namespace { +constexpr DWORD GET_MODULE_HANDLE_EX_FLAG_PIN = 0x00000001; +constexpr DWORD GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT = 0x00000002; +constexpr DWORD GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS = 0x00000004; +constexpr DWORD GET_MODULE_HANDLE_EX_VALID_FLAGS = GET_MODULE_HANDLE_EX_FLAG_PIN | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS; + HRSRC findResourceInternal(HMODULE hModule, const wibo::ResourceIdentifier &type, const wibo::ResourceIdentifier &name, std::optional language) { auto *exe = wibo::executableFromModule(hModule); @@ -74,6 +82,43 @@ HMODULE WINAPI GetModuleHandleW(LPCWSTR lpModuleName) { return GetModuleHandleA(nullptr); } +BOOL WINAPI GetModuleHandleExW(DWORD dwFlags, LPCWSTR lpModuleName, HMODULE *phModule) { + HOST_CONTEXT_GUARD(); + DEBUG_LOG("GetModuleHandleExW(%x, %p, %p)\n", dwFlags, lpModuleName, phModule); + if (!phModule) { + setLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + *phModule = NO_HANDLE; + if ((dwFlags & ~GET_MODULE_HANDLE_EX_VALID_FLAGS) != 0 || + ((dwFlags & GET_MODULE_HANDLE_EX_FLAG_PIN) && (dwFlags & GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT))) { + setLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + wibo::ModuleInfo *module = nullptr; + if (dwFlags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS) { + module = wibo::moduleInfoFromAddress(const_cast(reinterpret_cast(lpModuleName))); + } else if (lpModuleName) { + const auto lpModuleNameA = wideStringToString(lpModuleName); + module = wibo::findLoadedModule(lpModuleNameA.c_str()); + } else { + module = wibo::findLoadedModule(nullptr); + } + + if (!module) { + setLastError(ERROR_MOD_NOT_FOUND); + return FALSE; + } + if (dwFlags & GET_MODULE_HANDLE_EX_FLAG_PIN) { + module->refCount = UINT_MAX; + } else if (!(dwFlags & GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT) && module->refCount != UINT_MAX) { + module->refCount++; + } + *phModule = module->handle; + return TRUE; +} + DWORD WINAPI GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize) { HOST_CONTEXT_GUARD(); DEBUG_LOG("GetModuleFileNameA(%p, %p, %u)\n", hModule, lpFilename, nSize); diff --git a/dll/kernel32/libloaderapi.h b/dll/kernel32/libloaderapi.h index 7638de8..346127f 100644 --- a/dll/kernel32/libloaderapi.h +++ b/dll/kernel32/libloaderapi.h @@ -7,6 +7,7 @@ namespace kernel32 { BOOL WINAPI DisableThreadLibraryCalls(HMODULE hLibModule); HMODULE WINAPI GetModuleHandleA(LPCSTR lpModuleName); HMODULE WINAPI GetModuleHandleW(LPCWSTR lpModuleName); +BOOL WINAPI GetModuleHandleExW(DWORD dwFlags, LPCWSTR lpModuleName, HMODULE *phModule); DWORD WINAPI GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize); DWORD WINAPI GetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename, DWORD nSize); HRSRC WINAPI FindResourceA(HMODULE hModule, LPCSTR lpName, LPCSTR lpType); diff --git a/test/test_getmodulehandleex.c b/test/test_getmodulehandleex.c new file mode 100644 index 0000000..942b6f4 --- /dev/null +++ b/test/test_getmodulehandleex.c @@ -0,0 +1,54 @@ +#define _WIN32_WINNT 0x0501 +#define WIN32_LEAN_AND_MEAN +#include + +#include "test_assert.h" + +static int g_address_probe; + +int main(void) { + HMODULE processModule = GetModuleHandleW(NULL); + TEST_CHECK_MSG(processModule != NULL, "GetModuleHandleW(NULL) failed: %lu", (unsigned long)GetLastError()); + + HMODULE module = (HMODULE)(ULONG_PTR)0x12345678; + SetLastError(0xdeadbeef); + TEST_CHECK_MSG(GetModuleHandleExW(0, NULL, &module), "GetModuleHandleExW(NULL) failed: %lu", + (unsigned long)GetLastError()); + TEST_CHECK_EQ((ULONG_PTR)processModule, (ULONG_PTR)module); + + HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll"); + TEST_CHECK_MSG(kernel32 != NULL, "GetModuleHandleW(kernel32.dll) failed: %lu", (unsigned long)GetLastError()); + module = NULL; + TEST_CHECK_MSG(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, L"kernel32.dll", &module), + "GetModuleHandleExW(kernel32.dll) failed: %lu", (unsigned long)GetLastError()); + TEST_CHECK_EQ((ULONG_PTR)kernel32, (ULONG_PTR)module); + + module = NULL; + TEST_CHECK_MSG(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, L"kernel32.dll", &module), + "GetModuleHandleExW(PIN) failed: %lu", (unsigned long)GetLastError()); + TEST_CHECK_EQ((ULONG_PTR)kernel32, (ULONG_PTR)module); + + module = NULL; + TEST_CHECK_MSG(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)&g_address_probe, &module), + "GetModuleHandleExW(FROM_ADDRESS) failed: %lu", (unsigned long)GetLastError()); + TEST_CHECK_EQ((ULONG_PTR)processModule, (ULONG_PTR)module); + + module = (HMODULE)(ULONG_PTR)0x12345678; + SetLastError(0xdeadbeef); + TEST_CHECK(!GetModuleHandleExW(0, L"definitely_missing_wibo_test.dll", &module)); + TEST_CHECK_EQ(0, (ULONG_PTR)module); + TEST_CHECK_EQ(ERROR_MOD_NOT_FOUND, GetLastError()); + + SetLastError(0xdeadbeef); + TEST_CHECK(!GetModuleHandleExW(0, NULL, NULL)); + TEST_CHECK_EQ(ERROR_INVALID_PARAMETER, GetLastError()); + + module = (HMODULE)(ULONG_PTR)0x12345678; + SetLastError(0xdeadbeef); + TEST_CHECK(!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + L"kernel32.dll", &module)); + TEST_CHECK_EQ(0, (ULONG_PTR)module); + TEST_CHECK_EQ(ERROR_INVALID_PARAMETER, GetLastError()); + + return 0; +}