Implemented better testing and fixed skipepd frames

This commit is contained in:
Martino Ferrari
2026-07-01 16:39:34 +02:00
parent 7a326c5d78
commit 0bea41f866
46 changed files with 4358 additions and 1739 deletions
@@ -78,8 +78,9 @@ public:
bool Push(uint32 signalID, uint64 timestamp, void* data, uint32 size) {
uint32 packetSize = 4 + 8 + 4 + size; // ID + TS + Size + Data
uint32 read = readIndex;
uint32 write = writeIndex;
/* HI-9: use atomic loads for cross-thread index reads */
uint32 read = __atomic_load_n(&readIndex, __ATOMIC_ACQUIRE);
uint32 write = __atomic_load_n(&writeIndex, __ATOMIC_ACQUIRE);
uint32 available = 0;
if (read <= write) {
@@ -96,13 +97,15 @@ public:
WriteToBuffer(&tempWrite, &size, 4);
WriteToBuffer(&tempWrite, data, size);
writeIndex = tempWrite;
// HI-9: release store so data writes are visible before index update
__atomic_store_n(&writeIndex, tempWrite, __ATOMIC_RELEASE);
return true;
}
bool Pop(uint32 &signalID, uint64 &timestamp, void* dataBuffer, uint32 &size, uint32 maxSize) {
uint32 read = readIndex;
uint32 write = writeIndex;
/* HI-9: acquire-load writeIndex to see data written by Push */
uint32 read = __atomic_load_n(&readIndex, __ATOMIC_ACQUIRE);
uint32 write = __atomic_load_n(&writeIndex, __ATOMIC_ACQUIRE);
if (read == write) return false;
uint32 tempRead = read;
@@ -124,9 +127,9 @@ public:
// locate the next entry safely, so fall back to discarding everything
// to avoid reading garbage as sample headers on future Pop() calls.
if (tempSize >= bufferSize) {
readIndex = write; // corrupt ring — discard all
__atomic_store_n(&readIndex, write, __ATOMIC_RELEASE); // corrupt ring — discard all
} else {
readIndex = (tempRead + tempSize) % bufferSize;
__atomic_store_n(&readIndex, (tempRead + tempSize) % bufferSize, __ATOMIC_RELEASE);
}
return false;
}
@@ -137,13 +140,14 @@ public:
timestamp = tempTs;
size = tempSize;
readIndex = tempRead;
// HI-9: release-store readIndex after reading data
__atomic_store_n(&readIndex, tempRead, __ATOMIC_RELEASE);
return true;
}
uint32 Count() {
uint32 read = readIndex;
uint32 write = writeIndex;
uint32 read = __atomic_load_n(&readIndex, __ATOMIC_ACQUIRE);
uint32 write = __atomic_load_n(&writeIndex, __ATOMIC_ACQUIRE);
if (write >= read) return write - read;
return bufferSize - (read - write);
}
@@ -13,6 +13,7 @@
#include "Threads.h"
#include "TimeoutType.h"
#include "UDPSProtocol.h"
#include <string.h>
namespace MARTe {
@@ -122,6 +123,12 @@ bool DebugService::Initialise(StructuredDataI &data) {
suppressTimeoutLogs = (suppress == 1u);
}
StreamString tempToken;
if (data.Read("AuthToken", tempToken)) {
authToken = tempToken;
}
clientAuthenticated = (authToken.Size() == 0u);
// Capture only the local subtree — do NOT call MoveToRoot() on the shared CDB.
(void)data.Copy(fullConfig);
@@ -281,6 +288,8 @@ ErrorManagement::ErrorType DebugService::Server(ExecutionInfo &info) {
cmdCountInWindow = 0u;
cmdWindowStartMs = nowMs;
lastDataTimeMs = nowMs;
/* CR-5: require auth if an AuthToken is configured. */
clientAuthenticated = (authToken.Size() == 0u);
}
} else {
if (nowMs - lastDataTimeMs > CLIENT_IDLE_TIMEOUT_MS) {
@@ -351,23 +360,101 @@ ErrorManagement::ErrorType DebugService::Server(ExecutionInfo &info) {
uint32 cmdLen = len;
command.Write(raw + lineStart, cmdLen);
// Dispatch via base HandleCommand, write response to socket.
StreamString out;
HandleCommand(command, out);
if (out.Size() > 0u) {
const char8 *wPtr = out.Buffer();
uint32 remaining = (uint32)out.Size();
lastDataTimeMs = (uint64)((float64)HighResolutionTimer::Counter() *
HighResolutionTimer::Period() * 1000.0);
while (remaining > 0u) {
uint32 wrote = remaining;
if (!activeClient->Write(wPtr, wrote) || wrote == 0u) {
break;
/* CR-5: Auth token gate. If an AuthToken is
* configured, the client must send
* "AUTH <token>" before any other command. */
if (authToken.Size() > 0u) {
const char8 *cmdPtr = command.Buffer();
if (cmdLen >= 5u &&
strncmp(cmdPtr, "AUTH ", 5u) == 0) {
const char8 *recvToken = cmdPtr + 5u;
uint32 recvLen = cmdLen - 5u;
/* Strip trailing \r if present */
if (recvLen > 0u &&
recvToken[recvLen - 1u] == '\r') {
recvLen--;
}
wPtr += wrote;
remaining -= wrote;
if (recvLen == authToken.Size() &&
strncmp(recvToken,
authToken.Buffer(),
recvLen) == 0) {
clientAuthenticated = true;
const char8 *okResp =
"OK AUTHENTICATED\n";
uint32 respSz =
static_cast<uint32>(
strlen(okResp));
(void) activeClient->Write(
okResp, respSz);
} else {
const char8 *badResp =
"ERR AUTH_FAILED\n";
uint32 respSz =
static_cast<uint32>(
strlen(badResp));
(void) activeClient->Write(
badResp, respSz);
}
} else if (!clientAuthenticated) {
const char8 *needAuth =
"ERR AUTH_REQUIRED\n";
uint32 respSz =
static_cast<uint32>(
strlen(needAuth));
(void) activeClient->Write(
needAuth, respSz);
} else {
// Dispatch via base HandleCommand,
// write response to socket.
StreamString out;
HandleCommand(command, out);
if (out.Size() > 0u) {
const char8 *wPtr = out.Buffer();
uint32 remaining =
(uint32)out.Size();
lastDataTimeMs =
(uint64)((float64)
HighResolutionTimer::Counter() *
HighResolutionTimer::Period() *
1000.0);
while (remaining > 0u) {
uint32 wrote = remaining;
if (!activeClient->Write(
wPtr, wrote) ||
wrote == 0u) {
break;
}
wPtr += wrote;
remaining -= wrote;
lastDataTimeMs =
(uint64)((float64)
HighResolutionTimer::Counter() *
HighResolutionTimer::Period() *
1000.0);
}
}
}
} else {
// No auth token configured — back-compat.
// Dispatch via base HandleCommand, write
// response to socket.
StreamString out;
HandleCommand(command, out);
if (out.Size() > 0u) {
const char8 *wPtr = out.Buffer();
uint32 remaining = (uint32)out.Size();
lastDataTimeMs = (uint64)((float64)HighResolutionTimer::Counter() *
HighResolutionTimer::Period() * 1000.0);
while (remaining > 0u) {
uint32 wrote = remaining;
if (!activeClient->Write(wPtr, wrote) || wrote == 0u) {
break;
}
wPtr += wrote;
remaining -= wrote;
lastDataTimeMs = (uint64)((float64)HighResolutionTimer::Counter() *
HighResolutionTimer::Period() * 1000.0);
}
}
}
}
@@ -76,6 +76,13 @@ private:
bool isServer;
bool suppressTimeoutLogs;
/** Optional authentication token (CR-5). If set (non-empty), the first
* command from a new TCP client must be "AUTH <token>". All other
* commands are rejected until the client authenticates. If empty
* (default), no authentication is required (back-compat). */
StreamString authToken;
bool clientAuthenticated;
BasicTCPSocket tcpServer;
UDPSServer udpsServer; ///< Handles fragmentation and multi-client sending
@@ -181,6 +181,12 @@ static void BuildCDBFromContainer(ReferenceContainer *container,
}
}
/* HI-8: Guard against double-patching (e.g. two DebugService instances).
* Once the registry has been patched, subsequent PatchRegistry() calls are
* no-ops. Original builders are not saved/restored — the debug wrappers
* persist for the process lifetime (intentional for transparent debugging). */
static bool registryPatched = false;
static void PatchItemInternal(const char8 *originalName,
ObjectBuilder *debugBuilder) {
ClassRegistryItem *item =
@@ -215,6 +221,14 @@ DebugServiceBase::~DebugServiceBase() {
// ---------------------------------------------------------------------------
void DebugServiceBase::PatchRegistry() {
/* HI-8: skip if already patched (prevents double-patch leak when multiple
* DebugService instances are created). */
if (registryPatched) {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
"PatchRegistry: registry already patched — skipping (double-patch guard).");
return;
}
registryPatched = true;
PatchItemInternal("MemoryMapInputBroker",
new DebugMemoryMapInputBrokerBuilder());
PatchItemInternal("MemoryMapOutputBroker",
@@ -305,13 +319,20 @@ void DebugServiceBase::ProcessSignal(DebugSignalInfo *signalInfo, uint32 size,
return;
if (signalInfo->isForcing) {
uint32 nEl = signalInfo->numberOfElements;
/* HI-4: clamp size to forcedValue buffer to prevent OOB read */
uint32 forceSize = size;
if (forceSize > static_cast<uint32>(sizeof(signalInfo->forcedValue))) {
forceSize = static_cast<uint32>(sizeof(signalInfo->forcedValue));
}
if (nEl <= 1u) {
// Scalar — single memcpy.
memcpy(signalInfo->memoryAddress, signalInfo->forcedValue, size);
// Scalar — single memcpy (clamped to forcedValue bounds).
memcpy(signalInfo->memoryAddress, signalInfo->forcedValue, forceSize);
} else {
// Array — copy only the elements whose bit is set in forcedMask.
uint32 elemBytes = size / nEl;
for (uint32 e = 0u; e < nEl; e++) {
// HI-4: cap loop at 256 elements (forcedMask is 32 bytes = 256 bits).
uint32 elemBytes = forceSize / nEl;
uint32 nElCapped = (nEl > 256u) ? 256u : nEl;
for (uint32 e = 0u; e < nElCapped; e++) {
if (signalInfo->forcedMask[e >> 3u] & (uint8)(1u << (e & 7u))) {
memcpy((uint8 *)signalInfo->memoryAddress + e * elemBytes,
signalInfo->forcedValue + e * elemBytes,