63 lines
1.9 KiB
C
63 lines
1.9 KiB
C
// Pin down the LAST forked-child blocker: an indirect call (function pointer).
|
|
// clang routes indirect calls through __jsl_indir, whose target slot is selected
|
|
// by a single global self-modifying operand __jsl_indir_op that crt0 patches to
|
|
// the MAIN thread's DP+$B8. A forked child stores its target to its OWN (fresh)
|
|
// DP+$B8, but __jsl_indir still reads through the parent's operand -> the child's
|
|
// indirect call dispatches through the WRONG direct page. gFp is volatile so the
|
|
// call is a genuine indirect dispatch, not a devirtualized direct call.
|
|
//
|
|
// 0x025060 0001 child entered
|
|
// 0x025062 BEEF child is ABOUT to make the indirect call (crash localizer)
|
|
// 0x025064 0001 gFp(41)==42 (indirect call returned correctly)
|
|
// 0x025066 E0DD child reached end (no crash/wild-jump)
|
|
// 0x025068 5050 parent reaped the child
|
|
#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))
|
|
#define BAD 0xDEAD
|
|
|
|
|
|
static uint16_t addOne(uint16_t x) {
|
|
return x + 1;
|
|
}
|
|
|
|
|
|
static uint16_t (*volatile gFp)(uint16_t) = addOne;
|
|
|
|
|
|
static void child(void) {
|
|
uint16_t r;
|
|
M(0x025060UL) = 0x0001;
|
|
M(0x025062UL) = 0xBEEF; // reached the indirect-call site
|
|
r = gFp(41); // indirect dispatch via __jsl_indir
|
|
if (r == 42) {
|
|
M(0x025064UL) = 0x0001;
|
|
}
|
|
M(0x025066UL) = 0xE0DD; // survived the indirect call
|
|
_exit(0);
|
|
}
|
|
|
|
|
|
int main(void) {
|
|
int pid;
|
|
int st;
|
|
|
|
M(0x025060UL) = BAD;
|
|
M(0x025062UL) = BAD;
|
|
M(0x025064UL) = BAD;
|
|
M(0x025066UL) = BAD;
|
|
M(0x025068UL) = BAD;
|
|
|
|
pid = fork((void *)child);
|
|
if (pid > 0) {
|
|
wait(&st);
|
|
M(0x025068UL) = 0x5050;
|
|
}
|
|
for (volatile unsigned long j = 0; j < 300000UL; j++) {
|
|
}
|
|
return 0;
|
|
}
|