Created
July 7, 2022 17:06
-
-
Save promovicz/65f22c417cab7af13d2addaaa00988a8 to your computer and use it in GitHub Desktop.
Automatically map unmapped memory
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This little program uses a SIGSEGV handler to auto-map pages as they are used. | |
// If you want you can use this to program without a memory allocator. | |
// gcc -std=c11 -Wall -Wextra -g -o allmem allmem.c /usr/lib/x86_64-linux-gnu/libsigsegv.a | |
#define _GNU_SOURCE 1 | |
#include <stddef.h> | |
#include <stdint.h> | |
#include <errno.h> | |
#include <fcntl.h> | |
#include <limits.h> | |
#include <pthread.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <sys/mman.h> | |
#include <sys/stat.h> | |
#include <sys/types.h> | |
#include <sigsegv.h> | |
/* Structure describing a VM mapping */ | |
typedef struct { | |
uintptr_t map_start; | |
uintptr_t map_end; | |
int map_prot; | |
int map_flags; | |
const char *map_path; | |
} vmscan_map_t; | |
/* Callback for vmscan */ | |
typedef int (*vmscan_fun_t)(void *ctx, const vmscan_map_t *map); | |
/* Linux implementation */ | |
#ifdef __linux__ | |
static int vmscan_linux_line(void *ctx, vmscan_fun_t fun, const char *line) { | |
int ret = -1, res; | |
vmscan_map_t map; | |
char flag[4]; char path[PATH_MAX]; | |
/* scan the line */ | |
res = sscanf(line, "%16zx-%16zx %4c %*s %*s %*[0-9 ]%[^\n]", | |
&map.map_start, &map.map_end, flag, path); | |
if(res < 3) { | |
goto out; | |
} | |
/* gather information */ | |
map.map_prot = 0; | |
map.map_flags = 0; | |
map.map_path = NULL; | |
if(res == 4) { | |
map.map_path = path; | |
} | |
if(flag[0]=='r') map.map_prot |= PROT_READ; | |
if(flag[1]=='w') map.map_prot |= PROT_WRITE; | |
if(flag[2]=='x') map.map_prot |= PROT_EXEC; | |
if(flag[3]=='s') map.map_flags |= MAP_SHARED; | |
if(flag[3]=='p') map.map_flags |= MAP_PRIVATE; | |
/* nothing more to fail */ | |
ret = 0; | |
/* user callback */ | |
if(fun) { | |
ret = fun(ctx, &map); | |
} | |
out: | |
/* done */ | |
return ret; | |
} | |
static int vmscan_linux(void *ctx, vmscan_fun_t fun) { | |
int ret = -1, fd = -1; | |
size_t nbuf, ndone; | |
char *cur; char buf[8192]; | |
/* open the file */ | |
fd = open("/proc/self/maps",O_RDONLY); | |
if(fd == -1) { | |
goto out; | |
} | |
/* read loop */ | |
nbuf = 0; | |
do { | |
ssize_t rd; size_t ln; | |
/* read some */ | |
do { | |
rd = read(fd, buf+nbuf, sizeof(buf)-nbuf-1); | |
} while(rd < 0 && errno == EAGAIN); | |
/* read errors */ | |
if(rd < 0) { | |
goto out; | |
} | |
/* end of file */ | |
if(rd == 0) { | |
break; | |
} | |
/* adjust buffer */ | |
nbuf += rd; | |
cur = buf; | |
/* add string sentinel */ | |
buf[nbuf] = 0; | |
/* scan the buffer */ | |
ndone = 0; | |
do { | |
/* scan for newline */ | |
ln = strcspn(cur,"\n"); | |
/* break if not found */ | |
if(cur[ln] != '\n') { | |
break; | |
} | |
/* squash newline */ | |
cur[ln++] = 0; | |
/* process */ | |
ret = vmscan_linux_line(ctx,fun,cur); | |
if(ret) { | |
goto out; | |
} | |
/* advance */ | |
cur += ln; | |
ndone += ln; | |
} while(ndone < nbuf); | |
/* move remaining data to front */ | |
if(ndone) { | |
memmove(buf, buf+ndone, nbuf - ndone); | |
nbuf -= ndone; | |
} | |
} while(nbuf > 0); | |
/* done failing */ | |
ret = 0; | |
out: | |
/* clean up */ | |
if(fd != -1) { | |
close(fd); | |
} | |
/* return */ | |
return ret; | |
} | |
#endif | |
int vmscan_mapped(void *ptr, const vmscan_map_t *map) | |
{ | |
uintptr_t addr = (uintptr_t)ptr; | |
if(addr >= map->map_start && addr < map->map_end) { | |
return 1; | |
} | |
return 0; | |
} | |
int vmscan_print(void *ctx, const vmscan_map_t *map) { | |
FILE *os = ctx ? (FILE*)ctx : stderr; | |
int p = map->map_prot, f = map->map_flags; | |
fprintf(os, "mem 0x%012zx - 0x%012zx %c%c%c%c %s\n", | |
map->map_start, map->map_end, | |
(p&PROT_READ)?'r':'-', | |
(p&PROT_WRITE)?'w':'-', | |
(p&PROT_EXEC)?'x':'-', | |
(f&MAP_PRIVATE)?'p':'-', | |
map->map_path); | |
return 0; | |
} | |
int vmscan(void *ctx, vmscan_fun_t fun) { | |
return vmscan_linux(ctx, fun); | |
} | |
/* Do not allow access to the first page */ | |
#define MEM_MIN (4096) | |
/* Do not allow access above the bar */ | |
#define MEM_MAX (UINTPTR_MAX>>16) | |
#define PAGE_SIZE (4096) | |
#define PAGE_MASK (UINTPTR_MAX&~0xFFF) | |
static int memall_sigsegv (void *ptr, int serious) | |
{ | |
int res; | |
void *new; | |
uintptr_t addr = (uintptr_t)ptr; | |
/* we only care about serious faults */ | |
if(!serious) { | |
return 0; | |
} | |
/* clamp addresses */ | |
if(addr < MEM_MIN) { | |
return 0; | |
} | |
if(addr >= MEM_MAX) { | |
return 0; | |
} | |
/* scan for the page - ignore if mapped */ | |
res = vmscan((void*)addr, vmscan_mapped); | |
if(res) { | |
return 0; | |
} | |
/* page is not mapped - try to map it */ | |
addr &= PAGE_MASK; | |
new = mmap((void*)addr, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); | |
if(!new) { | |
/* we failed - propagate */ | |
return 0; | |
} | |
/* done */ | |
return 1; | |
} | |
static int init(void) { | |
int res; | |
puts("installing handler"); | |
res = sigsegv_install_handler(memall_sigsegv); | |
return res; | |
} | |
int main(int argc, char **argv) { | |
int cyc = 1; | |
if(init()) { | |
abort(); | |
} | |
while(1) { | |
printf("cycle %d\n", cyc); | |
if(cyc == 2) { | |
vmscan(NULL,vmscan_print); | |
} | |
if(cyc == 3) { | |
*((uint32_t*)0x80000000) = 1000; | |
} | |
if(cyc == 4) { | |
vmscan(NULL,vmscan_print); | |
} | |
if(cyc == 5) { | |
*((uint32_t*)0x90000000) = 1000; | |
} | |
if(cyc == 6) { | |
vmscan(NULL,vmscan_print); | |
} | |
if(cyc == 7) { | |
for(int i = 0; i < 100; i++) { | |
uint32_t *pt = (uint32_t*)(0x80000000ll+(i*4096)); | |
*pt = 1000; | |
} | |
} | |
if(cyc == 8) { | |
vmscan(NULL,vmscan_print); | |
} | |
if(cyc == 5) { | |
*((uint32_t*)0x80000000) = 1000; | |
} | |
if(cyc == 10) { | |
vmscan(NULL,vmscan_print); | |
break; | |
} | |
sleep(1); | |
cyc++; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment