aboutsummaryrefslogtreecommitdiff
path: root/semestr-5/so/lista2/so21_lista_2/demand.c
diff options
context:
space:
mode:
Diffstat (limited to 'semestr-5/so/lista2/so21_lista_2/demand.c')
-rw-r--r--semestr-5/so/lista2/so21_lista_2/demand.c83
1 files changed, 83 insertions, 0 deletions
diff --git a/semestr-5/so/lista2/so21_lista_2/demand.c b/semestr-5/so/lista2/so21_lista_2/demand.c
new file mode 100644
index 0000000..d4ae715
--- /dev/null
+++ b/semestr-5/so/lista2/so21_lista_2/demand.c
@@ -0,0 +1,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;
+}