aboutsummaryrefslogtreecommitdiff
path: root/semestr-5/so/lista2/so21_lista_2/demand.c
blob: d4ae71503f43ddd902ed685975b7c07d2b4027e8 (plain)
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;
}