Implemented better testing and fixed skipepd frames
This commit is contained in:
@@ -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 ×tamp, 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,
|
||||
|
||||
Reference in New Issue
Block a user