Skip to content
Permalink
Browse files

Merge PR #3510: Prevent instability and crash due to message flood

  • Loading branch information...
davidebeatrici committed Aug 30, 2018
2 parents f672edd + 0daec57 commit 44b9004d2c208b42c6f8ffa99938361e31f5a071
Showing with 104 additions and 0 deletions.
  1. +17 −0 src/murmur/Messages.cpp
  2. +58 −0 src/murmur/ServerUser.cpp
  3. +29 −0 src/murmur/ServerUser.h
@@ -17,6 +17,11 @@
#include "Version.h"
#include "CryptState.h"

#define RATELIMIT(user) \
if (user->leakyBucket.ratelimit(1)) { \
return; \
}

#define MSG_SETUP(st) \
if (uSource->sState != st) { \
return; \
@@ -507,6 +512,10 @@ void Server::msgUserState(ServerUser *uSource, MumbleProto::UserState &msg) {
return;
}

if (uSource == pDstServerUser) {
RATELIMIT(uSource);
}

if (msg.has_channel_id()) {
Channel *c = qhChannels.value(msg.channel_id());
if (!c || (c == pDstServerUser->cChannel))
@@ -830,6 +839,8 @@ void Server::msgChannelState(ServerUser *uSource, MumbleProto::ChannelState &msg
c = qhChannels.value(msg.channel_id());
if (! c)
return;
} else {
RATELIMIT(uSource);
}

// Check if the parent exists
@@ -1123,6 +1134,8 @@ void Server::msgTextMessage(ServerUser *uSource, MumbleProto::TextMessage &msg)
QSet<ServerUser *> users;
QQueue<Channel *> q;

RATELIMIT(uSource);

int res = 0;
emit textMessageFilterSig(res, uSource, msg);
switch (res) {
@@ -1241,6 +1254,8 @@ void Server::msgACL(ServerUser *uSource, MumbleProto::ACL &msg) {
return;
}

RATELIMIT(uSource);

if (msg.has_query() && msg.query()) {
QStack<Channel *> chans;
Channel *p;
@@ -1497,6 +1512,8 @@ void Server::msgContextAction(ServerUser *uSource, MumbleProto::ContextAction &m
}

void Server::msgVersion(ServerUser *uSource, MumbleProto::Version &msg) {
RATELIMIT(uSource);

if (msg.has_version())
uSource->uiVersion=msg.version();
if (msg.has_release())
@@ -112,3 +112,61 @@ int BandwidthRecord::bandwidth() const {
return static_cast<int>((sum * 1000000ULL) / elapsed);
}

#if __cplusplus > 199711LL

inline static
time_point now() {
return std::chrono::steady_clock::now();
}

inline static
unsigned long millisecondsBetween(time_point start, time_point end) {
return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
}

#else

inline static
time_point now() {
return clock();
}

inline static
unsigned long millisecondsBetween(time_point start, time_point end) {
return 1000 * (end - start) / CLOCKS_PER_SEC;
}

#endif

// Rate limiting: burst up to 30, 4 message per sec limit over longer time
LeakyBucket::LeakyBucket() : tokensPerSec(4), maxTokens(30), currentTokens(0) {
lastUpdate = now();
}

bool LeakyBucket::ratelimit(int tokens) {
// First remove tokens we leaked over time
time_point tnow = now();
long ms = millisecondsBetween(lastUpdate, tnow);

long drainTokens = (ms * tokensPerSec) / 1000;

// Prevent constant starvation due to too many updates
if (drainTokens > 0) {
this->lastUpdate = tnow;

this->currentTokens -= drainTokens;
if (this->currentTokens < 0) {
this->currentTokens = 0;
}
}

// Then try to add tokens
bool limit = this->currentTokens > ((static_cast<long>(maxTokens)) - tokens);

// If the bucket is not overflowed, allow message and add tokens
if (!limit) {
this->currentTokens += tokens;
}

return limit;
}
@@ -14,6 +14,13 @@
#include <winsock2.h>
#endif

// <chrono> was introduced in C++11
#if __cplusplus > 199711LL
#include <chrono>
#else
#include <ctime>
#endif

#include "Connection.h"
#include "Timer.h"
#include "User.h"
@@ -55,6 +62,26 @@ struct WhisperTarget {

class Server;

#if __cplusplus > 199711L
typedef std::chrono::time_point<std::chrono::steady_clock> time_point;
#else
typedef clock_t time_point;
#endif

// Simple algorithm for rate limiting
class LeakyBucket {
private:
unsigned int tokensPerSec, maxTokens;
long currentTokens;
time_point lastUpdate;

public:
// Returns true if packets should be dropped
bool ratelimit(int tokens);

LeakyBucket();
};

class ServerUser : public Connection, public User {
private:
Q_OBJECT
@@ -103,6 +130,8 @@ class ServerUser : public Connection, public User {
QMap<int, TargetCache> qmTargetCache;
QMap<QString, QString> qmWhisperRedirect;

LeakyBucket leakyBucket;

int iLastPermissionCheck;
QMap<int, unsigned int> qmPermissionSent;
#ifdef Q_OS_UNIX

0 comments on commit 44b9004

Please sign in to comment.
You can’t perform that action at this time.