Created
May 15, 2019 14:56
This file contains 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
#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() saves sharing and flags */ | |
#define __NR_process_vm_mmap 428 | |
#define PVMMAP_FIXED 0x01 | |
#define PAGE_SIZE 4096 | |
#define MAX_SIZE (PAGE_SIZE * 32768 * 16UL / 4) | |
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 child() | |
{ | |
while (1) | |
sleep(1); | |
} | |
static int test(bool file, bool shared, bool huge, int map_prot, bool populate, | |
unsigned long size, unsigned long offset, bool unaligned) | |
{ | |
int status, mmap_flags = 0, pvm_mmap_flags = 0, fd = -1; | |
void *addr, *faddr, *addr2 = NULL; | |
long i, ret, retval = -1; | |
unsigned long size2; | |
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; | |
} | |
} | |
if (populate) { | |
for (i = 0; i < size; i += PAGE_SIZE) | |
*(unsigned long *)(addr + i) = (unsigned long)addr + i; | |
} | |
faddr = process_vm_mmap(-pid, (unsigned long)addr, size, 0, 0); | |
if (faddr == MAP_FAILED) { | |
warn("process_vm_mmap: %d", __LINE__); | |
goto kill; | |
} | |
if (unaligned) { | |
pvm_mmap_flags |= PVMMAP_FIXED; | |
/* Find a place for addr2 */ | |
addr2 = mmap(0, size + PAGE_SIZE, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); | |
if (addr2 == MAP_FAILED) { | |
perror("mmap addr2"); | |
goto kill; | |
} | |
munmap(addr2, size + PAGE_SIZE); | |
if (((unsigned long)addr2 & PAGE_SIZE) == 0) | |
addr2 += PAGE_SIZE; | |
} else { | |
addr2 = NULL; | |
} | |
size2 = size - offset; | |
addr2 = process_vm_mmap(pid, (unsigned long)faddr + offset, size2, | |
(unsigned long)addr2, pvm_mmap_flags); | |
if (addr2 == MAP_FAILED) { | |
warn("process_vm_mmap: %d", __LINE__); | |
goto kill; | |
} | |
if (memcmp(addr + offset, addr2, size2)) { | |
printf("not equal: %d\n", __LINE__); | |
goto kill; | |
} | |
for (i = 0; i < size2; i += PAGE_SIZE) { | |
bool is_shared; | |
*(unsigned long *)(addr2 + i) = (unsigned long)addr2 + i; | |
is_shared = (*(unsigned long *)(addr + offset + i) == *(unsigned long *)(addr2 + i)); | |
if (shared != is_shared) { | |
printf("sharing changed\n"); | |
goto kill; | |
} | |
} | |
retval = 0; | |
kill: | |
printf("file=%d shared=%d huge=%d map_prot=%d populate=%d size=%lu offset=%lu unaligned=%d: %s\n", | |
file, shared, huge, map_prot, populate, size, offset, unaligned, | |
retval ? "FAIL" : "OK"); | |
kill(pid, SIGKILL); | |
if (fd >= 0) | |
close(fd); | |
munmap(addr2, size2); | |
munmap(addr, size); | |
wait(&status); | |
return retval; | |
} | |
int main() | |
{ | |
int file, shared, huge, map_prot, populate, unaligned, ret; | |
unsigned long size, offset; | |
/* test(bool file, bool shared, bool huge, int map_prot, bool populate, unsigned long size, unsigned long offset, bool unaligned) */ | |
for (file = 0; file <= 1; file++) | |
for (shared = 0; shared <= 1; shared++) | |
for (huge = 0; huge <= 1; huge++) | |
for (populate = 0; populate <= 1; populate++) | |
for (unaligned = 0; unaligned <= 1; unaligned++) | |
for (size = PAGE_SIZE; size <= MAX_SIZE; size <<= 1) | |
for (offset = 0; offset < size; offset = (offset << 1) ? : PAGE_SIZE) { | |
map_prot = PROT_WRITE|PROT_READ; | |
while (1) { | |
ret = test(file, shared, huge, map_prot, populate, size, offset, unaligned); | |
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