xref: /src/crypto/openssl/ssl/rio/poll_immediate.c (revision f25b8c9fb4f58cf61adb47d7570abe7caa6d385d)
1 /*
2  * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9 
10 #include "internal/common.h"
11 #include "internal/quic_ssl.h"
12 #include "internal/quic_reactor_wait_ctx.h"
13 #include <openssl/ssl.h>
14 #include <openssl/err.h>
15 #include "../ssl_local.h"
16 #include "poll_builder.h"
17 
18 #if defined(_AIX)
19 /*
20  * Some versions of AIX define macros for events and revents for use when
21  * accessing pollfd structures (see Github issue #24236). That interferes
22  * with our use of these names here. We simply undef them.
23  */
24 #undef revents
25 #undef events
26 #endif
27 
28 #define ITEM_N(items, stride, n) \
29     (*(SSL_POLL_ITEM *)((char *)(items) + (n) * (stride)))
30 
31 #define FAIL_FROM(n)                              \
32     do {                                          \
33         size_t j;                                 \
34                                                   \
35         for (j = (n); j < num_items; ++j)         \
36             ITEM_N(items, stride, j).revents = 0; \
37                                                   \
38         ok = 0;                                   \
39         goto out;                                 \
40     } while (0)
41 
42 #define FAIL_ITEM(idx)                                          \
43     do {                                                        \
44         size_t idx_ = (idx);                                    \
45                                                                 \
46         ITEM_N(items, stride, idx_).revents = SSL_POLL_EVENT_F; \
47         ++result_count;                                         \
48         FAIL_FROM(idx_ + 1);                                    \
49     } while (0)
50 
51 #ifndef OPENSSL_NO_QUIC
poll_translate_ssl_quic(SSL * ssl,QUIC_REACTOR_WAIT_CTX * wctx,RIO_POLL_BUILDER * rpb,uint64_t events,int * abort_blocking)52 static int poll_translate_ssl_quic(SSL *ssl,
53     QUIC_REACTOR_WAIT_CTX *wctx,
54     RIO_POLL_BUILDER *rpb,
55     uint64_t events,
56     int *abort_blocking)
57 {
58     BIO_POLL_DESCRIPTOR rd, wd;
59     int fd1 = -1, fd2 = -1, fd_nfy = -1;
60     int fd1_r = 0, fd1_w = 0, fd2_w = 0;
61 
62     if (SSL_net_read_desired(ssl)) {
63         if (!SSL_get_rpoll_descriptor(ssl, &rd)) {
64             ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
65                 "SSL_poll requires the network BIOs underlying "
66                 "a QUIC SSL object provide poll descriptors");
67             return 0;
68         }
69 
70         if (rd.type != BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD) {
71             ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
72                 "SSL_poll requires the poll descriptors of the "
73                 "network BIOs underlying a QUIC SSL object be "
74                 "of socket type");
75             return 0;
76         }
77 
78         fd1 = rd.value.fd;
79         fd1_r = 1;
80     }
81 
82     if (SSL_net_write_desired(ssl)) {
83         if (!SSL_get_wpoll_descriptor(ssl, &wd)) {
84             ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
85                 "SSL_poll requires the network BIOs underlying "
86                 "a QUIC SSL object provide poll descriptors");
87             return 0;
88         }
89 
90         if (wd.type != BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD) {
91             ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
92                 "SSL_poll requires the poll descriptors of the "
93                 "network BIOs underlying a QUIC SSL object be "
94                 "of socket type");
95             return 0;
96         }
97 
98         fd2 = wd.value.fd;
99         fd2_w = 1;
100     }
101 
102     if (fd2 == fd1) {
103         fd2 = -1;
104         fd1_w = fd2_w;
105     }
106 
107     if (fd1 != -1)
108         if (!ossl_rio_poll_builder_add_fd(rpb, fd1, fd1_r, fd1_w))
109             return 0;
110 
111     if (fd2 != -1 && fd2_w)
112         if (!ossl_rio_poll_builder_add_fd(rpb, fd2, /*r = */ 0, fd2_w))
113             return 0;
114 
115     /*
116      * Add the notifier FD for the QUIC domain this SSL object is a part of (if
117      * there is one). This ensures we get woken up if another thread calls into
118      * that QUIC domain and some readiness event relevant to the SSL_poll call
119      * on this thread arises without the underlying network socket ever becoming
120      * readable.
121      */
122     fd_nfy = ossl_quic_get_notifier_fd(ssl);
123     if (fd_nfy != -1) {
124         uint64_t revents = 0;
125 
126         if (!ossl_rio_poll_builder_add_fd(rpb, fd_nfy, /*r = */ 1, /*w = */ 0))
127             return 0;
128 
129         /* Tell QUIC domain we need to receive notifications. */
130         ossl_quic_enter_blocking_section(ssl, wctx);
131 
132         /*
133          * Only after the above call returns is it guaranteed that any readiness
134          * events will cause the above notifier to become readable. Therefore,
135          * it is possible the object became ready after our initial
136          * poll_readout() call (before we determined that nothing was ready and
137          * we needed to block). We now need to do another readout, in which case
138          * blocking is to be aborted.
139          */
140         if (!ossl_quic_conn_poll_events(ssl, events, /*do_tick = */ 0, &revents)) {
141             ossl_quic_leave_blocking_section(ssl, wctx);
142             return 0;
143         }
144 
145         if (revents != 0) {
146             ossl_quic_leave_blocking_section(ssl, wctx);
147             *abort_blocking = 1;
148             return 1;
149         }
150     }
151 
152     return 1;
153 }
154 
postpoll_translation_cleanup_ssl_quic(SSL * ssl,QUIC_REACTOR_WAIT_CTX * wctx)155 static void postpoll_translation_cleanup_ssl_quic(SSL *ssl,
156     QUIC_REACTOR_WAIT_CTX *wctx)
157 {
158     if (ossl_quic_get_notifier_fd(ssl) != -1)
159         ossl_quic_leave_blocking_section(ssl, wctx);
160 }
161 
postpoll_translation_cleanup(SSL_POLL_ITEM * items,size_t num_items,size_t stride,QUIC_REACTOR_WAIT_CTX * wctx)162 static void postpoll_translation_cleanup(SSL_POLL_ITEM *items,
163     size_t num_items,
164     size_t stride,
165     QUIC_REACTOR_WAIT_CTX *wctx)
166 {
167     SSL_POLL_ITEM *item;
168     SSL *ssl;
169     size_t i;
170 
171     for (i = 0; i < num_items; ++i) {
172         item = &ITEM_N(items, stride, i);
173 
174         switch (item->desc.type) {
175         case BIO_POLL_DESCRIPTOR_TYPE_SSL:
176             ssl = item->desc.value.ssl;
177             if (ssl == NULL)
178                 break;
179 
180             switch (ssl->type) {
181 #ifndef OPENSSL_NO_QUIC
182             case SSL_TYPE_QUIC_LISTENER:
183             case SSL_TYPE_QUIC_CONNECTION:
184             case SSL_TYPE_QUIC_XSO:
185                 postpoll_translation_cleanup_ssl_quic(ssl, wctx);
186                 break;
187 #endif
188             default:
189                 break;
190             }
191             break;
192         default:
193             break;
194         }
195     }
196 }
197 
poll_translate(SSL_POLL_ITEM * items,size_t num_items,size_t stride,QUIC_REACTOR_WAIT_CTX * wctx,RIO_POLL_BUILDER * rpb,OSSL_TIME * p_earliest_wakeup_deadline,int * abort_blocking,size_t * p_result_count)198 static int poll_translate(SSL_POLL_ITEM *items,
199     size_t num_items,
200     size_t stride,
201     QUIC_REACTOR_WAIT_CTX *wctx,
202     RIO_POLL_BUILDER *rpb,
203     OSSL_TIME *p_earliest_wakeup_deadline,
204     int *abort_blocking,
205     size_t *p_result_count)
206 {
207     int ok = 1;
208     SSL_POLL_ITEM *item;
209     size_t result_count = 0;
210     SSL *ssl;
211     OSSL_TIME earliest_wakeup_deadline = ossl_time_infinite();
212     struct timeval timeout;
213     int is_infinite = 0;
214     size_t i;
215 
216     for (i = 0; i < num_items; ++i) {
217         item = &ITEM_N(items, stride, i);
218 
219         switch (item->desc.type) {
220         case BIO_POLL_DESCRIPTOR_TYPE_SSL:
221             ssl = item->desc.value.ssl;
222             if (ssl == NULL)
223                 /* NULL items are no-ops and have revents reported as 0 */
224                 break;
225 
226             switch (ssl->type) {
227 #ifndef OPENSSL_NO_QUIC
228             case SSL_TYPE_QUIC_LISTENER:
229             case SSL_TYPE_QUIC_CONNECTION:
230             case SSL_TYPE_QUIC_XSO:
231                 if (!poll_translate_ssl_quic(ssl, wctx, rpb, item->events,
232                         abort_blocking))
233                     FAIL_ITEM(i);
234 
235                 if (*abort_blocking)
236                     return 1;
237 
238                 if (!SSL_get_event_timeout(ssl, &timeout, &is_infinite))
239                     FAIL_ITEM(i++); /* need to clean up this item too */
240 
241                 if (!is_infinite)
242                     earliest_wakeup_deadline
243                         = ossl_time_min(earliest_wakeup_deadline,
244                             ossl_time_add(ossl_time_now(),
245                                 ossl_time_from_timeval(timeout)));
246 
247                 break;
248 #endif
249 
250             default:
251                 ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
252                     "SSL_poll currently only supports QUIC SSL "
253                     "objects");
254                 FAIL_ITEM(i);
255             }
256             break;
257 
258         case BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD:
259             ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
260                 "SSL_poll currently does not support polling "
261                 "sockets");
262             FAIL_ITEM(i);
263 
264         default:
265             ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
266                 "SSL_poll does not support unknown poll descriptor "
267                 "type %d",
268                 item->desc.type);
269             FAIL_ITEM(i);
270         }
271     }
272 
273 out:
274     if (!ok)
275         postpoll_translation_cleanup(items, i, stride, wctx);
276 
277     *p_earliest_wakeup_deadline = earliest_wakeup_deadline;
278     *p_result_count = result_count;
279     return ok;
280 }
281 
poll_block(SSL_POLL_ITEM * items,size_t num_items,size_t stride,OSSL_TIME user_deadline,size_t * p_result_count)282 static int poll_block(SSL_POLL_ITEM *items,
283     size_t num_items,
284     size_t stride,
285     OSSL_TIME user_deadline,
286     size_t *p_result_count)
287 {
288     int ok = 0, abort_blocking = 0;
289     RIO_POLL_BUILDER rpb;
290     QUIC_REACTOR_WAIT_CTX wctx;
291     OSSL_TIME earliest_wakeup_deadline;
292 
293     /*
294      * Blocking is somewhat involved and involves the following steps:
295      *
296      * - Translation, in which the various logical items (SSL objects, etc.) to
297      *   be polled are translated into items an OS polling API understands.
298      *
299      * - Synchronisation bookkeeping. This ensures that we can be woken up
300      *   not just by readiness of any underlying file descriptor distilled from
301      *   the provided items but also by other threads, which might do work
302      *   on a relevant QUIC object to cause the object to be ready without the
303      *   underlying file descriptor ever becoming ready from our perspective.
304      *
305      * - The blocking call to the OS polling API.
306      *
307      * - Currently we do not do reverse translation but simply call
308      *   poll_readout() again to read out all readiness state for all
309      *   descriptors which the user passed.
310      *
311      *   TODO(QUIC POLLING): In the future we will do reverse translation here
312      *   also to facilitate a more efficient readout.
313      */
314     ossl_quic_reactor_wait_ctx_init(&wctx);
315     ossl_rio_poll_builder_init(&rpb);
316 
317     if (!poll_translate(items, num_items, stride, &wctx, &rpb,
318             &earliest_wakeup_deadline,
319             &abort_blocking,
320             p_result_count))
321         goto out;
322 
323     if (abort_blocking)
324         goto out;
325 
326     earliest_wakeup_deadline = ossl_time_min(earliest_wakeup_deadline,
327         user_deadline);
328 
329     ok = ossl_rio_poll_builder_poll(&rpb, earliest_wakeup_deadline);
330 
331     postpoll_translation_cleanup(items, num_items, stride, &wctx);
332 
333 out:
334     ossl_rio_poll_builder_cleanup(&rpb);
335     ossl_quic_reactor_wait_ctx_cleanup(&wctx);
336     return ok;
337 }
338 #endif
339 
poll_readout(SSL_POLL_ITEM * items,size_t num_items,size_t stride,int do_tick,size_t * p_result_count)340 static int poll_readout(SSL_POLL_ITEM *items,
341     size_t num_items,
342     size_t stride,
343     int do_tick,
344     size_t *p_result_count)
345 {
346     int ok = 1;
347     size_t i, result_count = 0;
348     SSL_POLL_ITEM *item;
349     SSL *ssl;
350 #ifndef OPENSSL_NO_QUIC
351     uint64_t events;
352 #endif
353     uint64_t revents;
354 
355     for (i = 0; i < num_items; ++i) {
356         item = &ITEM_N(items, stride, i);
357 #ifndef OPENSSL_NO_QUIC
358         events = item->events;
359 #endif
360         revents = 0;
361 
362         switch (item->desc.type) {
363         case BIO_POLL_DESCRIPTOR_TYPE_SSL:
364             ssl = item->desc.value.ssl;
365             if (ssl == NULL)
366                 /* NULL items are no-ops and have revents reported as 0 */
367                 break;
368 
369             switch (ssl->type) {
370 #ifndef OPENSSL_NO_QUIC
371             case SSL_TYPE_QUIC_LISTENER:
372             case SSL_TYPE_QUIC_CONNECTION:
373             case SSL_TYPE_QUIC_XSO:
374                 if (!ossl_quic_conn_poll_events(ssl, events, do_tick, &revents))
375                     /* above call raises ERR */
376                     FAIL_ITEM(i);
377 
378                 if (revents != 0)
379                     ++result_count;
380 
381                 break;
382 #endif
383 
384             default:
385                 ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
386                     "SSL_poll currently only supports QUIC SSL "
387                     "objects");
388                 FAIL_ITEM(i);
389             }
390             break;
391         case BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD:
392             ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
393                 "SSL_poll currently does not support polling "
394                 "sockets");
395             FAIL_ITEM(i);
396         default:
397             ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,
398                 "SSL_poll does not support unknown poll descriptor "
399                 "type %d",
400                 item->desc.type);
401             FAIL_ITEM(i);
402         }
403 
404         item->revents = revents;
405     }
406 
407 out:
408     if (p_result_count != NULL)
409         *p_result_count = result_count;
410 
411     return ok;
412 }
413 
SSL_poll(SSL_POLL_ITEM * items,size_t num_items,size_t stride,const struct timeval * timeout,uint64_t flags,size_t * p_result_count)414 int SSL_poll(SSL_POLL_ITEM *items,
415     size_t num_items,
416     size_t stride,
417     const struct timeval *timeout,
418     uint64_t flags,
419     size_t *p_result_count)
420 {
421     int ok = 1;
422     size_t result_count = 0;
423     ossl_unused int do_tick = ((flags & SSL_POLL_FLAG_NO_HANDLE_EVENTS) == 0);
424     OSSL_TIME deadline;
425 
426     /* Trivial case. */
427     if (num_items == 0) {
428         if (timeout == NULL)
429             goto out;
430         OSSL_sleep(ossl_time2ms(ossl_time_from_timeval(*timeout)));
431         goto out;
432     }
433 
434     /* Convert timeout to deadline. */
435     if (timeout == NULL)
436         deadline = ossl_time_infinite();
437     else if (timeout->tv_sec == 0 && timeout->tv_usec == 0)
438         deadline = ossl_time_zero();
439     else
440         deadline = ossl_time_add(ossl_time_now(),
441             ossl_time_from_timeval(*timeout));
442 
443     /* Loop until we have something to report. */
444     for (;;) {
445         /* Readout phase - poll current state of each item. */
446         if (!poll_readout(items, num_items, stride, do_tick, &result_count)) {
447             ok = 0;
448             goto out;
449         }
450 
451         /*
452          * If we got anything, or we are in immediate mode (zero timeout), or
453          * the deadline has expired, we're done.
454          */
455         if (result_count > 0
456             || ossl_time_is_zero(deadline) /* (avoids now call) */
457             || ossl_time_compare(ossl_time_now(), deadline) >= 0)
458             goto out;
459 
460         /*
461          * Block until something is ready. Ignore NO_HANDLE_EVENTS from this
462          * point onwards.
463          */
464         do_tick = 1;
465 #ifndef OPENSSL_NO_QUIC
466         if (!poll_block(items, num_items, stride, deadline, &result_count)) {
467             ok = 0;
468             goto out;
469         }
470 #endif
471     }
472 
473     /* TODO(QUIC POLLING): Support for polling FDs */
474 
475 out:
476     if (p_result_count != NULL)
477         *p_result_count = result_count;
478 
479     return ok;
480 }
481