Implemented better testing and fixed skipepd frames
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* @file WSServerBufferTest.cpp
|
||||
* @brief Reproduction test for CR-1: 1-byte heap OOB write in WSServer.
|
||||
*
|
||||
* Verifies that the receive buffer allocated in ClientReadLoop is large enough
|
||||
* to hold a maximal masked WebSocket frame (14-byte header + 65536 payload)
|
||||
* plus one extra byte for in-place NUL-termination, without overflowing.
|
||||
*
|
||||
* Build: linked into the GTest harness alongside MainGTest.cpp.
|
||||
*/
|
||||
|
||||
#include "WSFrame.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
using namespace StreamHub;
|
||||
|
||||
// Mirror the WSServer.h constant (TEST_WS_MAX_RECV_PAYLOAD = 65536).
|
||||
static const uint32 TEST_WS_MAX_RECV_PAYLOAD = 65536u;
|
||||
|
||||
// Test: A maximal masked WebSocket frame (64-bit extended length, masked)
|
||||
// with payloadLen = TEST_WS_MAX_RECV_PAYLOAD must fit within kRecvBuf, and
|
||||
// payload[plen] must be a valid in-bounds index (for NUL-termination).
|
||||
TEST(WSServerBufferTest, MaximalFrameFitsInRecvBuffer) {
|
||||
// Reproduce the exact buffer sizing logic from WSServer::ClientReadLoop.
|
||||
const uint32 kRecvBuf = TEST_WS_MAX_RECV_PAYLOAD + 14u + 1u;
|
||||
uint8 *buf = new uint8[kRecvBuf];
|
||||
|
||||
// Build a maximal masked frame: FIN + TEXT, payloadLen=65536 (64-bit ext),
|
||||
// mask=1.
|
||||
uint8 frame[14 + 65536];
|
||||
frame[0] = WS_FIN_BIT | WS_OPCODE_TEXT; // FIN + TEXT
|
||||
frame[1] = WS_MASK_BIT | 127u; // masked + 64-bit length
|
||||
// 8-byte extended length = 65536
|
||||
uint64 plen = TEST_WS_MAX_RECV_PAYLOAD;
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
frame[2 + i] = static_cast<uint8>(plen & 0xFFu);
|
||||
plen >>= 8u;
|
||||
}
|
||||
// 4-byte mask key
|
||||
frame[10] = 0xAA; frame[11] = 0xBB; frame[12] = 0xCC; frame[13] = 0xDD;
|
||||
// Payload (doesn't matter, just fill with zeros)
|
||||
memset(frame + 14, 0, 65536);
|
||||
|
||||
// Copy into buf (simulating a TCP read)
|
||||
ASSERT_LE(sizeof(frame), static_cast<size_t>(kRecvBuf));
|
||||
memcpy(buf, frame, sizeof(frame));
|
||||
|
||||
// Parse the header
|
||||
WSFrameHeader hdr;
|
||||
ASSERT_TRUE(WSParseHeader(buf, sizeof(frame), hdr));
|
||||
ASSERT_EQ(hdr.headerSize, 14u);
|
||||
ASSERT_EQ(hdr.payloadLen, static_cast<uint64>(TEST_WS_MAX_RECV_PAYLOAD));
|
||||
ASSERT_TRUE(hdr.masked);
|
||||
|
||||
// Unmask
|
||||
uint8 *payload = buf + hdr.headerSize;
|
||||
WSUnmask(payload, static_cast<uint32>(hdr.payloadLen), hdr.maskKey);
|
||||
|
||||
// The critical check: payload[plen] must be within the buffer.
|
||||
// Before the fix, kRecvBuf was 65550 and payload[65536] = buf[65550]
|
||||
// was one byte past the end. After the fix (+1), it's in bounds.
|
||||
uint32 plenIdx = static_cast<uint32>(hdr.payloadLen);
|
||||
ASSERT_LT(hdr.headerSize + plenIdx, kRecvBuf)
|
||||
<< "payload[plen] would be out of bounds — buffer overflow!";
|
||||
|
||||
// Simulate the NUL-termination that WSServer does:
|
||||
uint8 savedByte = payload[plenIdx];
|
||||
payload[plenIdx] = '\0';
|
||||
// Verify it's within bounds (no ASan/heap overflow)
|
||||
EXPECT_EQ(payload[plenIdx], '\0');
|
||||
payload[plenIdx] = savedByte;
|
||||
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
// Test: Verify the old (buggy) buffer size would have overflowed.
|
||||
// This documents the bug for future readers.
|
||||
TEST(WSServerBufferTest, OldBufferSizeWouldOverflow) {
|
||||
const uint32 oldKRecvBuf = TEST_WS_MAX_RECV_PAYLOAD + 14u; // the buggy size
|
||||
const uint32 headerSize = 14u;
|
||||
const uint32 plen = TEST_WS_MAX_RECV_PAYLOAD;
|
||||
// headerSize + plen == oldKRecvBuf, so payload[plen] = buf[oldKRecvBuf]
|
||||
// is one byte past the end.
|
||||
ASSERT_EQ(headerSize + plen, oldKRecvBuf)
|
||||
<< "Expected the old buffer to be exactly full (no room for NUL term)";
|
||||
// The fix adds +1:
|
||||
const uint32 newKRecvBuf = TEST_WS_MAX_RECV_PAYLOAD + 14u + 1u;
|
||||
ASSERT_LT(headerSize + plen, newKRecvBuf)
|
||||
<< "New buffer must have room for the NUL-termination byte";
|
||||
}
|
||||
Reference in New Issue
Block a user