mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-27 05:23:51 +00:00
memory, threading, network hardening
Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
This commit is contained in:
parent
312994be83
commit
2e8856bacd
9 changed files with 135 additions and 24 deletions
|
|
@ -10,6 +10,7 @@
|
|||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace wowee {
|
||||
|
|
@ -26,6 +27,10 @@ struct DecodedWavCacheEntry {
|
|||
};
|
||||
|
||||
static std::unordered_map<uint64_t, DecodedWavCacheEntry> gDecodedWavCache;
|
||||
// Protects gDecodedWavCache — shared_lock for reads, unique_lock for writes.
|
||||
// Required because playSound2D() can be called from multiple threads
|
||||
// (main thread, async loaders, animation callbacks).
|
||||
static std::shared_mutex gDecodedWavCacheMutex;
|
||||
|
||||
static uint64_t makeWavCacheKey(const std::vector<uint8_t>& wavData) {
|
||||
// FNV-1a over the first 256 bytes + last 256 bytes + total size.
|
||||
|
|
@ -53,9 +58,14 @@ static bool decodeWavCached(const std::vector<uint8_t>& wavData, DecodedWavCache
|
|||
if (wavData.empty()) return false;
|
||||
|
||||
const uint64_t key = makeWavCacheKey(wavData);
|
||||
if (auto it = gDecodedWavCache.find(key); it != gDecodedWavCache.end()) {
|
||||
out = it->second;
|
||||
return true;
|
||||
|
||||
// Fast path: shared (read) lock for cache hits — allows concurrent lookups.
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> readLock(gDecodedWavCacheMutex);
|
||||
if (auto it = gDecodedWavCache.find(key); it != gDecodedWavCache.end()) {
|
||||
out = it->second;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ma_decoder decoder;
|
||||
|
|
@ -102,13 +112,22 @@ static bool decodeWavCached(const std::vector<uint8_t>& wavData, DecodedWavCache
|
|||
// Evict oldest half when cache grows too large. 256 entries ≈ 50-100 MB of decoded
|
||||
// PCM data depending on file lengths; halving keeps memory bounded while retaining
|
||||
// recently-heard sounds (footsteps, UI clicks, combat hits) for instant replay.
|
||||
constexpr size_t kMaxCachedSounds = 256;
|
||||
if (gDecodedWavCache.size() >= kMaxCachedSounds) {
|
||||
auto it = gDecodedWavCache.begin();
|
||||
std::advance(it, gDecodedWavCache.size() / 2);
|
||||
gDecodedWavCache.erase(gDecodedWavCache.begin(), it);
|
||||
// Exclusive (write) lock — only one thread can evict + insert.
|
||||
{
|
||||
std::lock_guard<std::shared_mutex> writeLock(gDecodedWavCacheMutex);
|
||||
// Re-check in case another thread inserted while we were decoding.
|
||||
if (auto it = gDecodedWavCache.find(key); it != gDecodedWavCache.end()) {
|
||||
out = it->second;
|
||||
return true;
|
||||
}
|
||||
constexpr size_t kMaxCachedSounds = 256;
|
||||
if (gDecodedWavCache.size() >= kMaxCachedSounds) {
|
||||
auto it = gDecodedWavCache.begin();
|
||||
std::advance(it, gDecodedWavCache.size() / 2);
|
||||
gDecodedWavCache.erase(gDecodedWavCache.begin(), it);
|
||||
}
|
||||
gDecodedWavCache.emplace(key, entry);
|
||||
}
|
||||
gDecodedWavCache.emplace(key, entry);
|
||||
out = entry;
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,26 @@ void AuthHandler::disconnect() {
|
|||
socket->disconnect();
|
||||
socket.reset();
|
||||
}
|
||||
|
||||
// Scrub sensitive material when tearing down the auth session.
|
||||
if (!password.empty()) {
|
||||
volatile char* p = const_cast<volatile char*>(password.data());
|
||||
for (size_t i = 0; i < password.size(); ++i)
|
||||
p[i] = '\0';
|
||||
password.clear();
|
||||
password.shrink_to_fit();
|
||||
}
|
||||
if (!sessionKey.empty()) {
|
||||
volatile uint8_t* k = const_cast<volatile uint8_t*>(sessionKey.data());
|
||||
for (size_t i = 0; i < sessionKey.size(); ++i)
|
||||
k[i] = 0;
|
||||
sessionKey.clear();
|
||||
sessionKey.shrink_to_fit();
|
||||
}
|
||||
if (srp) {
|
||||
srp->clearCredentials();
|
||||
}
|
||||
|
||||
setState(AuthState::DISCONNECTED);
|
||||
LOG_INFO("Disconnected from auth server");
|
||||
}
|
||||
|
|
@ -354,6 +374,16 @@ void AuthHandler::handleLogonProofResponse(network::Packet& packet) {
|
|||
sessionKey = srp->getSessionKey();
|
||||
setState(AuthState::AUTHENTICATED);
|
||||
|
||||
// Plaintext password is no longer needed — zero-fill and release it so it
|
||||
// doesn't sit in process memory for the rest of the session.
|
||||
if (!password.empty()) {
|
||||
volatile char* p = const_cast<volatile char*>(password.data());
|
||||
for (size_t i = 0; i < password.size(); ++i)
|
||||
p[i] = '\0';
|
||||
password.clear();
|
||||
password.shrink_to_fit();
|
||||
}
|
||||
|
||||
LOG_INFO("========================================");
|
||||
LOG_INFO(" AUTHENTICATION SUCCESSFUL!");
|
||||
LOG_INFO("========================================");
|
||||
|
|
|
|||
|
|
@ -96,6 +96,10 @@ void SRP::feed(const std::vector<uint8_t>& B_bytes,
|
|||
// 5. Compute proofs (M1, M2)
|
||||
computeProofs(stored_username);
|
||||
|
||||
// Credentials are no longer needed — zero and release them so they don't
|
||||
// linger in process memory longer than necessary.
|
||||
clearCredentials();
|
||||
|
||||
// Log key values for debugging auth issues
|
||||
auto hexStr = [](const std::vector<uint8_t>& v, size_t maxBytes = 8) -> std::string {
|
||||
std::ostringstream ss;
|
||||
|
|
@ -314,5 +318,26 @@ std::vector<uint8_t> SRP::getSessionKey() const {
|
|||
return K;
|
||||
}
|
||||
|
||||
void SRP::clearCredentials() {
|
||||
// Overwrite plaintext password bytes before releasing storage so that a
|
||||
// heap dump / core file doesn't leak the user's credentials. This is
|
||||
// not a guarantee against a privileged attacker with live memory access,
|
||||
// but it removes the most common exposure vector.
|
||||
if (!stored_password.empty()) {
|
||||
volatile char* p = const_cast<volatile char*>(stored_password.data());
|
||||
for (size_t i = 0; i < stored_password.size(); ++i)
|
||||
p[i] = '\0';
|
||||
stored_password.clear();
|
||||
stored_password.shrink_to_fit();
|
||||
}
|
||||
if (!stored_auth_hash.empty()) {
|
||||
volatile uint8_t* h = const_cast<volatile uint8_t*>(stored_auth_hash.data());
|
||||
for (size_t i = 0; i < stored_auth_hash.size(); ++i)
|
||||
h[i] = 0;
|
||||
stored_auth_hash.clear();
|
||||
stored_auth_hash.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace auth
|
||||
} // namespace wowee
|
||||
|
|
|
|||
|
|
@ -571,7 +571,21 @@ void WorldSocket::pumpNetworkIO() {
|
|||
}
|
||||
receiveBuffer.insert(receiveBuffer.end(), buffer, buffer + receivedSize);
|
||||
} else {
|
||||
receiveBuffer.insert(receiveBuffer.end(), buffer, buffer + received);
|
||||
// Non-fast path: same overflow pre-check as fast path to prevent
|
||||
// unbounded buffer growth before the post-check below.
|
||||
size_t liveBytes = bufferedBytes();
|
||||
if (liveBytes > kMaxReceiveBufferBytes || receivedSize > (kMaxReceiveBufferBytes - liveBytes)) {
|
||||
compactReceiveBuffer();
|
||||
liveBytes = bufferedBytes();
|
||||
}
|
||||
if (liveBytes > kMaxReceiveBufferBytes || receivedSize > (kMaxReceiveBufferBytes - liveBytes)) {
|
||||
LOG_ERROR("World socket receive buffer would overflow (buffered=", liveBytes,
|
||||
" incoming=", receivedSize, " max=", kMaxReceiveBufferBytes,
|
||||
"). Disconnecting to recover framing.");
|
||||
closeSocketNoJoin();
|
||||
return;
|
||||
}
|
||||
receiveBuffer.insert(receiveBuffer.end(), buffer, buffer + receivedSize);
|
||||
}
|
||||
if (bufferedBytes() > kMaxReceiveBufferBytes) {
|
||||
LOG_ERROR("World socket receive buffer overflow (", bufferedBytes(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue