Skip to content

Instantly share code, notes, and snippets.

@promovicz
Created July 7, 2022 17:06
Show Gist options
  • Save promovicz/65f22c417cab7af13d2addaaa00988a8 to your computer and use it in GitHub Desktop.
Save promovicz/65f22c417cab7af13d2addaaa00988a8 to your computer and use it in GitHub Desktop.
Automatically map unmapped memory
// 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