Here are the programs to reproduce this bug. mprotect.c is based on Xavier's program, but changed to take a path from the command line and to run an infinite loop.
madvise.c is roughly based on Xavier's program, but it exercises the ma dvise(MADV_REMOVE) case. As this operation is only implemented by tmpfs, you need to give it the path to a file on a tmpfs, or on a union filesystem where the rw branch is a tmpfs. On an SMP system they should trigger the bug in under a minute. Ben. -- Ben Hutchings Nothing is ever a complete failure; it can always serve as a bad example.
#include <pthread.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <stdio.h> #include <malloc.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <linux/limits.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/wait.h> #define handle_error_en(en, msg) \ do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0) #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0) static char *open_map(const char *path, int no, int size) { int fd = -1; int flags = MAP_SHARED; char *addr; fd = open(path, O_CREAT|O_RDWR, 0600); if (fd < 0) { handle_error("open"); } if (ftruncate(fd, size) < 0) { handle_error("ftruncate"); } /* Allocate a buffer aligned on a page boundary; initial protection is PROT_READ | PROT_WRITE */ addr = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, fd, 0); if (addr == MAP_FAILED) { handle_error("mmap"); } close(fd); return addr; } int len = 1 << 12; static void *thread_msync(void *arg) { char *p = arg; if (msync(p, len, MS_SYNC) < 0) { handle_error("msync"); } return NULL; } #define NB_THREADS 10000 int main(int argc, char *argv[]) { char *addr; pthread_t threads[NB_THREADS]; addr = open_map(argv[1], 0, 256 << 12); printf("Start of region: 0x%lx\n", (long)addr); for (;;) { for (int i = 0; i < NB_THREADS; i++) { int s; int page = rand() % 256; char *p = addr + (page << 12); if (mprotect(p, len, PROT_READ | PROT_WRITE) == -1) { handle_error("mprotect"); } memset(p, 'a', len / 2); memset(p + len / 2, 'b', len / 2); if (mprotect(p, len, PROT_READ) == -1) { handle_error("mprotect"); } s = pthread_create(&threads[i], NULL, &thread_msync, p); if (s) { handle_error_en(s, "pthread_create"); } } for (int i = 0; i < NB_THREADS; i++) { pthread_join(threads[i], NULL); } } }
#include <errno.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <sys/fcntl.h> #include <sys/mman.h> #include <unistd.h> #define handle_error_en(en, msg) \ do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0) #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0) #define PAGE_SIZE (1 << 12) #define MAP_SIZE (PAGE_SIZE << 8) #define NB_THREADS 2 static int fd; static char *addr; static void *thread1(void *arg) { for (;;) { int page = rand() % 256; char *p = addr + page * PAGE_SIZE; madvise(p, PAGE_SIZE, MADV_REMOVE); } return NULL; } static void *thread2(void *arg) { for (unsigned int i = 0; ; i++) { if (i & 1) munmap(addr, MAP_SIZE); else mmap(addr, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); } return NULL; } int main(int argc, char **argv) { pthread_t threads[NB_THREADS]; int s; fd = open(argv[1], O_CREAT|O_RDWR, 0600); if (fd < 0) { handle_error("open"); } if (ftruncate(fd, MAP_SIZE) < 0) { handle_error("ftruncate"); } addr = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (addr == MAP_FAILED) { handle_error("mmap"); } s = pthread_create(&threads[0], NULL, &thread1, NULL); if (s) { handle_error_en(s, "pthread_create"); } s = pthread_create(&threads[1], NULL, &thread2, NULL); if (s) { handle_error_en(s, "pthread_create"); } for (int i = 0; i < NB_THREADS; i++) { pthread_join(threads[i], NULL); } if (munmap(addr, MAP_SIZE) == -1) { handle_error("munmap"); } printf("Closed region: 0x%lx\n", (long)addr); exit(EXIT_SUCCESS); }
signature.asc
Description: This is a digitally signed message part
------------------------------------------------------------------------------