Skip to content

Commit 7199292

Browse files
committed
fix(rma): add peer bounds checks and harden bootstrap/test robustness
1 parent 31b4b78 commit 7199292

5 files changed

Lines changed: 64 additions & 8 deletions

File tree

flagcx/core/flagcx_hetero.cc

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <sched.h>
1515
#include <stdlib.h>
1616
#include <string.h>
17+
#include <time.h>
1718
#include <unistd.h>
1819

1920
#ifndef FLAGCX_RMA_QUEUE_SIZE
@@ -51,17 +52,29 @@ struct flagcxRmaDoneWaitCtx {
5152
volatile uint64_t *doneSeqsCpu; // pointer to proxy's doneSeqsCpu[peer]
5253
uint64_t opSeq; // sequence to wait for
5354
volatile int *rmaError; // proxy error flag
55+
pthread_mutex_t *doneMutex; // proxy done condvar mutex
56+
pthread_cond_t *doneCond; // proxy done condvar
5457
};
5558

5659
// Host-func callback: blocks stream until proxy signals completion.
60+
// Uses condvar to sleep instead of spinning — zero CPU while waiting.
5761
static void flagcxRmaDoneWaitHostFunc(void *arg) {
5862
struct flagcxRmaDoneWaitCtx *ctx = (struct flagcxRmaDoneWaitCtx *)arg;
63+
pthread_mutex_lock(ctx->doneMutex);
5964
while (__atomic_load_n(ctx->doneSeqsCpu, __ATOMIC_ACQUIRE) < ctx->opSeq) {
6065
if (__atomic_load_n(ctx->rmaError, __ATOMIC_ACQUIRE)) {
6166
break;
6267
}
63-
sched_yield();
68+
struct timespec ts;
69+
clock_gettime(CLOCK_REALTIME, &ts);
70+
ts.tv_nsec += 1000000; // 1ms timeout as safety net
71+
if (ts.tv_nsec >= 1000000000) {
72+
ts.tv_sec++;
73+
ts.tv_nsec -= 1000000000;
74+
}
75+
pthread_cond_timedwait(ctx->doneCond, ctx->doneMutex, &ts);
6476
}
77+
pthread_mutex_unlock(ctx->doneMutex);
6578
free(ctx);
6679
}
6780

@@ -96,14 +109,16 @@ static flagcxResult_t flagcxRmaWaitDone(struct flagcxRmaProxyState *proxy,
96109
return deviceAdaptor->streamWaitValue64(stream, &proxy->doneSeqsDev[peer],
97110
opSeq, 0);
98111
} else {
99-
// HOST_FUNC: launch callback that polls doneSeqsCpu until done
112+
// HOST_FUNC: launch callback that waits on doneCond until done
100113
struct flagcxRmaDoneWaitCtx *ctx =
101114
(struct flagcxRmaDoneWaitCtx *)malloc(sizeof(*ctx));
102115
if (ctx == NULL)
103116
return flagcxSystemError;
104117
ctx->doneSeqsCpu = &proxy->doneSeqsCpu[peer];
105118
ctx->opSeq = opSeq;
106119
ctx->rmaError = &proxy->rmaError;
120+
ctx->doneMutex = &proxy->doneMutex;
121+
ctx->doneCond = &proxy->doneCond;
107122
return deviceAdaptor->launchHostFunc(stream, flagcxRmaDoneWaitHostFunc,
108123
ctx);
109124
}
@@ -313,6 +328,12 @@ flagcxRmaProxyPollNonPersistCompletion(struct flagcxRmaProxyState *proxy,
313328
__atomic_store_n(&proxy->doneSeqsCpu[peer], desc->opSeq,
314329
__ATOMIC_RELEASE);
315330
__atomic_fetch_add(&proxy->completionCount, 1ULL, __ATOMIC_RELEASE);
331+
// Wake HOST_FUNC waiters sleeping on doneCond
332+
if (!proxy->useStreamOps) {
333+
pthread_mutex_lock(&proxy->doneMutex);
334+
pthread_cond_broadcast(&proxy->doneCond);
335+
pthread_mutex_unlock(&proxy->doneMutex);
336+
}
316337
}
317338
free(desc);
318339
did = true;
@@ -570,6 +591,9 @@ flagcxResult_t flagcxHeteroRmaProxyStart(flagcxHeteroComm_t comm) {
570591
flagcxIntruQueueConstruct(&proxy->inProgressQueues[p]);
571592
}
572593

594+
pthread_mutex_init(&proxy->doneMutex, NULL);
595+
pthread_cond_init(&proxy->doneCond, NULL);
596+
573597
// Allocate device memory for stream-based synchronization.
574598
proxy->doneSeqsDev = NULL;
575599
proxy->doneSeqsCpu = NULL;
@@ -659,6 +683,8 @@ flagcxResult_t flagcxHeteroRmaProxyStart(flagcxHeteroComm_t comm) {
659683
deviceAdaptor->gdrMemFree(proxy->readySeqsDev, NULL);
660684
for (int p = 0; p < nRanks; p++)
661685
pthread_mutex_destroy(&proxy->peerProducerMutexes[p]);
686+
pthread_cond_destroy(&proxy->doneCond);
687+
pthread_mutex_destroy(&proxy->doneMutex);
662688
free(proxy->circularBuffers);
663689
free((void *)proxy->pis);
664690
free((void *)proxy->cis);
@@ -691,6 +717,9 @@ flagcxResult_t flagcxHeteroRmaProxyStop(flagcxHeteroComm_t comm) {
691717
for (int p = 0; p < proxy->nRanks; p++)
692718
pthread_mutex_destroy(&proxy->peerProducerMutexes[p]);
693719

720+
pthread_cond_destroy(&proxy->doneCond);
721+
pthread_mutex_destroy(&proxy->doneMutex);
722+
694723
// Free CPU mappings / host memory
695724
if (proxy->useStreamOps && deviceAdaptor->gdrPtrMunmap != NULL) {
696725
if (proxy->doneSeqsCpu != NULL)

flagcx/core/include/flagcx_hetero.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ struct flagcxRmaProxyState {
9797
bool ipcInitFailed; // true if IpcInit was attempted and failed (prevents
9898
// retries)
9999

100+
// Condition variable for HOST_FUNC done-wait: proxy thread broadcasts
101+
// after updating doneSeqsCpu so host-func callbacks wake without spinning.
102+
pthread_mutex_t doneMutex;
103+
pthread_cond_t doneCond;
104+
100105
pthread_t thread;
101106
volatile int stop;
102107
};

flagcx/flagcx.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2592,6 +2592,11 @@ flagcxResult_t flagcxPutSignal(const void *localbuff, size_t count,
25922592
return flagcxInvalidArgument;
25932593
if (peerWin == NULL)
25942594
return flagcxInvalidArgument;
2595+
if (peer < 0 || peer >= comm->heteroComm->nRanks) {
2596+
WARN("flagcxPutSignal: peer %d out of range (nRanks=%d)", peer,
2597+
comm->heteroComm->nRanks);
2598+
return flagcxInvalidArgument;
2599+
}
25952600

25962601
// Resolve window to MR index and compute byte size
25972602
size_t byteSize = count * getFlagcxDataTypeSize(datatype);
@@ -2638,6 +2643,11 @@ flagcxResult_t flagcxSignal(int peer, unsigned int flags, flagcxComm_t comm,
26382643
(void)flags;
26392644
if (comm == NULL || comm->heteroComm == NULL)
26402645
return flagcxInvalidArgument;
2646+
if (peer < 0 || peer >= comm->heteroComm->nRanks) {
2647+
WARN("flagcxSignal: peer %d out of range (nRanks=%d)", peer,
2648+
comm->heteroComm->nRanks);
2649+
return flagcxInvalidArgument;
2650+
}
26412651

26422652
// Sender writes to its own slot in receiver's signal buffer
26432653
size_t signalOffset = (size_t)comm->heteroComm->rank * sizeof(uint64_t);
@@ -2663,6 +2673,11 @@ flagcxResult_t flagcxWaitSignal(int nDesc,
26632673

26642674
for (int i = 0; i < nDesc; i++) {
26652675
int peer = signalDescs[i].peer;
2676+
if (peer < 0 || peer >= hetero->nRanks) {
2677+
WARN("flagcxWaitSignal: peer %d out of range (nRanks=%d)", peer,
2678+
hetero->nRanks);
2679+
return flagcxInvalidArgument;
2680+
}
26662681
uint64_t opCnt = (uint64_t)signalDescs[i].opCnt;
26672682

26682683
// Signal offset: peer's slot in my local signal buffer

flagcx/service/bootstrap.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,15 @@ static flagcxResult_t bootstrapNetSendRecv(struct flagcxSocket *sendSocket,
139139
int recvExpectedSize;
140140
FLAGCXCHECK(flagcxSocketSendRecv(sendSocket, &sendSize, sizeof(int),
141141
recvSocket, &recvExpectedSize, sizeof(int)));
142-
if (recvExpectedSize > recvSize) {
143-
WARN("bootstrapNetSendRecv: message truncated: received %d bytes instead "
144-
"of %d",
142+
if (recvExpectedSize < 0 || recvExpectedSize > recvSize) {
143+
WARN("bootstrapNetSendRecv: invalid recvExpectedSize %d (recvSize=%d)",
145144
recvExpectedSize, recvSize);
146145
return flagcxInternalError;
147146
}
148147
// Exchange payload (interleaved, handles arbitrary sizes)
149148
FLAGCXCHECK(flagcxSocketSendRecv(sendSocket, sendData, sendSize, recvSocket,
150-
recvData, recvExpectedSize));
149+
recvData,
150+
std::min(recvSize, recvExpectedSize)));
151151
return flagcxSuccess;
152152
}
153153

test/unittest/rma/rma_test.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ void RmaTest::SetUpTestSuite() {
1717
MPI_Comm_size(MPI_COMM_WORLD, &nranks);
1818

1919
size = RMA_TEST_SIZE;
20-
signalSize = sizeof(uint64_t) * 64; // room for multiple signals
20+
signalSize = sizeof(uint64_t) * nranks;
2121

2222
flagcxDeviceHandleInit(&devHandle);
2323

@@ -56,7 +56,14 @@ void RmaTest::SetUpTestSuite() {
5656
// Allocate and register signal buffer
5757
flagcxMemAlloc(&signalBuff, signalSize);
5858
devHandle->deviceMemset(signalBuff, 0, signalSize, flagcxMemDevice, nullptr);
59-
flagcxOneSideSignalRegister(comm, signalBuff, signalSize, FLAGCX_PTR_CUDA);
59+
res = flagcxOneSideSignalRegister(comm, signalBuff, signalSize,
60+
FLAGCX_PTR_CUDA);
61+
if (res != flagcxSuccess) {
62+
flagcxMemFree(signalBuff);
63+
signalBuff = nullptr;
64+
dataWin = nullptr;
65+
return;
66+
}
6067
}
6168

6269
void RmaTest::TearDownTestSuite() {

0 commit comments

Comments
 (0)