/* * reproducer for https://reviews.freebsd.org/D14347 * * TLB invalidation for COW must not violate memory ordering */ #include #include #include #include #include #include #include #include struct foo { long x; char pad[512]; int y; }; static pthread_mutex_t mutex; static void lock(void) { int error; error = pthread_mutex_lock(&mutex); if (error) errc(1, error, "lock@%p", __builtin_return_address(0)); } static void unlock(void) { int error; error = pthread_mutex_unlock(&mutex); if (error) errc(1, error, "lock@%p", __builtin_return_address(0)); } static void * __dead a(void *cookie) { volatile struct foo *f = cookie; for (;; pthread_testcancel()) f->y = 1; } static void * __dead b(void *cookie) { volatile struct foo *f = cookie; long x; for (x = 0;; x++, pthread_testcancel()) { lock(); f->x = x; unlock(); } } static void fence_it_up(void) { int eax, ebx, ecx, edx; asm volatile("xorl %%eax,%%eax; cpuid; pause" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)); } static void * __dead c(void *cookie) { volatile struct foo *f = cookie; long x0, x1; for (;; pthread_testcancel()) { lock(); x0 = f->x; fence_it_up(); x1 = f->x; unlock(); if (x0 != x1) errx(1, "%ld != %ld", x0, x1); } } static void cow_it_up(void) { long rax = 2; /* SYS_FORK */ asm volatile( " syscall\n" " cmpq $0,%%rax\n" " jne 1f\n" " movq $0,%%rdi\n" /* child */ " movq $1,%%rax\n" /* SYS_EXIT */ " syscall\n" " hlt\n" "1:" : /*output*/ "+a"(rax) : /*input*/ : /*clobber*/ "memory"); if (rax == -1) err(1, "fork"); if (waitpid(rax, NULL, 0) == -1) err(1, "waitpid"); } int main(void) { static void *(*f[])(void *) = {a, b, c}; pthread_t t[__arraycount(f)]; size_t pagesize = sysconf(_SC_PAGESIZE); void *p; unsigned i; int error; assert(pagesize >= sizeof(struct foo)); p = mmap(/*addr*/NULL, pagesize, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, /*fd*/-1, /*offset*/0); if (p == MAP_FAILED) err(1, "mmap"); error = pthread_mutex_init(&mutex, NULL); if (error) errc(1, error, "pthread_mutex_init"); for (i = 0; i < __arraycount(f); i++) { error = pthread_create(&t[i], NULL, f[i], p); if (error) errc(1, error, "pthread_create %u", i); } for (i = 0; i < 100000; i++) { if ((i % 10000) == 0) fprintf(stderr, "%u\n", i); cow_it_up(); } for (i = 0; i < __arraycount(f); i++) { fprintf(stderr, "cancel %u\n", i); error = pthread_cancel(t[i]); if (error) errc(1, error, "pthread_cancel %u", i); } for (i = 0; i < __arraycount(f); i++) { fprintf(stderr, "join %u\n", i); error = pthread_join(t[i], NULL); if (error) errc(1, error, "pthread_join %u", i); } error = pthread_mutex_destroy(&mutex); if (error) errc(1, error, "pthread_mutex_destroy"); if (munmap(p, pagesize) == -1) err(1, "munmap"); return 0; }