Refer to C23 in place of C2X in glibc
[glibc.git] / debug / tst-chk-cancel.c
blobf5214abc1a23550b7864315440c65018eb59b8bb
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.
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.
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.
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/>. */
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>
30 /* Cleanup handling test. */
31 static int cl_called;
33 static void
34 cl (void *arg)
36 ++cl_called;
39 static int fds[2];
40 static pthread_barrier_t barrier;
42 static void *
43 tf_read (void *n)
45 pthread_cleanup_push (cl, NULL);
47 xpthread_barrier_wait (&barrier);
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;
55 pthread_cleanup_pop (0);
56 return 0;
59 static void *
60 tf_pread (void *n)
62 pthread_cleanup_push (cl, NULL);
64 xpthread_barrier_wait (&barrier);
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 (fds[0], &c, (uintptr_t) n, 0) != 1)
70 return (void *) -1L;
72 pthread_cleanup_pop (0);
73 return 0;
76 static void *
77 tf_pread64 (void *n)
79 pthread_cleanup_push (cl, NULL);
81 xpthread_barrier_wait (&barrier);
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 (fds[0], &c, (uintptr_t) n, 0) != 1)
87 return (void *) -1L;
89 pthread_cleanup_pop (0);
90 return 0;
93 static void *
94 tf_poll (void *n)
96 pthread_cleanup_push (cl, NULL);
98 xpthread_barrier_wait (&barrier);
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 (&pfd, (uintptr_t) n, -1) != 1)
104 return (void *) -1L;
106 pthread_cleanup_pop (0);
107 return 0;
110 static void *
111 tf_ppoll (void *n)
113 pthread_cleanup_push (cl, NULL);
115 xpthread_barrier_wait (&barrier);
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;
123 pthread_cleanup_pop (0);
124 return 0;
127 static void *
128 tf_recv (void *n)
130 pthread_cleanup_push (cl, NULL);
132 xpthread_barrier_wait (&barrier);
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 (fds[0], &c, (uintptr_t) n, 0) != 1)
138 return (void *) -1L;
140 pthread_cleanup_pop (0);
141 return 0;
144 static void *
145 tf_recvfrom (void *n)
147 pthread_cleanup_push (cl, NULL);
149 xpthread_barrier_wait (&barrier);
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 (fds[0], &c, (uintptr_t) n, 0, NULL, NULL) != 1)
155 return (void *) -1L;
157 pthread_cleanup_pop (0);
158 return 0;
161 static struct cancel_tests
163 const char *name;
164 void *(*tf) (void *);
165 bool only_early;
166 #define ADD_TEST(name, early) { #name, tf_##name, early }
167 } tests[] =
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),
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. */
180 static void
181 set_socket_buffer (int s)
183 int val = 1;
184 socklen_t len = sizeof (val);
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 ("%s: got size %d\n", __func__, val);
192 static int
193 do_test (void)
195 xpthread_barrier_init (&barrier, 0, 2);
197 if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) != 0)
198 FAIL_EXIT1 ("socketpair: %m");
199 set_socket_buffer (fds[1]);
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++)
208 if (tests[i].only_early)
209 continue;
211 xpthread_barrier_init (&barrier, NULL, 2);
212 /* Reset the counter for the cleanup handler. */
213 cl_called = 0;
215 pthread_t thr = xpthread_create (0, tests[i].tf, (void *) 1L);
216 /* After this wait the threads cancellation handler is installed. */
217 xpthread_barrier_wait (&barrier);
219 struct timespec ts = { .tv_sec = 0, .tv_nsec = 100000000 };
220 TEMP_FAILURE_RETRY (clock_nanosleep (CLOCK_REALTIME, 0, &ts, &ts));
222 xpthread_cancel (thr);
224 void *status = xpthread_join (thr);
225 TEST_VERIFY (status == PTHREAD_CANCELED);
226 TEST_COMPARE (cl_called, 1);
228 printf ("in-time cancel test of '%s' successful\n", tests[i].name);
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.
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++)
241 xpthread_barrier_init (&barrier, NULL, 2);
242 /* Reset the counter for the cleanup handler. */
243 cl_called = 0;
245 /* After this wait the cancellation handler is in place. */
246 pthread_t thr = xpthread_create (0, tests[i].tf, NULL);
248 xpthread_cancel (thr);
249 xpthread_barrier_wait (&barrier);
251 void *status = xpthread_join (thr);
252 TEST_VERIFY (status == PTHREAD_CANCELED);
253 TEST_COMPARE (cl_called, 1);
255 printf ("early cancel test of '%s' successful\n", tests[i].name);
258 xpthread_barrier_destroy (&barrier);
260 return 0;
263 #include <support/test-driver.c>