57 lines
1.7 KiB
C
57 lines
1.7 KiB
C
// Confirm the &global blocker for forked children: forming a 32-bit &global
|
|
// pointer reads DP $BE for the bank byte, and a forked child's fresh DP has $BE
|
|
// uninitialized (crt0 sets it only for the main thread). So a child that takes
|
|
// the address of a global and dereferences it through a real pointer should read
|
|
// from the WRONG bank -> garbage (or crash), unless/until a fork thunk sets $BE.
|
|
// Passing &gCanary through a noinline fn forces a genuine 32-bit pointer (not a
|
|
// near/absolute access the compiler could fold).
|
|
//
|
|
// 0x025060 0001 child entered
|
|
// 0x025062 xxxx value read back through &gCanary (0x1234 == $BE correct)
|
|
// 0x025064 E0DD child reached end (no crash)
|
|
// 0x025066 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 volatile uint16_t gCanary = 0x1234;
|
|
|
|
|
|
static uint16_t __attribute__((noinline)) deref16(volatile uint16_t *p) {
|
|
return *p; // deref a full 32-bit pointer
|
|
}
|
|
|
|
|
|
static void child(void) {
|
|
uint16_t v;
|
|
M(0x025060UL) = 0x0001;
|
|
v = deref16((volatile uint16_t *)&gCanary); // &global -> uses $BE bank byte
|
|
M(0x025062UL) = v; // 0x1234 if $BE right; else garbage
|
|
M(0x025064UL) = 0xE0DD;
|
|
_exit(0);
|
|
}
|
|
|
|
|
|
int main(void) {
|
|
int pid;
|
|
int st;
|
|
|
|
M(0x025060UL) = BAD;
|
|
M(0x025062UL) = BAD;
|
|
M(0x025064UL) = BAD;
|
|
M(0x025066UL) = BAD;
|
|
|
|
pid = fork((void *)child);
|
|
if (pid > 0) {
|
|
wait(&st);
|
|
M(0x025066UL) = 0x5050;
|
|
}
|
|
for (volatile unsigned long j = 0; j < 300000UL; j++) {
|
|
}
|
|
return 0;
|
|
}
|