Skip to content

Instantly share code, notes, and snippets.

@tkhai
Created May 15, 2019 14:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tkhai/f52dbaeedad5a699f3fb386fda676562 to your computer and use it in GitHub Desktop.
Save tkhai/f52dbaeedad5a699f3fb386fda676562 to your computer and use it in GitHub Desktop.
#define _GNU_SOURCE
#include <linux/mman.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <signal.h>
#include <sched.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <errno.h>
#include <err.h>
#include <wait.h>
/* Check process_vm_mmap() merges splitted vmas */
#define __NR_process_vm_mmap 428
#define PVMMAP_FIXED 0x01
#define PAGE_SIZE 4096
#define MAX_SIZE (PAGE_SIZE * 32768 * 16UL * 6)
struct map {
unsigned long start, end, off;
char r, w, x, p;
};
typedef _Bool bool;
static void *process_vm_mmap(pid_t pid, unsigned long src_addr,
unsigned long len,
unsigned long dst_addr,
unsigned long flags)
{
unsigned long ret;
ret = syscall(__NR_process_vm_mmap, pid, src_addr, len, dst_addr, flags);
if (ret < PAGE_SIZE) {
errno = (int)ret;
return MAP_FAILED;
}
return (void *)ret;
}
static int get_maps_line(pid_t pid, unsigned long addr, struct map *map)
{
char buf[64] = { 0 };
char *line = NULL;
size_t n = 0;
int ret = -1;
FILE *fp;
snprintf(buf, sizeof(buf) - 1, "/proc/%d/maps", pid);
fp = fopen(buf, "r");
if (fp == NULL) {
perror("fopen");
goto out;
}
while (getline(&line, &n, fp) != -1) {
if (sscanf(line, "%lx-%lx %c%c%c%c %lx", &map->start, &map->end,
&map->r, &map->w, &map->x, &map->p, &map->off) != 7) {
printf("Can't parse map %s", line);
goto out;
}
if (map->start <= addr && map->end > addr) {
ret = 0;
goto out;
}
}
out:
fclose(fp);
return ret;
}
static int child()
{
while (1)
sleep(1);
}
static int test(bool file, bool shared, bool huge, int map_prot, bool tail,
unsigned long size, unsigned long offset)
{
int status, mmap_flags = 0, fd = -1;
void *addr, *faddr, *addr2 = NULL;
long i, ret, retval = -1;
unsigned long size2;
struct map map;
pid_t pid;
if (file) {
fd = open("/tmp/proc_vm_mmap.test", O_RDWR|O_CREAT);
if (fd < 0) {
perror("open");
return -1;
}
if (ftruncate(fd, size)) {
perror("ftruncate");
return -1;
}
} else {
mmap_flags |= MAP_ANONYMOUS;
}
if (shared)
mmap_flags |= MAP_SHARED;
else
mmap_flags |= MAP_PRIVATE;
if (huge)
mmap_flags |= MAP_HUGE_2MB;
pid = fork();
if (pid < 0) {
perror("fork");
return -1;
}
if (pid == 0)
return child();
addr = mmap(0, size, map_prot, mmap_flags, fd, 0);
if (addr == MAP_FAILED) {
perror("mmap\n");
goto kill;
}
if (huge) {
ret = madvise(addr, size, MADV_HUGEPAGE);
if (ret) {
perror("madvise");
goto kill;
}
}
faddr = process_vm_mmap(-pid, (unsigned long)addr, size, 0, 0);
if (faddr == MAP_FAILED) {
warn("process_vm_mmap: %d", __LINE__);
goto kill;
}
if (tail) {
size2 = size-offset;
addr2 = addr + offset;
faddr += offset;
} else {
size2 = offset;
addr2 = addr;
}
munmap(addr2, size2);
addr2 = process_vm_mmap(pid, (unsigned long)faddr, size2,
(unsigned long)addr2, PVMMAP_FIXED);
if (addr2 == MAP_FAILED) {
warn("process_vm_mmap: %d", __LINE__);
goto kill;
}
ret = get_maps_line(getpid(), (unsigned long)addr, &map);
if (ret) {
printf("Can't find map\n");
goto kill;
}
if ((unsigned long)addr + size > map.end) {
printf("vma splited\n");
goto kill;
}
if (((map.r == 'r') != !!(map_prot & PROT_READ)) ||
((map.w == 'w') != !!(map_prot & PROT_WRITE)) ||
((map.x == 'x') != !!(map_prot & PROT_EXEC)) ||
((map.p == 'p') != !!(mmap_flags & MAP_PRIVATE))) {
printf("flags/prot changed\n");
goto kill;
}
retval = 0;
kill:
printf("file=%d shared=%d huge=%d map_prot=%d tail=%d size=%lu offset=%lu: %s\n",
file, shared, huge, map_prot, tail, size, offset,
retval ? "FAIL" : "OK");
kill(pid, SIGKILL);
if (fd >= 0)
close(fd);
munmap(addr, size);
wait(&status);
return retval;
}
int main()
{
int file, shared, huge, map_prot, tail, ret;
unsigned long size, offset;
/* test(bool file, bool shared, bool huge, int map_prot, bool tail, unsigned long size, unsigned long offset) */
for (file = 0; file <= 1; file++)
for (shared = 0; shared <= 1; shared++)
for (huge = 0; huge <= 1; huge++)
for (tail = 0; tail <= 1; tail++)
for (size = PAGE_SIZE; size <= MAX_SIZE; size <<= 1)
for (offset = PAGE_SIZE; offset < size; offset = (offset << 1)) {
map_prot = PROT_WRITE|PROT_READ;
while (1) {
ret = test(file, shared, huge, map_prot, tail, size, offset);
if (ret < 0)
exit(1);
if (map_prot == PROT_READ|PROT_WRITE|PROT_EXEC)
break;
map_prot |= PROT_EXEC;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment