1/* $OpenBSD: test-close.c,v 1.1 2020/06/29 18:25:37 anton Exp $ */ 2 3/* 4 * Copyright (c) 2019 Anton Lindqvist <anton@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <err.h> 20#include <pthread.h> 21#include <signal.h> 22#include <unistd.h> 23 24#include "pipe.h" 25 26struct context { 27 volatile sig_atomic_t *c_alive; 28 int c_fd; 29}; 30 31static void *close_thread(void *); 32static void sighandler(int); 33 34static volatile sig_atomic_t alive = 1; 35 36/* 37 * Regression during close(2) causing a use-after-free. 38 * The main thread repeatedly creates a new pipe which two other threads tries 39 * to close. By default, 100 iterations is performed. 40 */ 41int 42test_close_race(void) 43{ 44 pthread_t th1, th2; 45 struct context ctx1, ctx2; 46 int nrounds = 100; 47 int pip[2]; 48 int error; 49 50 if (signal(SIGINT, sighandler) == SIG_ERR) 51 err(1, "signal"); 52 53 ctx1.c_alive = &alive; 54 ctx1.c_fd = 3; 55 error = pthread_create(&th1, NULL, close_thread, &ctx1); 56 if (error) 57 errc(1, error, "pthread_create"); 58 ctx2.c_alive = &alive; 59 ctx2.c_fd = 4; 60 error = pthread_create(&th2, NULL, close_thread, &ctx2); 61 if (error) 62 errc(1, error, "pthread_create"); 63 64 while (alive) { 65 if (!infinity && nrounds-- == 0) 66 alive = 0; 67 68 if (pipe(pip) == -1) 69 err(1, "pipe"); 70 if (pip[0] != 3) 71 close(pip[0]); 72 if (pip[1] != 4) 73 close(pip[1]); 74 } 75 76 error = pthread_join(th1, NULL); 77 if (error) 78 errc(1, error, "pthread_join"); 79 error = pthread_join(th2, NULL); 80 if (error) 81 errc(1, error, "pthread_join"); 82 83 return 0; 84} 85 86static void * 87close_thread(void *arg) 88{ 89 const struct context *ctx = arg; 90 91 while (*ctx->c_alive) 92 close(ctx->c_fd); 93 94 return NULL; 95} 96 97static void 98sighandler(int signo) 99{ 100 101 alive = 0; 102} 103