From 59bbeaca62c2981027099689178235feeafd49a7 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sun, 29 Mar 2026 19:58:36 -0700 Subject: [PATCH] fix: ::toupper/::tolower UB on signed char at 5 remaining call sites std::toupper(int) and std::tolower(int) have undefined behavior when passed a negative value. These sites passed raw signed char without casting to unsigned char first, unlike the rest of the codebase which already uses the correct pattern. Affects auth (account names), world packets, and mount sound path matching. --- src/audio/mount_sound_manager.cpp | 2 +- src/auth/auth_packets.cpp | 3 ++- src/auth/srp.cpp | 5 +++-- src/game/world_packets.cpp | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/audio/mount_sound_manager.cpp b/src/audio/mount_sound_manager.cpp index 0762ea26..92452161 100644 --- a/src/audio/mount_sound_manager.cpp +++ b/src/audio/mount_sound_manager.cpp @@ -495,7 +495,7 @@ MountType MountSoundManager::detectMountType(uint32_t creatureDisplayId) const { MountFamily MountSoundManager::detectMountFamilyFromPath(const std::string& modelPath) const { // Convert path to lowercase for matching std::string lower = modelPath; - for (char& c : lower) c = std::tolower(c); + for (char& c : lower) c = static_cast(std::tolower(static_cast(c))); // Check creature model path for family keywords if (lower.find("tallstrider") != std::string::npos || diff --git a/src/auth/auth_packets.cpp b/src/auth/auth_packets.cpp index 258490ac..7ffef253 100644 --- a/src/auth/auth_packets.cpp +++ b/src/auth/auth_packets.cpp @@ -57,7 +57,8 @@ bool detectOutboundIPv4(std::array& outIp) { network::Packet LogonChallengePacket::build(const std::string& account, const ClientInfo& info) { // Convert account to uppercase std::string upperAccount = account; - std::transform(upperAccount.begin(), upperAccount.end(), upperAccount.begin(), ::toupper); + std::transform(upperAccount.begin(), upperAccount.end(), upperAccount.begin(), + [](unsigned char c) { return static_cast(std::toupper(c)); }); // Calculate payload size (everything after cmd + error + size) // game(4) + version(3) + build(2) + platform(4) + os(4) + locale(4) + diff --git a/src/auth/srp.cpp b/src/auth/srp.cpp index 6d741920..154c4f55 100644 --- a/src/auth/srp.cpp +++ b/src/auth/srp.cpp @@ -117,8 +117,9 @@ std::vector SRP::computeAuthHash(const std::string& username, // Convert to uppercase (WoW requirement) std::string upperUser = username; std::string upperPass = password; - std::transform(upperUser.begin(), upperUser.end(), upperUser.begin(), ::toupper); - std::transform(upperPass.begin(), upperPass.end(), upperPass.begin(), ::toupper); + auto toUpper = [](unsigned char c) { return static_cast(std::toupper(c)); }; + std::transform(upperUser.begin(), upperUser.end(), upperUser.begin(), toUpper); + std::transform(upperPass.begin(), upperPass.end(), upperPass.begin(), toUpper); // H(I:P) std::string combined = upperUser + ":" + upperPass; diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index a1012f42..7297298d 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -80,7 +80,7 @@ network::Packet AuthSessionPacket::build(uint32_t build, // Convert account name to uppercase std::string upperAccount = accountName; std::transform(upperAccount.begin(), upperAccount.end(), - upperAccount.begin(), ::toupper); + upperAccount.begin(), [](unsigned char c) { return static_cast(std::toupper(c)); }); LOG_INFO("Building CMSG_AUTH_SESSION for account: ", upperAccount);