memory, threading, network hardening

Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
This commit is contained in:
Pavel Okhlopkov 2026-04-06 21:19:37 +03:00
parent 312994be83
commit 2e8856bacd
9 changed files with 135 additions and 24 deletions

View file

@ -48,6 +48,10 @@ public:
// Get session key (K) - used for encryption
std::vector<uint8_t> getSessionKey() const;
// Securely erase stored plaintext credentials from memory.
// Called automatically at the end of feed() once the SRP values are computed.
void clearCredentials();
private:
// WoW-specific SRP multiplier (k = 3)
static constexpr uint32_t K_VALUE = 3;

View file

@ -361,6 +361,8 @@ public:
}
private:
// MAIN-THREAD-ONLY: all entity map mutations happen via dispatchQueuedPackets()
// which runs on the main thread. Do NOT access from the async network pump thread.
std::unordered_map<uint64_t, std::shared_ptr<Entity>> entities;
};

View file

@ -94,15 +94,18 @@ private:
void recordRecentPacket(bool outbound, uint16_t opcode, uint16_t payloadLen);
void dumpRecentPacketHistoryLocked(const char* reason, size_t bufferedBytes);
socket_t sockfd = INVALID_SOCK;
bool connected = false;
bool encryptionEnabled = false;
socket_t sockfd = INVALID_SOCK; // THREAD-SAFE: protected by ioMutex_
bool connected = false; // THREAD-SAFE: protected by ioMutex_
bool encryptionEnabled = false; // THREAD-SAFE: protected by ioMutex_
bool useVanillaCrypt = false; // true = XOR cipher, false = RC4
bool useAsyncPump_ = true;
std::thread asyncPumpThread_;
std::atomic<bool> asyncPumpStop_{false};
std::atomic<bool> asyncPumpRunning_{false};
std::atomic<bool> asyncPumpStop_{false}; // THREAD-SAFE: atomic
std::atomic<bool> asyncPumpRunning_{false}; // THREAD-SAFE: atomic
// Guards sockfd, connected, encryptionEnabled, receiveBuffer, cipher state,
// headerBytesDecrypted, and recentPacketHistory_.
mutable std::mutex ioMutex_;
// Guards pendingPacketCallbacks_ (asyncPumpThread_ produces, main thread consumes).
mutable std::mutex callbackMutex_;
// WotLK RC4 ciphers for header encryption/decryption
@ -112,11 +115,12 @@ private:
// Vanilla/TBC XOR+addition cipher
auth::VanillaCrypt vanillaCrypt;
// Receive buffer
// THREAD-SAFE: protected by ioMutex_
std::vector<uint8_t> receiveBuffer;
size_t receiveReadOffset_ = 0;
// Optional reused packet queue (feature-gated) to reduce per-update allocations.
std::vector<Packet> parsedPacketsScratch_;
// THREAD-SAFE: protected by callbackMutex_.
// Parsed packets waiting for callback dispatch; drained with a strict per-update budget.
std::deque<Packet> pendingPacketCallbacks_;

View file

@ -4,6 +4,7 @@
#include "pipeline/dbc_loader.hpp"
#include "pipeline/asset_manifest.hpp"
#include "pipeline/loose_file_reader.hpp"
#include <atomic>
#include <memory>
#include <string>
#include <vector>
@ -166,7 +167,11 @@ private:
*/
std::string resolveFile(const std::string& normalizedPath) const;
// Guards fileCache, dbcCache, fileCacheTotalBytes, fileCacheAccessCounter, and
// fileCacheBudget. Shared lock for read-only cache lookups (readFile cache hit,
// loadDBC cache hit); exclusive lock for inserts and eviction.
mutable std::shared_mutex cacheMutex;
// THREAD-SAFE: protected by cacheMutex (exclusive lock for writes).
std::unordered_map<std::string, std::shared_ptr<DBCFile>> dbcCache;
// File cache (LRU, dynamic budget based on system RAM)
@ -174,11 +179,14 @@ private:
std::vector<uint8_t> data;
uint64_t lastAccessTime;
};
// THREAD-SAFE: protected by cacheMutex (shared_mutex — shared_lock for reads,
// exclusive lock_guard for writes/eviction).
mutable std::unordered_map<std::string, CachedFile> fileCache;
mutable size_t fileCacheTotalBytes = 0;
mutable uint64_t fileCacheAccessCounter = 0;
mutable size_t fileCacheHits = 0;
mutable size_t fileCacheMisses = 0;
// THREAD-SAFE: atomic — incremented from any thread after releasing cacheMutex.
mutable std::atomic<size_t> fileCacheHits{0};
mutable std::atomic<size_t> fileCacheMisses{0};
mutable size_t fileCacheBudget = 1024 * 1024 * 1024; // Dynamic, starts at 1GB
void setupFileCacheBudget();

View file

@ -362,10 +362,13 @@ private:
// Background loading worker pool
std::vector<std::thread> workerThreads;
int workerCount = 0;
// THREAD-SAFE: guards loadQueue, readyQueue, and pendingTiles.
// Workers wait on queueCV; main thread signals when new tiles are enqueued
// or when readyQueue drains below maxReadyQueueSize_.
std::mutex queueMutex;
std::condition_variable queueCV;
std::deque<TileCoord> loadQueue;
std::queue<std::shared_ptr<PendingTile>> readyQueue;
std::deque<TileCoord> loadQueue; // THREAD-SAFE: protected by queueMutex
std::queue<std::shared_ptr<PendingTile>> readyQueue; // THREAD-SAFE: protected by queueMutex
// Maximum number of prepared-but-not-finalized tiles in readyQueue.
// Each prepared tile can hold 100500 MB of decoded textures in RAM.
// Workers sleep when this limit is reached, letting the main thread
@ -378,6 +381,7 @@ private:
size_t bytes = 0;
std::list<TileCoord>::iterator lruIt;
};
// THREAD-SAFE: protected by tileCacheMutex_.
std::unordered_map<TileCoord, CachedTile, TileCoord::Hash> tileCache_;
std::list<TileCoord> tileCacheLru_;
size_t tileCacheBytes_ = 0;
@ -391,8 +395,8 @@ private:
std::atomic<bool> workerRunning{false};
// Track tiles currently queued or being processed to avoid duplicates
std::unordered_map<TileCoord, bool, TileCoord::Hash> pendingTiles;
std::unordered_set<std::string> missingAdtWarnings_;
std::unordered_map<TileCoord, bool, TileCoord::Hash> pendingTiles; // THREAD-SAFE: protected by queueMutex
std::unordered_set<std::string> missingAdtWarnings_; // THREAD-SAFE: protected by missingAdtWarningsMutex_
std::mutex missingAdtWarningsMutex_;
// Thread-safe set of M2 model IDs already uploaded to GPU
@ -405,10 +409,11 @@ private:
std::unordered_set<uint32_t> preparedWmoUniqueIds_;
std::mutex preparedWmoUniqueIdsMutex_;
// Dedup set for doodad placements across tile boundaries
// MAIN-THREAD-ONLY: checked and modified in processReadyTiles() and unloadDistantTiles(),
// both of which run exclusively on the main thread.
std::unordered_set<uint32_t> placedDoodadIds;
// Dedup set for WMO placements across tile boundaries (prevents rendering Stormwind 16x)
// MAIN-THREAD-ONLY: same contract as placedDoodadIds.
std::unordered_set<uint32_t> placedWmoIds;
// Tiles currently being incrementally finalized across frames