| /* Test different kinds of addressability and definedness */ |
| #include "../memcheck.h" |
| #include <sys/mman.h> |
| #include <stdio.h> |
| #include <sys/resource.h> |
| #include <unistd.h> |
| #include <sys/wait.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <stdlib.h> |
| |
| static int pgsz; |
| |
| static char *mm(char *addr, int size, int prot) |
| { |
| int flags = MAP_PRIVATE | MAP_ANONYMOUS; |
| char *ret; |
| |
| if (addr) |
| flags |= MAP_FIXED; |
| |
| ret = mmap(addr, size, prot, flags, -1, 0); |
| if (ret == (char *)-1) { |
| perror("mmap failed"); |
| exit(1); |
| } |
| |
| return ret; |
| } |
| |
| /* Case 1 - mmaped memory is defined */ |
| static void test1() |
| { |
| char *m = mm(0, pgsz * 5, PROT_READ); |
| |
| VALGRIND_CHECK_READABLE(m, pgsz*5); /* all defined */ |
| } |
| |
| /* Case 2 - unmapped memory is unaddressable+undefined */ |
| static void test2() |
| { |
| char *m = mm(0, pgsz * 5, PROT_READ|PROT_WRITE); |
| VALGRIND_CHECK_READABLE(m, pgsz*5); /* all OK */ |
| |
| munmap(&m[pgsz*2], pgsz); |
| |
| VALGRIND_CHECK_READABLE(&m[pgsz*2], pgsz); /* undefined */ |
| |
| /* XXX need an memcheck/addrcheck request to test addressability */ |
| m[pgsz*2] = 'x'; /* unmapped fault */ |
| } |
| |
| /* Case 3 - memory definedness doesn't survive remapping */ |
| static void test3() |
| { |
| char *m = mm(0, pgsz * 5, PROT_READ|PROT_WRITE); |
| |
| VALGRIND_MAKE_WRITABLE(&m[pgsz], pgsz); |
| mm(&m[pgsz], pgsz, PROT_READ); |
| VALGRIND_CHECK_READABLE(&m[pgsz], pgsz); /* OK */ |
| } |
| |
| /* Case 4 - mprotect doesn't affect addressability */ |
| static void test4() |
| { |
| char *m = mm(0, pgsz * 5, PROT_READ|PROT_WRITE); |
| |
| mprotect(m, pgsz, PROT_WRITE); |
| VALGRIND_CHECK_READABLE(m, pgsz); /* OK */ |
| m[44] = 'y'; /* OK */ |
| |
| mprotect(m, pgsz*5, PROT_NONE); |
| m[55] = 'x'; /* permission fault, but no tool complaint */ |
| } |
| |
| /* Case 5 - mprotect doesn't affect definedness */ |
| static void test5() |
| { |
| char *m = mm(0, pgsz * 5, PROT_READ|PROT_WRITE); |
| |
| VALGRIND_MAKE_WRITABLE (m, pgsz*5); |
| memset(m, 'x', 10); |
| VALGRIND_CHECK_READABLE(m, 10); /* OK */ |
| VALGRIND_CHECK_READABLE(m+10, 10); /* BAD */ |
| |
| mprotect(m, pgsz*5, PROT_NONE); |
| mprotect(m, pgsz*5, PROT_READ); |
| |
| VALGRIND_CHECK_READABLE(m, 10); /* still OK */ |
| VALGRIND_CHECK_READABLE(m+20, 10); /* BAD */ |
| } |
| |
| static struct test { |
| void (*test)(void); |
| int sig; |
| } tests[] = { |
| { test1, 0 }, |
| { test2, SIGSEGV }, |
| { test3, 0 }, |
| { test4, SIGSEGV }, |
| { test5, 0 }, |
| }; |
| static const int n_tests = sizeof(tests)/sizeof(*tests); |
| |
| int main() |
| { |
| static const struct rlimit zero = { 0, 0 }; |
| int i; |
| |
| pgsz = getpagesize(); |
| setvbuf(stdout, NULL, _IOLBF, 0); |
| |
| setrlimit(RLIMIT_CORE, &zero); |
| |
| for(i = 0; i < n_tests; i++) { |
| int pid; |
| |
| pid = fork(); |
| if (pid == -1) { |
| perror("fork"); |
| exit(1); |
| } |
| if (pid == 0) { |
| (*tests[i].test)(); |
| exit(0); |
| } else { |
| int status; |
| int ret; |
| |
| printf("Test %d: ", i+1); |
| fflush(stdout); |
| |
| while((ret = waitpid(pid, &status, 0)) != pid) { |
| if (errno != EINTR) { |
| perror("waitpid"); |
| exit(1); |
| } |
| } |
| if (WIFSIGNALED(status)) { |
| assert(WTERMSIG(status) != 0); |
| |
| if (WTERMSIG(status) == tests[i].sig) |
| printf("PASS\n"); |
| else |
| printf("died with unexpected signal %d\n", |
| WTERMSIG(status)); |
| } else if (WIFEXITED(status)) { |
| if (WEXITSTATUS(status) == 0) { |
| if (tests[i].sig == 0) |
| printf("PASS\n"); |
| else |
| printf("exited without expected signal %d\n", |
| tests[i].sig); |
| } else |
| printf("exited with unexpected status %d\n", |
| WEXITSTATUS(status)); |
| } else { |
| printf("strange status %x?\n", status); |
| } |
| } |
| } |
| exit(0); |
| } |