From f74797ae0c60fad63cf6bd21721bbd9dbf78a520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Fri, 28 Oct 2022 00:35:41 +0200 Subject: [PATCH 1/6] Use assertions in `CWindowsConsoleLogger` instead of error messages The function `MultiByteToWideChar` can't fail unless wrong arguments are passed, when the buffer size is set correctly by calling the function twice. The returned buffer size on the second call should always match the buffer size determined with the first call. --- src/base/log.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/base/log.cpp b/src/base/log.cpp index 633aad75c..dbe46911a 100644 --- a/src/base/log.cpp +++ b/src/base/log.cpp @@ -339,21 +339,9 @@ public: void Log(const CLogMessage *pMessage) override { int WLen = MultiByteToWideChar(CP_UTF8, 0, pMessage->m_aLine, pMessage->m_LineLength, NULL, 0); - if(!WLen) - { - WCHAR aError[] = L"Failed to obtain length of log message\r\n"; - WriteConsoleW(m_pConsole, aError, std::size(aError) - 1, NULL, NULL); - return; - } + dbg_assert(WLen > 0, "MultiByteToWideChar failure"); WCHAR *pWide = (WCHAR *)malloc((WLen + 2) * sizeof(*pWide)); - WLen = MultiByteToWideChar(CP_UTF8, 0, pMessage->m_aLine, pMessage->m_LineLength, pWide, WLen); - if(!WLen) - { - WCHAR aError[] = L"Failed to convert log message encoding\r\n"; - WriteConsoleW(m_pConsole, aError, std::size(aError) - 1, NULL, NULL); - free(pWide); - return; - } + dbg_assert(MultiByteToWideChar(CP_UTF8, 0, pMessage->m_aLine, pMessage->m_LineLength, pWide, WLen) == WLen, "MultiByteToWideChar failure"); pWide[WLen++] = '\r'; pWide[WLen++] = '\n'; From 72991fbe9a63fa5215ce498cdcdce737de78e59c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Fri, 28 Oct 2022 00:21:26 +0200 Subject: [PATCH 2/6] Use dynamic buffer size in `CLoggerWindowsDebugger` Make sure the Windows debugger logger does not fail when log messages are too long. The first call to `MultiByteToWideChar` gets the required size of the wide-char buffer, which should be at least one for the null termination. The second call is expected to produce exactly the same number of characters. --- src/base/log.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/base/log.cpp b/src/base/log.cpp index dbe46911a..1cd0ce30a 100644 --- a/src/base/log.cpp +++ b/src/base/log.cpp @@ -423,9 +423,12 @@ class CLoggerWindowsDebugger : public ILogger public: void Log(const CLogMessage *pMessage) override { - WCHAR aWBuffer[4096]; - MultiByteToWideChar(CP_UTF8, 0, pMessage->m_aLine, -1, aWBuffer, sizeof(aWBuffer) / sizeof(WCHAR)); - OutputDebugStringW(aWBuffer); + int WLen = MultiByteToWideChar(CP_UTF8, 0, pMessage->m_aLine, -1, NULL, 0); + dbg_assert(WLen > 0, "MultiByteToWideChar failure"); + WCHAR *pWide = (WCHAR *)malloc(WLen * sizeof(*pWide)); + dbg_assert(MultiByteToWideChar(CP_UTF8, 0, pMessage->m_aLine, -1, pWide, WLen) == WLen, "MultiByteToWideChar failure"); + OutputDebugStringW(pWide); + free(pWide); } }; std::unique_ptr log_logger_windows_debugger() From a1c1a8f9728c8d73150cf620d50fd7f04c3b54f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Fri, 28 Oct 2022 00:53:06 +0200 Subject: [PATCH 3/6] Add assertions for all `MultiByteToWideChar` usages in system Converting multi-byte (UTF-8) to wide-char (UTF-16) take 1-4 bytes from the input and transforms them to 1-2 wide-chars, so having `char` and `WCHAR` buffers with equal lengths should mean that this function can't fail due to insufficient buffer sizes, unless a path that's already too long for Windows is being used internally. Other errors are also not expected, as those would indicate programming errors like wrong arguments being passed. The maximum length for paths in the Win32 API is 260 characters. This limitation can be lifted in Windows 10, but it must be done explicitly (opt-in) both in the Windows Registry by the user and in the application's manifest by us. References: - https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar - https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry --- src/base/system.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/base/system.cpp b/src/base/system.cpp index 331d02f08..d7d7e84e8 100644 --- a/src/base/system.cpp +++ b/src/base/system.cpp @@ -222,7 +222,7 @@ IOHANDLE io_open_impl(const char *filename, int flags) dbg_assert(flags == (IOFLAG_READ | IOFLAG_SKIP_BOM) || flags == IOFLAG_READ || flags == IOFLAG_WRITE || flags == IOFLAG_APPEND, "flags must be read, read+skipbom, write or append"); #if defined(CONF_FAMILY_WINDOWS) WCHAR wBuffer[IO_MAX_PATH_LENGTH]; - MultiByteToWideChar(CP_UTF8, 0, filename, -1, wBuffer, std::size(wBuffer)); + dbg_assert(MultiByteToWideChar(CP_UTF8, 0, filename, -1, wBuffer, std::size(wBuffer)) > 0, "MultiByteToWideChar failure"); if((flags & IOFLAG_READ) != 0) return (IOHANDLE)_wfsopen(wBuffer, L"rb", _SH_DENYNO); if(flags == IOFLAG_WRITE) @@ -2102,7 +2102,7 @@ void fs_listdir(const char *dir, FS_LISTDIR_CALLBACK cb, int type, void *user) int length; str_format(buffer, sizeof(buffer), "%s/*", dir); - MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, std::size(wBuffer)); + dbg_assert(MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, std::size(wBuffer)) > 0, "MultiByteToWideChar failure"); handle = FindFirstFileW(wBuffer, &finddata); if(handle == INVALID_HANDLE_VALUE) @@ -2156,7 +2156,7 @@ void fs_listdir_fileinfo(const char *dir, FS_LISTDIR_CALLBACK_FILEINFO cb, int t int length; str_format(buffer, sizeof(buffer), "%s/*", dir); - MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, std::size(wBuffer)); + dbg_assert(MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, std::size(wBuffer)) > 0, "MultiByteToWideChar failure"); handle = FindFirstFileW(wBuffer, &finddata); if(handle == INVALID_HANDLE_VALUE) @@ -2280,7 +2280,7 @@ int fs_makedir(const char *path) { #if defined(CONF_FAMILY_WINDOWS) WCHAR wBuffer[IO_MAX_PATH_LENGTH]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, wBuffer, std::size(wBuffer)); + dbg_assert(MultiByteToWideChar(CP_UTF8, 0, path, -1, wBuffer, std::size(wBuffer)) > 0, "MultiByteToWideChar failure"); if(CreateDirectoryW(wBuffer, NULL) != 0) return 0; if(GetLastError() == ERROR_ALREADY_EXISTS) @@ -2304,7 +2304,7 @@ int fs_removedir(const char *path) { #if defined(CONF_FAMILY_WINDOWS) WCHAR wPath[IO_MAX_PATH_LENGTH]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, std::size(wPath)); + dbg_assert(MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, std::size(wPath)) > 0, "MultiByteToWideChar failure"); if(RemoveDirectoryW(wPath) != 0) return 0; return -1; @@ -2319,7 +2319,7 @@ int fs_is_dir(const char *path) { #if defined(CONF_FAMILY_WINDOWS) WCHAR wPath[IO_MAX_PATH_LENGTH]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, std::size(wPath)); + dbg_assert(MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, std::size(wPath)) > 0, "MultiByteToWideChar failure"); DWORD attributes = GetFileAttributesW(wPath); return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0; #else @@ -2334,7 +2334,7 @@ int fs_is_relative_path(const char *path) { #if defined(CONF_FAMILY_WINDOWS) WCHAR wPath[IO_MAX_PATH_LENGTH]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, std::size(wPath)); + dbg_assert(MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, std::size(wPath)) > 0, "MultiByteToWideChar failure"); return PathIsRelativeW(wPath) ? 1 : 0; #else return path[0] == '/' ? 0 : 1; // yes, it's that simple @@ -2347,7 +2347,7 @@ int fs_chdir(const char *path) { #if defined(CONF_FAMILY_WINDOWS) WCHAR wBuffer[IO_MAX_PATH_LENGTH]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, wBuffer, std::size(wBuffer)); + dbg_assert(MultiByteToWideChar(CP_UTF8, 0, path, -1, wBuffer, std::size(wBuffer)) > 0, "MultiByteToWideChar failure"); return SetCurrentDirectoryW(wBuffer) != 0 ? 0 : 1; #else if(chdir(path)) @@ -2395,7 +2395,7 @@ int fs_remove(const char *filename) { #if defined(CONF_FAMILY_WINDOWS) WCHAR wFilename[IO_MAX_PATH_LENGTH]; - MultiByteToWideChar(CP_UTF8, 0, filename, -1, wFilename, std::size(wFilename)); + dbg_assert(MultiByteToWideChar(CP_UTF8, 0, filename, -1, wFilename, std::size(wFilename)) > 0, "MultiByteToWideChar failure"); return DeleteFileW(wFilename) == 0; #else return unlink(filename) != 0; @@ -2407,8 +2407,8 @@ int fs_rename(const char *oldname, const char *newname) #if defined(CONF_FAMILY_WINDOWS) WCHAR wOldname[IO_MAX_PATH_LENGTH]; WCHAR wNewname[IO_MAX_PATH_LENGTH]; - MultiByteToWideChar(CP_UTF8, 0, oldname, -1, wOldname, std::size(wOldname)); - MultiByteToWideChar(CP_UTF8, 0, newname, -1, wNewname, std::size(wNewname)); + dbg_assert(MultiByteToWideChar(CP_UTF8, 0, oldname, -1, wOldname, std::size(wOldname)) > 0, "MultiByteToWideChar failure"); + dbg_assert(MultiByteToWideChar(CP_UTF8, 0, newname, -1, wNewname, std::size(wNewname)) > 0, "MultiByteToWideChar failure"); if(MoveFileExW(wOldname, wNewname, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) == 0) return 1; #else @@ -2425,7 +2425,7 @@ int fs_file_time(const char *name, time_t *created, time_t *modified) HANDLE handle; WCHAR wBuffer[IO_MAX_PATH_LENGTH]; - MultiByteToWideChar(CP_UTF8, 0, name, -1, wBuffer, std::size(wBuffer)); + dbg_assert(MultiByteToWideChar(CP_UTF8, 0, name, -1, wBuffer, std::size(wBuffer)) > 0, "MultiByteToWideChar failure"); handle = FindFirstFileW(wBuffer, &finddata); if(handle == INVALID_HANDLE_VALUE) return 1; @@ -3861,8 +3861,8 @@ void cmdline_free(int argc, const char **argv) PROCESS shell_execute(const char *file) { #if defined(CONF_FAMILY_WINDOWS) - WCHAR wBuffer[512]; - MultiByteToWideChar(CP_UTF8, 0, file, -1, wBuffer, std::size(wBuffer)); + WCHAR wBuffer[IO_MAX_PATH_LENGTH]; + dbg_assert(MultiByteToWideChar(CP_UTF8, 0, file, -1, wBuffer, std::size(wBuffer)) > 0, "MultiByteToWideChar failure"); SHELLEXECUTEINFOW info; mem_zero(&info, sizeof(SHELLEXECUTEINFOW)); info.cbSize = sizeof(SHELLEXECUTEINFOW); @@ -3910,8 +3910,8 @@ int kill_process(PROCESS process) int open_link(const char *link) { #if defined(CONF_FAMILY_WINDOWS) - WCHAR wBuffer[512]; - MultiByteToWideChar(CP_UTF8, 0, link, -1, wBuffer, std::size(wBuffer)); + WCHAR wBuffer[IO_MAX_PATH_LENGTH]; + dbg_assert(MultiByteToWideChar(CP_UTF8, 0, link, -1, wBuffer, std::size(wBuffer)) > 0, "MultiByteToWideChar failure"); SHELLEXECUTEINFOW info; mem_zero(&info, sizeof(SHELLEXECUTEINFOW)); info.cbSize = sizeof(SHELLEXECUTEINFOW); @@ -4236,7 +4236,7 @@ void set_exception_handler_log_file(const char *log_file_path) if(exception_handling_module != nullptr) { WCHAR wBuffer[IO_MAX_PATH_LENGTH]; - MultiByteToWideChar(CP_UTF8, 0, log_file_path, -1, wBuffer, std::size(wBuffer)); + dbg_assert(MultiByteToWideChar(CP_UTF8, 0, log_file_path, -1, wBuffer, std::size(wBuffer)) > 0, "MultiByteToWideChar failure"); // Intentional #ifdef __MINGW32__ #pragma GCC diagnostic push From 8bde109acbeb005d3a33599325da100b62f79410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 29 Oct 2022 14:18:21 +0200 Subject: [PATCH 4/6] Use dynamic size for formatted Windows system messages Extract `windows_format_system_message` and allocate appropriate buffer for the formatted system error messages, so messages of any length can be displayed. The flag `FORMAT_MESSAGE_ALLOCATE_BUFFER` is used so `FormatMessageW` allocates the buffer which must later be freed with `LocalFree`. The flag `FORMAT_MESSAGE_MAX_WIDTH_MASK` is also added so the formatted message will not contain any line breaks at the end, which would make log messages less readable. Reference: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessagew --- src/base/system.cpp | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/base/system.cpp b/src/base/system.cpp index d7d7e84e8..b4576c4b2 100644 --- a/src/base/system.cpp +++ b/src/base/system.cpp @@ -1430,6 +1430,24 @@ static int priv_net_close_all_sockets(NETSOCKET sock) return 0; } +#if defined(CONF_FAMILY_WINDOWS) +static char *windows_format_system_message(int error) +{ + WCHAR *wide_message; + const DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK; + if(FormatMessageW(flags, NULL, error, 0, (LPWSTR)&wide_message, 0, NULL) == 0) + { + return nullptr; + } + int len = WideCharToMultiByte(CP_UTF8, 0, wide_message, -1, NULL, 0, NULL, NULL); + dbg_assert(len > 0, "WideCharToMultiByte failure"); + char *message = (char *)malloc(len * sizeof(*message)); + dbg_assert(WideCharToMultiByte(CP_UTF8, 0, wide_message, -1, message, len, NULL, NULL) == len, "WideCharToMultiByte failure"); + LocalFree(wide_message); + return message; +} +#endif + static int priv_net_create_socket(int domain, int type, struct sockaddr *addr, int sockaddrlen) { int sock, e; @@ -1439,13 +1457,10 @@ static int priv_net_create_socket(int domain, int type, struct sockaddr *addr, i if(sock < 0) { #if defined(CONF_FAMILY_WINDOWS) - char buf[128]; - WCHAR wBuffer[128]; int error = WSAGetLastError(); - if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, error, 0, wBuffer, std::size(wBuffer), 0) == 0) - wBuffer[0] = 0; - WideCharToMultiByte(CP_UTF8, 0, wBuffer, -1, buf, sizeof(buf), NULL, NULL); - dbg_msg("net", "failed to create socket with domain %d and type %d (%d '%s')", domain, type, error, buf); + char *message = windows_format_system_message(error); + dbg_msg("net", "failed to create socket with domain %d and type %d (%d '%s')", domain, type, error, message == nullptr ? "unknown error" : message); + free(message); #else dbg_msg("net", "failed to create socket with domain %d and type %d (%d '%s')", domain, type, errno, strerror(errno)); #endif @@ -1478,13 +1493,10 @@ static int priv_net_create_socket(int domain, int type, struct sockaddr *addr, i if(e != 0) { #if defined(CONF_FAMILY_WINDOWS) - char buf[128]; - WCHAR wBuffer[128]; int error = WSAGetLastError(); - if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, error, 0, wBuffer, std::size(wBuffer), 0) == 0) - wBuffer[0] = 0; - WideCharToMultiByte(CP_UTF8, 0, wBuffer, -1, buf, sizeof(buf), NULL, NULL); - dbg_msg("net", "failed to bind socket with domain %d and type %d (%d '%s')", domain, type, error, buf); + char *message = windows_format_system_message(error); + dbg_msg("net", "failed to bind socket with domain %d and type %d (%d '%s')", domain, type, error, message == nullptr ? "unknown error" : message); + free(message); #else dbg_msg("net", "failed to bind socket with domain %d and type %d (%d '%s')", domain, type, errno, strerror(errno)); #endif From 6c30fb6f5a03b56409c4875293aead70106174f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 29 Oct 2022 18:11:33 +0200 Subject: [PATCH 5/6] Remove duplicate buffer from `fs_listdir` functions The second buffer was previously used when calling `fs_is_dir` explicity, but it has become from obsolete from #4657 by using the flag `FILE_ATTRIBUTE_DIRECTORY` instead. --- src/base/system.cpp | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/base/system.cpp b/src/base/system.cpp index b4576c4b2..ffcfa763d 100644 --- a/src/base/system.cpp +++ b/src/base/system.cpp @@ -2109,9 +2109,7 @@ void fs_listdir(const char *dir, FS_LISTDIR_CALLBACK cb, int type, void *user) WIN32_FIND_DATAW finddata; HANDLE handle; char buffer[IO_MAX_PATH_LENGTH]; - char buffer2[IO_MAX_PATH_LENGTH]; WCHAR wBuffer[IO_MAX_PATH_LENGTH]; - int length; str_format(buffer, sizeof(buffer), "%s/*", dir); dbg_assert(MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, std::size(wBuffer)) > 0, "MultiByteToWideChar failure"); @@ -2120,15 +2118,11 @@ void fs_listdir(const char *dir, FS_LISTDIR_CALLBACK cb, int type, void *user) if(handle == INVALID_HANDLE_VALUE) return; - str_format(buffer, sizeof(buffer), "%s/", dir); - length = str_length(buffer); - /* add all the entries */ do { - WideCharToMultiByte(CP_UTF8, 0, finddata.cFileName, -1, buffer2, sizeof(buffer2), NULL, NULL); - str_copy(buffer + length, buffer2, (int)sizeof(buffer) - length); - if(cb(buffer2, (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0, type, user)) + WideCharToMultiByte(CP_UTF8, 0, finddata.cFileName, -1, buffer, sizeof(buffer), NULL, NULL); + if(cb(buffer, (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0, type, user)) break; } while(FindNextFileW(handle, &finddata)); @@ -2163,9 +2157,7 @@ void fs_listdir_fileinfo(const char *dir, FS_LISTDIR_CALLBACK_FILEINFO cb, int t WIN32_FIND_DATAW finddata; HANDLE handle; char buffer[IO_MAX_PATH_LENGTH]; - char buffer2[IO_MAX_PATH_LENGTH]; WCHAR wBuffer[IO_MAX_PATH_LENGTH]; - int length; str_format(buffer, sizeof(buffer), "%s/*", dir); dbg_assert(MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, std::size(wBuffer)) > 0, "MultiByteToWideChar failure"); @@ -2174,17 +2166,13 @@ void fs_listdir_fileinfo(const char *dir, FS_LISTDIR_CALLBACK_FILEINFO cb, int t if(handle == INVALID_HANDLE_VALUE) return; - str_format(buffer, sizeof(buffer), "%s/", dir); - length = str_length(buffer); - /* add all the entries */ do { - WideCharToMultiByte(CP_UTF8, 0, finddata.cFileName, -1, buffer2, sizeof(buffer2), NULL, NULL); - str_copy(buffer + length, buffer2, (int)sizeof(buffer) - length); + WideCharToMultiByte(CP_UTF8, 0, finddata.cFileName, -1, buffer, sizeof(buffer), NULL, NULL); CFsFileInfo info; - info.m_pName = buffer2; + info.m_pName = buffer; info.m_TimeCreated = filetime_to_unixtime(&finddata.ftCreationTime); info.m_TimeModified = filetime_to_unixtime(&finddata.ftLastWriteTime); From 6501ae9b9d4498908f68ed96e381c3e734889367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 29 Oct 2022 20:44:52 +0200 Subject: [PATCH 6/6] Add checks for remaining `WideCharToMultiByte` usages in system Converting wide-char (UTF-16) to multi-byte (UTF-8) takes 1-2 wide-chars from the input and transforms them to 1-4 bytes, so having `char` and `WCHAR` buffers with equal static lengths means that this function can fail due to insufficient buffer sizes when the user has folder or files with very long names (many unicode codepoints). Therefore checks are added that allow only the error `ERROR_INSUFFICIENT_BUFFER` when `WideCharToMultiByte` fails, which is expected on very long paths, as these would also lead to further errors later in the code. The respective functions will now fail with a return and ignore files that have too long names. This could only completely be fixed using dynamically sized buffers for all paths, which seems like too much work and overhead. Other errors are not expected and hence caught by the assertions, as those would indicate programming errors like wrong arguments being passed. Reference: https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte --- src/base/system.cpp | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/base/system.cpp b/src/base/system.cpp index ffcfa763d..d1514d6d9 100644 --- a/src/base/system.cpp +++ b/src/base/system.cpp @@ -95,12 +95,13 @@ IOHANDLE io_current_exe() #if defined(CONF_FAMILY_WINDOWS) wchar_t wpath[IO_MAX_PATH_LENGTH]; char path[IO_MAX_PATH_LENGTH]; - if(!GetModuleFileNameW(NULL, wpath, std::size(wpath))) + if(GetModuleFileNameW(NULL, wpath, std::size(wpath)) == 0 || GetLastError() != ERROR_SUCCESS) { return 0; } - if(!WideCharToMultiByte(CP_UTF8, 0, wpath, -1, path, sizeof(path), NULL, NULL)) + if(WideCharToMultiByte(CP_UTF8, 0, wpath, -1, path, sizeof(path), NULL, NULL) == 0) { + dbg_assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "WideCharToMultiByte failure"); return 0; } return io_open(path, IOFLAG_READ); @@ -2121,7 +2122,11 @@ void fs_listdir(const char *dir, FS_LISTDIR_CALLBACK cb, int type, void *user) /* add all the entries */ do { - WideCharToMultiByte(CP_UTF8, 0, finddata.cFileName, -1, buffer, sizeof(buffer), NULL, NULL); + if(WideCharToMultiByte(CP_UTF8, 0, finddata.cFileName, -1, buffer, sizeof(buffer), NULL, NULL) == 0) + { + dbg_assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "WideCharToMultiByte failure"); + continue; + } if(cb(buffer, (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0, type, user)) break; } while(FindNextFileW(handle, &finddata)); @@ -2169,7 +2174,11 @@ void fs_listdir_fileinfo(const char *dir, FS_LISTDIR_CALLBACK_FILEINFO cb, int t /* add all the entries */ do { - WideCharToMultiByte(CP_UTF8, 0, finddata.cFileName, -1, buffer, sizeof(buffer), NULL, NULL); + if(WideCharToMultiByte(CP_UTF8, 0, finddata.cFileName, -1, buffer, sizeof(buffer), NULL, NULL) == 0) + { + dbg_assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "WideCharToMultiByte failure"); + continue; + } CFsFileInfo info; info.m_pName = buffer; @@ -2221,7 +2230,11 @@ int fs_storage_path(const char *appname, char *path, int max) if(!home) return -1; char buffer[IO_MAX_PATH_LENGTH]; - WideCharToMultiByte(CP_UTF8, 0, home, -1, buffer, sizeof(buffer), NULL, NULL); + if(WideCharToMultiByte(CP_UTF8, 0, home, -1, buffer, sizeof(buffer), NULL, NULL) == 0) + { + dbg_assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "WideCharToMultiByte failure"); + return -1; + } str_format(path, max, "%s/%s", buffer, appname); return 0; #elif defined(CONF_PLATFORM_ANDROID) @@ -2366,8 +2379,12 @@ char *fs_getcwd(char *buffer, int buffer_size) WCHAR wBuffer[IO_MAX_PATH_LENGTH]; DWORD result = GetCurrentDirectoryW(std::size(wBuffer), wBuffer); if(result == 0 || result > std::size(wBuffer)) - return 0; - WideCharToMultiByte(CP_UTF8, 0, wBuffer, -1, buffer, buffer_size, NULL, NULL); + return nullptr; + if(WideCharToMultiByte(CP_UTF8, 0, wBuffer, -1, buffer, buffer_size, NULL, NULL) == 0) + { + dbg_assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "WideCharToMultiByte failure"); + return nullptr; + } return buffer; #else return getcwd(buffer, buffer_size); @@ -3958,7 +3975,8 @@ int open_file(const char *path) char workingDir[IO_MAX_PATH_LENGTH]; if(fs_is_relative_path(path)) { - fs_getcwd(workingDir, sizeof(workingDir)); + if(!fs_getcwd(workingDir, sizeof(workingDir))) + return 0; str_append(workingDir, "/", sizeof(workingDir)); } else