]> repos.lx-s.de Git - winmute.git/commitdiff
Fixed wifi detector memory leak.
authorlx-s <lx-s@users.noreply.github.com>
Wed, 8 Jul 2026 07:35:15 +0000 (09:35 +0200)
committerlx-s <lx-s@users.noreply.github.com>
Wed, 8 Jul 2026 07:35:15 +0000 (09:35 +0200)
WinMute/Utility.cpp
WinMute/Utility.h
WinMute/WiFiDetector.h

index 9e8c8ccb8a094bccbfb3417e80b22d4002200113..eac3f6149fb3396db9eba1d674ceeb064a6bfceb 100644 (file)
@@ -70,12 +70,11 @@ void ShowWindowsError(const wchar_t *functionName, DWORD lastError)
    }
    // Display the error message and exit the process
    const std::wstring errorMsgText{ reinterpret_cast<wchar_t *>(lpMsgBuf) };
-   const std::wstring errorMsg = std::vformat(
+   const std::wstring errorMsg = SafeVFormat(
       WMi18n::GetInstance().GetTranslationW("general.error.winapi.text"),
-      std::make_wformat_args(
-         functionName,
-         lastError,
-         errorMsgText));
+      functionName,
+      lastError,
+      errorMsgText);
    TaskDialog(
       nullptr, nullptr, PROGRAM_NAME, errorMsg.c_str(),
       nullptr, TDCBF_OK_BUTTON, TD_ERROR_ICON, nullptr);
@@ -156,14 +155,15 @@ std::optional<std::wstring> GetAudioDeviceName(
 
 std::wstring ConvertStringToWideString(const std::string &ansiString)
 {
+   // For CP_UTF8 the only valid flags are 0 and MB_ERR_INVALID_CHARS.
    std::wstring unicodeString;
-   auto wideCharSize = MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, ansiString.c_str(), -1, nullptr, 0);
+   auto wideCharSize = MultiByteToWideChar(CP_UTF8, 0, ansiString.c_str(), -1, nullptr, 0);
    if (wideCharSize == 0) {
       return L"";
    }
    unicodeString.reserve(wideCharSize);
    unicodeString.resize(wideCharSize - 1);
-   wideCharSize = MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, ansiString.c_str(), -1, &unicodeString[0], wideCharSize);
+   wideCharSize = MultiByteToWideChar(CP_UTF8, 0, ansiString.c_str(), -1, &unicodeString[0], wideCharSize);
    return unicodeString;
 }
 
@@ -186,6 +186,21 @@ std::string ConvertWideStringToString(const std::wstring &wideString)
 
 bool LaunchBrowser(HWND hParent, const std::wstring &url)
 {
+   // Only hand real web URLs to the shell. Anything else (local file paths,
+   // UNC shares, other protocol handlers) would be executed/opened directly
+   // by ShellExecute, which is dangerous for URLs that come from the
+   // update file or other external sources.
+   auto hasPrefix = [&url](const wchar_t *prefix) {
+      const size_t prefixLen = wcslen(prefix);
+      return url.length() > prefixLen
+          && _wcsnicmp(url.c_str(), prefix, prefixLen) == 0;
+   };
+   if (!hasPrefix(L"https://") && !hasPrefix(L"http://")) {
+      WMLog::GetInstance().LogError(
+         L"Refused to open \"%s\": not a http(s) URL", url.c_str());
+      return false;
+   }
+
    SHELLEXECUTEINFOW sxi = { 0 };
    sxi.cbSize = sizeof(sxi);
    sxi.nShow = SW_SHOWNORMAL;
index 3fad5a40eb4414ce7661ebf32f21ef20ac7d566a..e417030f347973eaf2fde72b77499379c703182a 100644 (file)
@@ -33,7 +33,9 @@ POSSIBILITY OF SUCH DAMAGE.
 
 #pragma once
 
+#include <format>
 #include <string>
+#include <string_view>
 #include <optional>
 
 #include <atlbase.h>
@@ -43,6 +45,19 @@ POSSIBILITY OF SUCH DAMAGE.
 // =============================================================================
 // Utility
 
+// Formats a runtime format string (e.g. from a translation file) without
+// letting a malformed placeholder terminate the process: on std::format_error
+// the unformatted string is returned instead.
+template<typename... Args>
+std::wstring SafeVFormat(std::wstring_view fmt, Args&&... args)
+{
+   try {
+      return std::vformat(fmt, std::make_wformat_args(args...));
+   } catch (const std::format_error &) {
+      return std::wstring{ fmt };
+   }
+}
+
 void ShowWindowsError(const wchar_t *functionName, DWORD lastError = -1);
 bool GetWinMuteVersion(std::wstring &versNumber);
 
index 25d70e43d1258e7f76c7ed58fd4b6a70a09fbed4..fd1b43a4c7ecdacb2f2b523510c1c42c80cd1b2e 100644 (file)
@@ -35,7 +35,9 @@ POSSIBILITY OF SUCH DAMAGE.
 
 #include "common.h"
 
-/* wParam = Connected = 1 | Disconnected = 0. lParam = Pointer to Wifi Name */
+/* wParam = Connected = 1 | Disconnected = 0. lParam = Pointer to Wifi Name.
+   Sent synchronously; the name is only valid for the duration of the call
+   and is owned by the sender. */
 constexpr int WM_WIFISTATUSCHANGED = WM_USER + 400;
 
 class WifiDetector {
@@ -55,6 +57,8 @@ public:
    void WlanNotificationCallback(PWLAN_NOTIFICATION_DATA notifyData);
 
 private:
+   bool IsNetworkRelevant(const wchar_t *profileName) const;
+
    HWND hNotifyWnd_;
    HANDLE wlanHandle_;
    // If "true" then networks_ contains all networks where the workstation should
@@ -63,4 +67,7 @@ private:
    bool isMuteList_;
    bool initialized_;
    std::vector<std::wstring> networks_;
+   // WlanNotificationCallback is invoked on a WLAN service thread, while
+   // SetNetworkList is called from the UI thread.
+   mutable std::mutex networksMutex_;
 };