Implemented better testing and fixed skipepd frames
This commit is contained in:
@@ -220,6 +220,9 @@ void UDPSourceSession::ParseConfigPayload(const uint8 *payload, uint32 size) {
|
||||
memcpy(&sigDescs_[i],
|
||||
payload + 4u + i * UDPS_SIGNAL_DESC_SIZE,
|
||||
UDPS_SIGNAL_DESC_SIZE);
|
||||
/* MD-3: force null-termination of name/unit to prevent intra-struct OOB read */
|
||||
sigDescs_[i].name[sizeof(sigDescs_[i].name) - 1u] = '\0';
|
||||
sigDescs_[i].unit[sizeof(sigDescs_[i].unit) - 1u] = '\0';
|
||||
}
|
||||
publishMode_ = payload[4u + numSigs * UDPS_SIGNAL_DESC_SIZE];
|
||||
numSignals_ = numSigs;
|
||||
@@ -237,8 +240,11 @@ void UDPSourceSession::ParseConfigPayload(const uint8 *payload, uint32 size) {
|
||||
/* (Re)allocate the time-signal decode scratch to the largest element count. */
|
||||
uint32 maxElems = 1u;
|
||||
for (uint32 i = 0u; i < numSigs; i++) {
|
||||
uint32 ne = sigDescs_[i].numRows * sigDescs_[i].numCols;
|
||||
if (ne > maxElems) { maxElems = ne; }
|
||||
uint64 ne = static_cast<uint64>(sigDescs_[i].numRows) *
|
||||
static_cast<uint64>(sigDescs_[i].numCols);
|
||||
if (ne == 0u) { ne = 1u; }
|
||||
if (ne > 0x100000u) { ne = 0x100000u; /* sanity cap */ }
|
||||
if (ne > maxElems) { maxElems = static_cast<uint32>(ne); }
|
||||
}
|
||||
if (maxElems > timeScratchLen_) {
|
||||
delete[] timeScratch_;
|
||||
@@ -343,22 +349,36 @@ void UDPSourceSession::ParseDataPayload(const uint8 *payload, uint32 size) {
|
||||
uint32 off = offset;
|
||||
for (uint32 s = 0u; s < nSigs; s++) {
|
||||
const UDPSSignalDescriptor &desc = descs[s];
|
||||
uint32 numElements = desc.numRows * desc.numCols;
|
||||
if (numElements == 0u) { numElements = 1u; }
|
||||
/* HI-1: use 64-bit multiply to avoid overflow on attacker-controlled numRows/numCols */
|
||||
uint64 numElements64 = static_cast<uint64>(desc.numRows) *
|
||||
static_cast<uint64>(desc.numCols);
|
||||
if (numElements64 == 0u) { numElements64 = 1u; }
|
||||
if (numElements64 > 0x100000u) { return; /* sanity cap: 1M elements */ }
|
||||
uint32 numElements = static_cast<uint32>(numElements64);
|
||||
|
||||
uint32 wireElemBytes = (desc.quantType != UDPS_QUANT_NONE)
|
||||
? QuantWireBytes(desc.quantType)
|
||||
: MARTe::UDPSTypeCodeByteSize(desc.typeCode);
|
||||
if (wireElemBytes == 0u) { return; }
|
||||
|
||||
uint32 elemsToRead = ((pm == UDPS_PUBLISH_ACCUMULATE) && (numElements == 1u))
|
||||
? numSamples
|
||||
: numElements;
|
||||
/* Accumulate mode batches one full snapshot (all elements) per RT
|
||||
* cycle for every signal (scalar or array) — see UDPStreamer's
|
||||
* SerializeAccumulated. HI-1: 64-bit multiply to avoid overflow on
|
||||
* attacker-controlled numSamples. */
|
||||
uint64 elemsToRead64 = (pm == UDPS_PUBLISH_ACCUMULATE)
|
||||
? (numElements64 * static_cast<uint64>(numSamples))
|
||||
: numElements64;
|
||||
if (elemsToRead64 > 0x100000u) { return; /* sanity cap: 1M elements */ }
|
||||
uint32 elemsToRead = static_cast<uint32>(elemsToRead64);
|
||||
|
||||
if (off + elemsToRead * wireElemBytes > size) { return; }
|
||||
/* HI-1: 64-bit bounds check to prevent uint32 multiply overflow */
|
||||
uint64 bytesNeeded = static_cast<uint64>(off) +
|
||||
static_cast<uint64>(elemsToRead) *
|
||||
static_cast<uint64>(wireElemBytes);
|
||||
if (bytesNeeded > static_cast<uint64>(size)) { return; }
|
||||
sigOff[s] = off;
|
||||
sigElems[s] = elemsToRead;
|
||||
off += elemsToRead * wireElemBytes;
|
||||
off += static_cast<uint32>(elemsToRead * wireElemBytes);
|
||||
}
|
||||
|
||||
/* The decode scratch is sized at CONFIG time to the largest per-signal
|
||||
@@ -389,8 +409,10 @@ void UDPSourceSession::ParseDataPayload(const uint8 *payload, uint32 size) {
|
||||
|
||||
for (uint32 s = 0u; s < nSigs; s++) {
|
||||
const UDPSSignalDescriptor &desc = descs[s];
|
||||
uint32 numElements = desc.numRows * desc.numCols;
|
||||
if (numElements == 0u) { numElements = 1u; }
|
||||
uint64 ne64 = static_cast<uint64>(desc.numRows) *
|
||||
static_cast<uint64>(desc.numCols);
|
||||
if (ne64 == 0u) { ne64 = 1u; }
|
||||
uint32 numElements = static_cast<uint32>(ne64);
|
||||
const uint32 nElems = sigElems[s];
|
||||
|
||||
const bool isFirstLast = (numElements > 1u) &&
|
||||
|
||||
@@ -199,6 +199,52 @@ bool WSServer::UpgradeHTTP(BasicTCPSocket *sock) {
|
||||
if (strstr(hdrBuf, "\r\n\r\n") != static_cast<char *>(0)) { break; }
|
||||
}
|
||||
|
||||
/* Origin validation (CSWSH / CSRF defence, RFC 6455 §10.2).
|
||||
* If an Origin header is present, its host must match the Host header
|
||||
* (same-origin). Non-browser clients (no Origin) are allowed. */
|
||||
const char *originHdr = FindSubstr(hdrBuf, "Origin:");
|
||||
if (originHdr != static_cast<const char *>(0)) {
|
||||
originHdr += 7; /* skip "Origin:" */
|
||||
while (*originHdr == ' ') { originHdr++; }
|
||||
/* Extract the host part of Origin: "scheme://host[:port]" */
|
||||
char originHost[256];
|
||||
uint32 ohLen = 0u;
|
||||
const char *op = originHdr;
|
||||
/* Skip scheme:// */
|
||||
const char *schemeEnd = strstr(op, "://");
|
||||
if (schemeEnd != static_cast<const char *>(0)) { op = schemeEnd + 3; }
|
||||
while (*op != '\r' && *op != '\n' && *op != '\0' &&
|
||||
*op != '/' && ohLen < 255u) {
|
||||
originHost[ohLen++] = *op++;
|
||||
}
|
||||
originHost[ohLen] = '\0';
|
||||
|
||||
/* Extract Host header value */
|
||||
const char *hostHdr = FindSubstr(hdrBuf, "Host:");
|
||||
if (hostHdr != static_cast<const char *>(0)) {
|
||||
hostHdr += 5; /* skip "Host:" */
|
||||
while (*hostHdr == ' ') { hostHdr++; }
|
||||
char hostVal[256];
|
||||
uint32 hvLen = 0u;
|
||||
while (*hostHdr != '\r' && *hostHdr != '\n' &&
|
||||
*hostHdr != '\0' && hvLen < 255u) {
|
||||
hostVal[hvLen++] = *hostHdr++;
|
||||
}
|
||||
hostVal[hvLen] = '\0';
|
||||
if (strcmp(originHost, hostVal) != 0) {
|
||||
/* Cross-origin — reject the upgrade */
|
||||
const char *forbidden =
|
||||
"HTTP/1.1 403 Forbidden\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\nOrigin not allowed\r\n";
|
||||
uint32 forbLen = static_cast<uint32>(strlen(forbidden));
|
||||
(void) sock->Write(forbidden, forbLen);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Find Sec-WebSocket-Key */
|
||||
const char *keyHdr = FindSubstr(hdrBuf, "Sec-WebSocket-Key:");
|
||||
if (keyHdr == static_cast<const char *>(0)) { return false; }
|
||||
@@ -246,8 +292,9 @@ void WSServer::ClientReadLoop(uint32 slotIdx) {
|
||||
WSClientSlot &slot = clients[slotIdx];
|
||||
BasicTCPSocket *sock = slot.sock;
|
||||
|
||||
/* Receive buffer (grows as needed by simple state machine) */
|
||||
static const uint32 kRecvBuf = WS_MAX_RECV_PAYLOAD + 14u;
|
||||
/* Receive buffer: WS_MAX_RECV_PAYLOAD + max header (14: 2 + 8 ext-length +
|
||||
* 4 mask) + 1 spare byte for in-place NUL-termination of the payload. */
|
||||
static const uint32 kRecvBuf = WS_MAX_RECV_PAYLOAD + 14u + 1u;
|
||||
uint8 *buf = new uint8[kRecvBuf];
|
||||
uint32 filled = 0u;
|
||||
|
||||
@@ -431,6 +478,9 @@ uint32 WSServer::AllocSlot(BasicTCPSocket *sock) {
|
||||
|
||||
void WSServer::FreeSlot(uint32 idx) {
|
||||
if (idx >= WS_MAX_CLIENTS) { return; }
|
||||
/* HI-5: acquire writeMutex before modifying active/sock to prevent
|
||||
* use-after-free when BroadcastText/BroadcastBinary are iterating. */
|
||||
(void) clients[idx].writeMutex.FastLock();
|
||||
(void) clientsMutex.FastLock();
|
||||
if (clients[idx].active) {
|
||||
clients[idx].active = false;
|
||||
@@ -442,6 +492,7 @@ void WSServer::FreeSlot(uint32 idx) {
|
||||
if (numClients > 0u) { numClients--; }
|
||||
}
|
||||
clientsMutex.FastUnLock();
|
||||
clients[idx].writeMutex.FastUnLock();
|
||||
}
|
||||
|
||||
} /* namespace StreamHub */
|
||||
|
||||
Reference in New Issue
Block a user