File mumble-1.2.19-limit-amount-of-messages.patch of Package mumble.openSUSE_Backports_SLE-15_Update

From 44b9004d2c208b42c6f8ffa99938361e31f5a071
From: MadMaurice <madmaurice@zom.bi>
Date:   Thu Aug 30 15:08:01 2018 +0200

    Prevent instability and crash due to message flood
    
    This patch adds a rate limiting to selected patches. The underlying rate limiter
    used is the Leaky-Bucket algorithm. It allows for a burst of messages, but
    limits them after a specified amount of messages within a time frame.

From: Ferdinand Thiessen <rpm@fthiessen.de>

    Modified this diff, to make it work with 1.2.19 tarball.
    "Backported" by manually change the 1.2.19 version according to the original diff.

diff -Nur mumble-1.2.19/src/murmur/Messages.cpp new/src/murmur/Messages.cpp
--- mumble-1.2.19/src/murmur/Messages.cpp	2017-01-27 07:48:33.000000000 +0100
+++ new/src/murmur/Messages.cpp	2019-07-13 00:45:48.281780195 +0200
@@ -42,6 +42,11 @@
 #include "ServerUser.h"
 #include "Version.h"
 
+#define RATELIMIT(user) \
+       if (user->leakyBucket.ratelimit(1)) { \
+               return; \
+       }
+
 #define MSG_SETUP(st) \
 	if (uSource->sState != st) { \
 		return; \
@@ -491,6 +496,10 @@
 	msg.set_session(pDstServerUser->uiSession);
 	msg.set_actor(uSource->uiSession);
 
+	if (uSource == pDstServerUser) {
+		RATELIMIT(uSource);
+	}
+
 	if (msg.has_channel_id()) {
 		Channel *c = qhChannels.value(msg.channel_id());
 		if (!c || (c == pDstServerUser->cChannel))
@@ -798,6 +807,8 @@
 		p = qhChannels.value(msg.parent());
 		if (! p)
 			return;
+	} else {
+		RATELIMIT(uSource);
 	}
 
 	msg.clear_links();
@@ -1074,6 +1085,8 @@
 	QSet<ServerUser *> users;
 	QQueue<Channel *> q;
 
+	RATELIMIT(uSource);
+
 	QString text = u8(msg.message());
 	bool changed = false;
 
@@ -1176,6 +1189,8 @@
 		return;
 	}
 
+	RATELIMIT(uSource);
+
 	if (msg.has_query() && msg.query()) {
 		QStack<Channel *> chans;
 		Channel *p;
@@ -1417,6 +1432,8 @@
 }
 
 void Server::msgVersion(ServerUser *uSource, MumbleProto::Version &msg) {
+	RATELIMIT(uSource);
+
 	if (msg.has_version())
 		uSource->uiVersion=msg.version();
 	if (msg.has_release())
diff -Nur mumble-1.2.19/src/murmur/ServerUser.cpp new/src/murmur/ServerUser.cpp
--- mumble-1.2.19/src/murmur/ServerUser.cpp	2017-01-27 07:48:33.000000000 +0100
+++ new/src/murmur/ServerUser.cpp	2019-07-13 00:47:25.974498227 +0200
@@ -128,3 +128,61 @@
 	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;
+}
diff -Nur mumble-1.2.19/src/murmur/ServerUser.h new/src/murmur/ServerUser.h
--- mumble-1.2.19/src/murmur/ServerUser.h	2017-01-27 07:48:33.000000000 +0100
+++ new/src/murmur/ServerUser.h	2019-07-13 00:49:28.023395272 +0200
@@ -40,6 +40,13 @@
 #include <winsock2.h>
 #endif
 
+// <chrono> was introduced in C++11
+#if __cplusplus > 199711LL
+#include <chrono>
+#else
+#include <ctime>
+#endif
+
 #include "Connection.h"
 #include "Net.h"
 #include "Timer.h"
@@ -80,6 +87,26 @@
 
 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
@@ -119,6 +146,8 @@
 		QMap<int, TargetCache> qmTargetCache;
 		QMap<QString, QString> qmWhisperRedirect;
 
+		LeakyBucket leakyBucket;
+
 		int iLastPermissionCheck;
 		QMap<int, unsigned int> qmPermissionSent;
 #ifdef Q_OS_UNIX
openSUSE Build Service is sponsored by