Created
May 20, 2019 13:19
-
-
Save tkhai/ce46502fc53580372da35e8c3b7818b9 to your computer and use it in GitHub Desktop.
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> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
/* Check process_vm_mmap() saves sharing and flags */ | |
#define __NR_process_vm_mmap 434 | |
#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(int sk, unsigned long size, pid_t ppid) | |
{ | |
unsigned long addr; | |
void *faddr; | |
if (read(sk, &addr, sizeof(unsigned long)) != sizeof(unsigned long)) { | |
warn("read: %d", __LINE__); | |
goto kill; | |
} | |
faddr = process_vm_mmap(ppid, (unsigned long)addr, size, 0, 0); | |
if (faddr == MAP_FAILED) { | |
warn("process_vm_mmap: %d", __LINE__); | |
goto kill; | |
} | |
if (write(sk, &faddr, sizeof(unsigned long)) != sizeof(unsigned long)) { | |
perror("Can't write"); | |
goto kill; | |
} | |
while (1) | |
sleep(1); | |
return 0; | |
kill: | |
kill(ppid, SIGKILL); | |
return 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; | |
int sk[2]; | |
pid_t ppid, 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; | |
if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sk) < 0) { | |
perror("socketpair"); | |
return -1; | |
} | |
ppid = getpid(); | |
pid = fork(); | |
if (pid < 0) { | |
perror("fork"); | |
return -1; | |
} | |
if (pid == 0) | |
return child(sk[0], size, ppid); | |
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; | |
} | |
/* Notify child about addr */ | |
if (write(sk[1], (void *)&addr, sizeof(unsigned long)) != sizeof(unsigned long)) { | |
perror("write"); | |
goto kill; | |
} | |
/* Wait till child maps addr */ | |
if (read(sk[1], &faddr, sizeof(unsigned long)) != sizeof(unsigned long)) { | |
perror("Can't read"); | |
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); | |
close(sk[0]); | |
close(sk[1]); | |
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