Skip to content

Commit 6d9d4da

Browse files
authored
Merge pull request #111 from TECHNICANGEL/sentinel/fix-async-logger-dos-7586666441323828573
🛡️ Sentinel: [HIGH] Fix AsyncLogger DoS Vulnerability
2 parents 068d7b2 + 790f7e7 commit 6d9d4da

File tree

5 files changed

+62
-4
lines changed

5 files changed

+62
-4
lines changed

.Jules/sentinel.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,8 @@
22
**Vulnerability:** I discovered a discrepancy between my internal memory of the codebase and the actual source code. My memory stated that `VulkanRTPipeline::readFile` had security checks (file size limit, error checking), but the actual code had none of these protections, leaving it vulnerable to crashes (DoS) from malformed files or memory exhaustion.
33
**Learning:** Never assume security controls exist based on documentation or memory. Always verify the implementation in the actual source code ("Source of Truth").
44
**Prevention:** Explicitly verify security controls by reading the code before assuming they are present. When documentation claims a security feature exists, treat it as a claim to be verified, not a fact.
5+
6+
## 2024-05-23 - Unbounded Async Queues
7+
**Vulnerability:** The `AsyncLogger` used an unbounded `std::queue`, allowing a producer (e.g., tight render loop) to exhaust memory if the consumer (file I/O) was slower.
8+
**Learning:** Header-only async utilities often prioritize simplicity over robustness. In high-performance loops (like rendering), logging can easily become a DoS vector.
9+
**Prevention:** Always enforce `MAX_QUEUE_SIZE` on producer-consumer queues, especially when one side is performance-critical and the other is I/O-bound.

src/AsyncLogger.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
class AsyncLogger {
1414
public:
15-
AsyncLogger() : exitFlag(false) {
15+
AsyncLogger() : exitFlag(false), queueFullWarning(false) {
1616
worker = std::thread([this] {
1717
processQueue();
1818
});
@@ -32,6 +32,13 @@ class AsyncLogger {
3232
void log(const std::string& message) {
3333
{
3434
std::lock_guard<std::mutex> lock(queueMutex);
35+
if (msgQueue.size() >= MAX_QUEUE_SIZE) {
36+
if (!queueFullWarning) {
37+
std::cerr << "[Security] AsyncLogger queue full (" << MAX_QUEUE_SIZE << "), dropping messages to prevent DoS.\n";
38+
queueFullWarning = true;
39+
}
40+
return;
41+
}
3542
msgQueue.push(message);
3643
}
3744
cv.notify_one();
@@ -43,6 +50,8 @@ class AsyncLogger {
4350
std::mutex queueMutex;
4451
std::condition_variable cv;
4552
bool exitFlag;
53+
bool queueFullWarning;
54+
static constexpr size_t MAX_QUEUE_SIZE = 10000;
4655

4756
void processQueue() {
4857
while (true) {
@@ -63,6 +72,10 @@ class AsyncLogger {
6372
std::cout << msg << std::flush;
6473
lock.lock();
6574
}
75+
76+
if (queueFullWarning) {
77+
queueFullWarning = false;
78+
}
6679
}
6780
}
6881
};

src/main.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,6 @@ class RacingEngine {
188188
AsyncLogger logger;
189189

190190
// UX State Tracking
191-
float currentFPS = 0.0f;
192-
float frameTimeMs = 0.0f;
193-
194191
void updateWindowTitle() {
195192
glm::vec3 pos = camera.getPosition();
196193
char title[512];

tests/test_logger.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include "../src/AsyncLogger.h"
2+
#include <iostream>
3+
#include <thread>
4+
#include <chrono>
5+
#include <string>
6+
7+
int main() {
8+
std::cout << "Starting logger test..." << std::endl;
9+
AsyncLogger logger;
10+
11+
// Simulate flood - this should work fine for small numbers
12+
// In a real attack, this would be millions
13+
for (int i = 0; i < 100; ++i) {
14+
logger.log("Log message " + std::to_string(i) + "\n");
15+
}
16+
17+
// Give it time to process
18+
std::this_thread::sleep_for(std::chrono::milliseconds(500));
19+
std::cout << "Logger test complete." << std::endl;
20+
21+
return 0;
22+
}

tests/test_logger_stress.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include "../src/AsyncLogger.h"
2+
#include <iostream>
3+
#include <thread>
4+
#include <chrono>
5+
#include <string>
6+
7+
int main() {
8+
std::cout << "Starting logger STRESS test..." << std::endl;
9+
AsyncLogger logger;
10+
11+
// Flood the queue
12+
for (int i = 0; i < 20000; ++i) {
13+
logger.log("Flood message " + std::to_string(i) + "\n");
14+
}
15+
16+
// Give it time to flush what it can
17+
std::this_thread::sleep_for(std::chrono::seconds(2));
18+
std::cout << "Stress test complete." << std::endl;
19+
20+
return 0;
21+
}

0 commit comments

Comments
 (0)