Skip to content

Commit 0ac3fd5

Browse files
committed
Add some test programs for checking platform issues
Signed-off-by: Corey Minyard <[email protected]>
1 parent 0e7848f commit 0ac3fd5

File tree

3 files changed

+359
-0
lines changed

3 files changed

+359
-0
lines changed

checks/README.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
======
2+
checks
3+
======
4+
5+
This directory contains little programs for checking things. They are
6+
just here for posterity, really, though they may be useful for
7+
checking things on new platforms.

checks/ssl_thread_issue.c

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* gensio - A library for abstracting stream I/O
3+
* Copyright (C) 2018 Corey Minyard <[email protected]>
4+
*
5+
* SPDX-License-Identifier: LGPL-2.1-only
6+
*
7+
* This program tests if openssl screws up the signal masks for all
8+
* threads by calling sicprocmask. This may only run under macos, it
9+
* may need fixes for other platforms. Run it with:
10+
*
11+
* DYLD_LIBRARY_PATH=/opt/homebrew/lib ./ssl_thread_issue
12+
*/
13+
#include <stdio.h>
14+
#include <signal.h>
15+
#include <unistd.h>
16+
#include <errno.h>
17+
#include <string.h>
18+
#include <pthread.h>
19+
#include <dlfcn.h>
20+
#include <assert.h>
21+
#include <sys/select.h>
22+
23+
struct cross_thread_info {
24+
int waitfd;
25+
int err;
26+
};
27+
28+
static void *
29+
cross_thread(void *data)
30+
{
31+
struct cross_thread_info *info = data;
32+
sigset_t sigmask, omask;
33+
int rv = 0;
34+
fd_set rfds;
35+
36+
rv = pthread_sigmask(SIG_SETMASK, NULL, &sigmask);
37+
assert(rv == 0);
38+
39+
if (!sigismember(&sigmask, SIGUSR1)) {
40+
fprintf(stderr, "SIGUSR1 not in sigmask 1\n");
41+
info->err = 1;
42+
goto out_err;
43+
}
44+
45+
FD_ZERO(&rfds);
46+
FD_SET(info->waitfd, &rfds);
47+
sigdelset(&sigmask, SIGUSR1);
48+
rv = pthread_sigmask(SIG_SETMASK, &sigmask, &omask);
49+
assert(rv == 0);
50+
51+
rv = pselect(info->waitfd + 1, &rfds, NULL, NULL, NULL, NULL);
52+
assert(rv == 1);
53+
54+
rv = pthread_sigmask(SIG_SETMASK, &omask, &sigmask);
55+
assert(rv == 0);
56+
if (sigismember(&sigmask, SIGUSR1)) {
57+
fprintf(stderr, "SIGUSR1 in sigmask 1\n");
58+
info->err = 4;
59+
goto out_err;
60+
}
61+
62+
out_err:
63+
return NULL;
64+
}
65+
66+
static int
67+
check_pselect_cross_thread(sigset_t mask)
68+
{
69+
int rv = 1;
70+
int pipefds[2] = { -1, -1 };
71+
struct cross_thread_info info;
72+
pthread_t th;
73+
char dummy = 0;
74+
75+
rv = pipe(pipefds);
76+
if (rv == -1) {
77+
perror("pipe");
78+
return 1;
79+
}
80+
81+
info.err = 0;
82+
info.waitfd = pipefds[0];
83+
rv = pthread_create(&th, NULL, cross_thread, &info);
84+
85+
sleep(2); /* Give the thread time to enter pselect() */
86+
87+
if (dlopen("libssl.dylib", RTLD_LAZY | RTLD_GLOBAL) == NULL) {
88+
fprintf(stderr, "dlopen failed: %s\n", dlerror());
89+
rv = 1;
90+
}
91+
92+
write(pipefds[1], &dummy, 1);
93+
94+
pthread_join(th, NULL);
95+
96+
if (!rv)
97+
rv = info.err;
98+
99+
if (!rv)
100+
printf("dlopen does not affect other threads' sigmasks\n");
101+
102+
close(pipefds[0]);
103+
close(pipefds[1]);
104+
return rv;
105+
}
106+
107+
int
108+
main(int argc, char *argv[])
109+
{
110+
sigset_t mask;
111+
int rv;
112+
113+
/* Start with SIGUSR1 blocked. */
114+
rv = sigprocmask(SIG_SETMASK, NULL, &mask);
115+
assert(rv == 0);
116+
sigaddset(&mask, SIGUSR1);
117+
rv = sigprocmask(SIG_SETMASK, &mask, NULL);
118+
assert(rv == 0);
119+
120+
rv = check_pselect_cross_thread(mask);
121+
return rv;
122+
}

checks/test_pselect.c

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/*
2+
* gensio - A library for abstracting stream I/O
3+
* Copyright (C) 2018 Corey Minyard <[email protected]>
4+
*
5+
* SPDX-License-Identifier: LGPL-2.1-only
6+
*
7+
* This program tests the following:
8+
*
9+
* * Is pselect() atomic? In other words, does it apply the signal
10+
* mask and wait for the FDs at the same time. If it applies the
11+
* signal mask then waits for the FDs, there is a window where a
12+
* signal can happen and not wake up the pselect(), so a workaround
13+
* is needed.
14+
*
15+
* * Does sigprocmask() affect all threads, or just the calling
16+
* thread? There is no need right now for a workaround on this, but
17+
* it might be useful debugging information.
18+
*/
19+
#include <stdio.h>
20+
#include <signal.h>
21+
#include <unistd.h>
22+
#include <errno.h>
23+
#include <string.h>
24+
#include <pthread.h>
25+
#include <dlfcn.h>
26+
#include <sys/select.h>
27+
28+
volatile int signalled;
29+
30+
static void
31+
sigusr1_handler(int sig)
32+
{
33+
signalled = 1;
34+
}
35+
36+
static int
37+
check_pselect_atomic(sigset_t mask)
38+
{
39+
fd_set rfds;
40+
int rv = 1;
41+
struct timespec to;
42+
int pipefds[2] = { -1, -1 };
43+
44+
signalled = 0;
45+
rv = pipe(pipefds);
46+
if (rv == -1) {
47+
perror("pipe");
48+
return 1;
49+
}
50+
51+
rv = kill(0, SIGUSR1);
52+
if (rv == -1) {
53+
perror("kill");
54+
goto out;
55+
}
56+
57+
if (signalled) {
58+
fprintf(stderr, "Signal called on blocked signal\n");
59+
rv = 2;
60+
goto out;
61+
}
62+
63+
FD_ZERO(&rfds);
64+
FD_SET(pipefds[0], &rfds);
65+
66+
to.tv_sec = 2;
67+
to.tv_nsec = 0;
68+
sigdelset(&mask, SIGUSR1);
69+
rv = pselect(pipefds[0] + 1, &rfds, NULL, NULL, &to, &mask);
70+
if (rv == 0) {
71+
fprintf(stderr, "Timeout, pselect signal handling is not atomic\n");
72+
rv = 2;
73+
goto out;
74+
} else if (rv == -1) {
75+
if (errno != EINTR) {
76+
fprintf(stderr, "Unknown pselect error: %s\n", strerror(errno));
77+
rv = 1;
78+
goto out;
79+
}
80+
} else {
81+
fprintf(stderr, "Unknown pselect return: %d\n", rv);
82+
rv = 1;
83+
goto out;
84+
}
85+
if (!signalled) {
86+
fprintf(stderr, "Signal didn't happen\n");
87+
rv = 2;
88+
goto out;
89+
}
90+
rv = 0;
91+
92+
printf("pselect is atomic\n");
93+
out:
94+
close(pipefds[0]);
95+
close(pipefds[1]);
96+
return rv;
97+
}
98+
99+
struct cross_thread_info {
100+
int waitfd;
101+
int err;
102+
};
103+
104+
static void *
105+
cross_thread(void *data)
106+
{
107+
struct cross_thread_info *info = data;
108+
sigset_t sigmask, omask;
109+
int rv = 0;
110+
fd_set rfds;
111+
112+
rv = pthread_sigmask(SIG_SETMASK, NULL, &sigmask);
113+
if (rv) {
114+
perror("pthread_sigmask 3");
115+
info->err = 1;
116+
goto out_err;
117+
}
118+
119+
if (!sigismember(&sigmask, SIGUSR1)) {
120+
fprintf(stderr, "SIGUSR1 not in sigmask 1\n");
121+
info->err = 1;
122+
goto out_err;
123+
}
124+
125+
FD_ZERO(&rfds);
126+
FD_SET(info->waitfd, &rfds);
127+
sigdelset(&sigmask, SIGUSR1);
128+
rv = pthread_sigmask(SIG_SETMASK, &sigmask, &omask);
129+
if (rv) {
130+
perror("pthread_sigmask 4");
131+
info->err = 1;
132+
goto out_err;
133+
}
134+
135+
rv = pselect(info->waitfd + 1, &rfds, NULL, NULL, NULL, NULL);
136+
137+
rv = pthread_sigmask(SIG_SETMASK, &omask, &sigmask);
138+
if (rv) {
139+
perror("pthread_sigmask 3");
140+
info->err = 1;
141+
goto out_err;
142+
}
143+
if (sigismember(&sigmask, SIGUSR1)) {
144+
fprintf(stderr, "SIGUSR1 in sigmask 1, sigprocmask affects all threads\n");
145+
info->err = 4;
146+
goto out_err;
147+
}
148+
149+
out_err:
150+
return NULL;
151+
}
152+
153+
static int
154+
check_pselect_cross_thread(sigset_t mask)
155+
{
156+
int rv = 1;
157+
int pipefds[2] = { -1, -1 };
158+
struct cross_thread_info info;
159+
pthread_t th;
160+
char dummy = 0;
161+
162+
signalled = 0;
163+
rv = pipe(pipefds);
164+
if (rv == -1) {
165+
perror("pipe");
166+
return 1;
167+
}
168+
169+
info.err = 0;
170+
info.waitfd = pipefds[0];
171+
rv = pthread_create(&th, NULL, cross_thread, &info);
172+
173+
sleep(2); /* Give the thread time to enter pselect() */
174+
175+
sigaddset(&mask, SIGUSR1);
176+
rv = sigprocmask(SIG_SETMASK, &mask, NULL);
177+
if (rv == -1) {
178+
perror("sigprocmask 4");
179+
rv = 1;
180+
goto out;
181+
}
182+
183+
write(pipefds[1], &dummy, 1);
184+
185+
pthread_join(th, NULL);
186+
187+
if (!rv)
188+
rv = info.err;
189+
190+
if (!rv)
191+
printf("sigprocmask does not affect other threads\n");
192+
out:
193+
close(pipefds[0]);
194+
close(pipefds[1]);
195+
return rv;
196+
}
197+
198+
int
199+
main(int argc, char *argv[])
200+
{
201+
sigset_t mask;
202+
struct sigaction action;
203+
int rv;
204+
205+
/* Start with SIGUSR1 blocked and with a handler. */
206+
rv = sigprocmask(SIG_SETMASK, NULL, &mask);
207+
if (rv == -1) {
208+
perror("sigprocmask 1");
209+
return 1;
210+
}
211+
sigaddset(&mask, SIGUSR1);
212+
rv = sigprocmask(SIG_SETMASK, &mask, NULL);
213+
if (rv == -1) {
214+
perror("sigprocmask 2");
215+
return 1;
216+
}
217+
218+
action.sa_flags = 0;
219+
action.sa_mask = mask;
220+
action.sa_handler = sigusr1_handler;
221+
rv = sigaction(SIGUSR1, &action, NULL);
222+
if (rv == -1) {
223+
perror("sigaction");
224+
return 1;
225+
}
226+
227+
rv = check_pselect_atomic(mask);
228+
rv |= check_pselect_cross_thread(mask);
229+
return rv;
230+
}

0 commit comments

Comments
 (0)