// Stress the fork stash race (fix for the rapid-double-fork window). The parent // forks FOUR children back-to-back, each a DIFFERENT function that writes its own // signature to its own slot. Without the fork mutex + consumption handshake, a // second fork() would overwrite __gnoPendingChildFn before the first child read // it, so a child would run the WRONG entry -- a slot would be missing or double- // written. With the handshake, fork() blocks until each child has consumed the // stash, so every child runs its intended function. // // 0x025090 AAAA childA ran (else DEAD -> A got the wrong entry) // 0x025092 BBBB childB ran // 0x025094 CCCC childC ran // 0x025096 DDDD childD ran // 0x025098 E0DD parent reaped all four #include extern int fork(void *subr); extern int wait(int *status); extern __attribute__((noreturn)) void _exit(int status); #define M(a) (*(volatile uint16_t *)(a)) static void childA(void) { M(0x025090UL) = 0xAAAA; _exit(0); } static void childB(void) { M(0x025092UL) = 0xBBBB; _exit(0); } static void childC(void) { M(0x025094UL) = 0xCCCC; _exit(0); } static void childD(void) { M(0x025096UL) = 0xDDDD; _exit(0); } int main(void) { int st; M(0x025090UL) = 0xDEAD; M(0x025092UL) = 0xDEAD; M(0x025094UL) = 0xDEAD; M(0x025096UL) = 0xDEAD; M(0x025098UL) = 0xDEAD; fork((void *)childA); // back-to-back: stresses the stash window fork((void *)childB); fork((void *)childC); fork((void *)childD); wait(&st); wait(&st); wait(&st); wait(&st); M(0x025098UL) = 0xE0DD; for (volatile unsigned long j = 0; j < 300000UL; j++) { } return 0; }