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