1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
#include "csapp.h"
/* First address of handled region. */
#define ADDR_START ((void *)0x10000000)
/* Last address of handled region (not inclusive). */
#define ADDR_END ((void *)0x10010000)
static size_t pagesize;
/* Maps anonymouse page with `prot` access permissions at `addr` address. */
static void mmap_page(void *addr, int prot) {
Mmap(addr, pagesize, prot, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
}
/* Changes protection bits to `prot` for page at `addr` address. */
static void mprotect_page(void *addr, int prot) {
Mprotect(addr, pagesize, prot);
}
static void sigsegv_handler(int signum, siginfo_t *info, void *data) {
ucontext_t *uc = data;
// intptr_t rip;
/* TODO: You need to get value of instruction pointer register from `uc`.
* Print all useful data from `info` and quit in such a way that a shell
* reports program has been terminated with SIGSEGV. */
#define REG_RIP 16
intptr_t fault_instr = uc->uc_mcontext.gregs[REG_RIP];
intptr_t fault_address = (intptr_t)info->si_addr;
intptr_t fault_page = fault_address / pagesize * pagesize;
safe_printf("Fault at rip=%lx, accessing %lx! ", fault_instr, fault_address);
if ((void *)fault_address < ADDR_START || (void *)fault_address >= ADDR_END) {
safe_printf("Address not mapped - terminating!\n");
_exit(128 + SIGSEGV);
}
switch(info->si_code) {
case SEGV_MAPERR:
safe_printf("Map missing page at %lx.\n", fault_page);
mmap_page((void *)fault_page, PROT_READ);
break;
case SEGV_ACCERR:
safe_printf("Make page at %lx writable.\n", fault_page);
mprotect_page((void *)fault_page, PROT_READ | PROT_WRITE);
break;
default:
safe_printf("Uknkonw code: %d.\n", info->si_code);
_exit(128 + SIGSEGV);
}
}
int main(int argc, char **argv) {
pagesize = sysconf(_SC_PAGESIZE);
/* Register signal handler for SIGSEGV */
struct sigaction action = {.sa_sigaction = sigsegv_handler,
.sa_flags = SA_SIGINFO};
sigaction(SIGSEGV, &action, NULL);
/* Initially all pages in the range are either not mapped or readonly! */
for (void *addr = ADDR_START; addr < ADDR_END; addr += pagesize)
if (random() % 2)
mmap_page(addr, PROT_READ);
/* Generate lots of writes to the region. */
volatile long *array = ADDR_START;
long nelems = (ADDR_END - ADDR_START) / sizeof(long);
for (long i = 0; i < nelems * 2; i++) {
long index = random() % nelems;
array[index] = (long)&array[index];
}
/* Perform off by one access - triggering a real fault! */
array[nelems] = 0xDEADC0DE;
return EXIT_SUCCESS;
}
|