1/* Test for required cancellation points in fortified functions (BZ #29274)
2 Copyright (C) 2022-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <array_length.h>
20#include <errno.h>
21#include <poll.h>
22#include <stdbool.h>
23#include <stdint.h>
24#include <stdio.h>
25#include <support/check.h>
26#include <support/xthread.h>
27#include <support/xunistd.h>
28#include <sys/socket.h>
29
30/* Cleanup handling test. */
31static int cl_called;
32
33static void
34cl (void *arg)
35{
36 ++cl_called;
37}
38
39static int fds[2];
40static pthread_barrier_t barrier;
41
42static void *
43tf_read (void *n)
44{
45 pthread_cleanup_push (cl, NULL);
46
47 xpthread_barrier_wait (barrier: &barrier);
48
49 /* This call should be forwarded to __read_chk because the buffer size
50 is known, but the read length is non-constant. */
51 char c;
52 if (read (fds[0], &c, (uintptr_t) n) != 1)
53 return (void *) -1L;
54
55 pthread_cleanup_pop (0);
56 return 0;
57}
58
59static void *
60tf_pread (void *n)
61{
62 pthread_cleanup_push (cl, NULL);
63
64 xpthread_barrier_wait (barrier: &barrier);
65
66 /* This call should be forwarded to __pread_chk because the buffer size
67 is known, but the read length is non-constant. */
68 char c;
69 if (pread (fd: fds[0], buf: &c, nbytes: (uintptr_t) n, offset: 0) != 1)
70 return (void *) -1L;
71
72 pthread_cleanup_pop (0);
73 return 0;
74}
75
76static void *
77tf_pread64 (void *n)
78{
79 pthread_cleanup_push (cl, NULL);
80
81 xpthread_barrier_wait (barrier: &barrier);
82
83 /* This call should be forwarded to __pread64_chk because the buffer size
84 is known, but the read length is non-constant. */
85 char c;
86 if (pread64 (fd: fds[0], buf: &c, nbytes: (uintptr_t) n, offset: 0) != 1)
87 return (void *) -1L;
88
89 pthread_cleanup_pop (0);
90 return 0;
91}
92
93static void *
94tf_poll (void *n)
95{
96 pthread_cleanup_push (cl, NULL);
97
98 xpthread_barrier_wait (barrier: &barrier);
99
100 /* This call should be forwarded to __poll_chk because the pollfd size
101 is known, but the number of entries is non-constant. */
102 struct pollfd pfd = { fds[0], POLLIN, 0 };
103 if (poll (fds: &pfd, nfds: (uintptr_t) n, timeout: -1) != 1)
104 return (void *) -1L;
105
106 pthread_cleanup_pop (0);
107 return 0;
108}
109
110static void *
111tf_ppoll (void *n)
112{
113 pthread_cleanup_push (cl, NULL);
114
115 xpthread_barrier_wait (barrier: &barrier);
116
117 /* This call should be forwarded to __ppoll_chk because the pollfd size
118 is known, but the number of entries is non-constant. */
119 struct pollfd pfd = { fds[0], POLLIN, 0 };
120 if (ppoll (&pfd, (uintptr_t) n, 0, 0) != 1)
121 return (void *) -1L;
122
123 pthread_cleanup_pop (0);
124 return 0;
125}
126
127static void *
128tf_recv (void *n)
129{
130 pthread_cleanup_push (cl, NULL);
131
132 xpthread_barrier_wait (barrier: &barrier);
133
134 /* This call should be forwarded to __ppoll_chk because the pollfd size
135 is known, but the number of entries is non-constant. */
136 char c;
137 if (recv (fd: fds[0], buf: &c, n: (uintptr_t) n, flags: 0) != 1)
138 return (void *) -1L;
139
140 pthread_cleanup_pop (0);
141 return 0;
142}
143
144static void *
145tf_recvfrom (void *n)
146{
147 pthread_cleanup_push (cl, NULL);
148
149 xpthread_barrier_wait (barrier: &barrier);
150
151 /* This call should be forwarded to __ppoll_chk because the pollfd size
152 is known, but the number of entries is non-constant. */
153 char c;
154 if (recvfrom (fd: fds[0], buf: &c, n: (uintptr_t) n, flags: 0, NULL, NULL) != 1)
155 return (void *) -1L;
156
157 pthread_cleanup_pop (0);
158 return 0;
159}
160
161static struct cancel_tests
162{
163 const char *name;
164 void *(*tf) (void *);
165 bool only_early;
166#define ADD_TEST(name, early) { #name, tf_##name, early }
167} tests[] =
168{
169 ADD_TEST (poll, false),
170 ADD_TEST (ppoll, false),
171 ADD_TEST (pread, true),
172 ADD_TEST (pread64, true),
173 ADD_TEST (read, false),
174 ADD_TEST (recv, false),
175 ADD_TEST (recvfrom, false),
176};
177
178/* Set the send buffer of socket S to 1 byte so any send operation
179 done with WRITE_BUFFER_SIZE bytes will force syscall blocking. */
180static void
181set_socket_buffer (int s)
182{
183 int val = 1;
184 socklen_t len = sizeof (val);
185
186 TEST_VERIFY_EXIT (setsockopt (s, SOL_SOCKET, SO_SNDBUF, &val,
187 sizeof (val)) == 0);
188 TEST_VERIFY_EXIT (getsockopt (s, SOL_SOCKET, SO_SNDBUF, &val, &len) == 0);
189 printf (format: "%s: got size %d\n", __func__, val);
190}
191
192static int
193do_test (void)
194{
195 xpthread_barrier_init (barrier: &barrier, attr: 0, count: 2);
196
197 if (socketpair (AF_UNIX, SOCK_STREAM, protocol: 0, fds: fds) != 0)
198 FAIL_EXIT1 ("socketpair: %m");
199 set_socket_buffer (fds[1]);
200
201 /* This is the !only_early test. It is a late cancel test that has a sleep
202 in the main thread in an attempt to allow the child thread to reach and
203 block on the syscall. The cancellation should happen with high
204 probability when the child thread blocked on the syscall, and that is
205 the intent of the test (syscall cancellation registration complete). */
206 for (int i = 0; i < array_length (tests); i++)
207 {
208 if (tests[i].only_early)
209 continue;
210
211 xpthread_barrier_init (barrier: &barrier, NULL, count: 2);
212 /* Reset the counter for the cleanup handler. */
213 cl_called = 0;
214
215 pthread_t thr = xpthread_create (attr: 0, thread_func: tests[i].tf, closure: (void *) 1L);
216 /* After this wait the threads cancellation handler is installed. */
217 xpthread_barrier_wait (barrier: &barrier);
218
219 struct timespec ts = { .tv_sec = 0, .tv_nsec = 100000000 };
220 TEMP_FAILURE_RETRY (clock_nanosleep (CLOCK_REALTIME, 0, &ts, &ts));
221
222 xpthread_cancel (thr);
223
224 void *status = xpthread_join (thr);
225 TEST_VERIFY (status == PTHREAD_CANCELED);
226 TEST_COMPARE (cl_called, 1);
227
228 printf (format: "in-time cancel test of '%s' successful\n", tests[i].name);
229 }
230
231 /* This is a early cancel test that happens before the syscall is issued.
232 In this case there is no signal involved, pthread_cancel will just mark
233 the target thread canceled, since asynchronous mode is not set, and the
234 cancellable entrypoint will check if the thread is set as cancelled and
235 exit early.
236
237 Keep in mind that neither pthread_barrier_wait nor pthread_cleanup_push
238 act as cancellation entrypoints. */
239 for (int i = 0; i < array_length (tests); i++)
240 {
241 xpthread_barrier_init (barrier: &barrier, NULL, count: 2);
242 /* Reset the counter for the cleanup handler. */
243 cl_called = 0;
244
245 /* After this wait the cancellation handler is in place. */
246 pthread_t thr = xpthread_create (attr: 0, thread_func: tests[i].tf, NULL);
247
248 xpthread_cancel (thr);
249 xpthread_barrier_wait (barrier: &barrier);
250
251 void *status = xpthread_join (thr);
252 TEST_VERIFY (status == PTHREAD_CANCELED);
253 TEST_COMPARE (cl_called, 1);
254
255 printf (format: "early cancel test of '%s' successful\n", tests[i].name);
256 }
257
258 xpthread_barrier_destroy (barrier: &barrier);
259
260 return 0;
261}
262
263#include <support/test-driver.c>
264

source code of glibc/debug/tst-chk-cancel.c