S390: Fix building with --disable-mutli-arch [BZ #31196]
[platform/upstream/glibc.git] / debug / tst-chk-cancel.c
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.  */
31 static int cl_called;
32
33 static void
34 cl (void *arg)
35 {
36   ++cl_called;
37 }
38
39 static int fds[2];
40 static pthread_barrier_t barrier;
41
42 static void *
43 tf_read (void *n)
44 {
45   pthread_cleanup_push (cl, NULL);
46
47   xpthread_barrier_wait (&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
59 static void *
60 tf_pread (void *n)
61 {
62   pthread_cleanup_push (cl, NULL);
63
64   xpthread_barrier_wait (&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 (fds[0], &c, (uintptr_t) n, 0) != 1)
70     return (void *) -1L;
71
72   pthread_cleanup_pop (0);
73   return 0;
74 }
75
76 static void *
77 tf_pread64 (void *n)
78 {
79   pthread_cleanup_push (cl, NULL);
80
81   xpthread_barrier_wait (&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 (fds[0], &c, (uintptr_t) n, 0) != 1)
87     return (void *) -1L;
88
89   pthread_cleanup_pop (0);
90   return 0;
91 }
92
93 static void *
94 tf_poll (void *n)
95 {
96   pthread_cleanup_push (cl, NULL);
97
98   xpthread_barrier_wait (&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 (&pfd, (uintptr_t) n, -1) != 1)
104     return (void *) -1L;
105
106   pthread_cleanup_pop (0);
107   return 0;
108 }
109
110 static void *
111 tf_ppoll (void *n)
112 {
113   pthread_cleanup_push (cl, NULL);
114
115   xpthread_barrier_wait (&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
127 static void *
128 tf_recv (void *n)
129 {
130   pthread_cleanup_push (cl, NULL);
131
132   xpthread_barrier_wait (&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 (fds[0], &c, (uintptr_t) n, 0) != 1)
138     return (void *) -1L;
139
140   pthread_cleanup_pop (0);
141   return 0;
142 }
143
144 static void *
145 tf_recvfrom (void *n)
146 {
147   pthread_cleanup_push (cl, NULL);
148
149   xpthread_barrier_wait (&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 (fds[0], &c, (uintptr_t) n, 0, NULL, NULL) != 1)
155     return (void *) -1L;
156
157   pthread_cleanup_pop (0);
158   return 0;
159 }
160
161 static 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.  */
180 static void
181 set_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 ("%s: got size %d\n", __func__, val);
190 }
191
192 static int
193 do_test (void)
194 {
195   xpthread_barrier_init (&barrier, 0, 2);
196
197   if (socketpair (AF_UNIX, SOCK_STREAM, 0, 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, NULL, 2);
212       /* Reset the counter for the cleanup handler.  */
213       cl_called = 0;
214
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);
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 ("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, NULL, 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 (0, tests[i].tf, NULL);
247
248       xpthread_cancel (thr);
249       xpthread_barrier_wait (&barrier);
250
251       void *status = xpthread_join (thr);
252       TEST_VERIFY (status == PTHREAD_CANCELED);
253       TEST_COMPARE (cl_called, 1);
254
255       printf ("early cancel test of '%s' successful\n", tests[i].name);
256     }
257
258   xpthread_barrier_destroy (&barrier);
259
260   return 0;
261 }
262
263 #include <support/test-driver.c>