70 lines
1.7 KiB
C
70 lines
1.7 KiB
C
// 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 <stdint.h>
|
|
|
|
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;
|
|
}
|