Path: blob/main/crypto/openssl/ssl/rio/poll_immediate.c
105585 views
/*1* Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.2*3* Licensed under the Apache License 2.0 (the "License"). You may not use4* this file except in compliance with the License. You can obtain a copy5* in the file LICENSE in the source distribution or at6* https://www.openssl.org/source/license.html7*/89#include "internal/common.h"10#include "internal/quic_ssl.h"11#include "internal/quic_reactor_wait_ctx.h"12#include <openssl/ssl.h>13#include <openssl/err.h>14#include "../ssl_local.h"15#include "poll_builder.h"1617#if defined(_AIX)18/*19* Some versions of AIX define macros for events and revents for use when20* accessing pollfd structures (see Github issue #24236). That interferes21* with our use of these names here. We simply undef them.22*/23#undef revents24#undef events25#endif2627#define ITEM_N(items, stride, n) \28(*(SSL_POLL_ITEM *)((char *)(items) + (n) * (stride)))2930#define FAIL_FROM(n) \31do { \32size_t j; \33\34for (j = (n); j < num_items; ++j) \35ITEM_N(items, stride, j).revents = 0; \36\37ok = 0; \38goto out; \39} while (0)4041#define FAIL_ITEM(idx) \42do { \43size_t idx_ = (idx); \44\45ITEM_N(items, stride, idx_).revents = SSL_POLL_EVENT_F; \46++result_count; \47FAIL_FROM(idx_ + 1); \48} while (0)4950#ifndef OPENSSL_NO_QUIC51static int poll_translate_ssl_quic(SSL *ssl,52QUIC_REACTOR_WAIT_CTX *wctx,53RIO_POLL_BUILDER *rpb,54uint64_t events,55int *abort_blocking)56{57BIO_POLL_DESCRIPTOR rd, wd;58int fd1 = -1, fd2 = -1, fd_nfy = -1;59int fd1_r = 0, fd1_w = 0, fd2_w = 0;6061if (SSL_net_read_desired(ssl)) {62if (!SSL_get_rpoll_descriptor(ssl, &rd)) {63ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,64"SSL_poll requires the network BIOs underlying "65"a QUIC SSL object provide poll descriptors");66return 0;67}6869if (rd.type != BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD) {70ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,71"SSL_poll requires the poll descriptors of the "72"network BIOs underlying a QUIC SSL object be "73"of socket type");74return 0;75}7677fd1 = rd.value.fd;78fd1_r = 1;79}8081if (SSL_net_write_desired(ssl)) {82if (!SSL_get_wpoll_descriptor(ssl, &wd)) {83ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,84"SSL_poll requires the network BIOs underlying "85"a QUIC SSL object provide poll descriptors");86return 0;87}8889if (wd.type != BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD) {90ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,91"SSL_poll requires the poll descriptors of the "92"network BIOs underlying a QUIC SSL object be "93"of socket type");94return 0;95}9697fd2 = wd.value.fd;98fd2_w = 1;99}100101if (fd2 == fd1) {102fd2 = -1;103fd1_w = fd2_w;104}105106if (fd1 != -1)107if (!ossl_rio_poll_builder_add_fd(rpb, fd1, fd1_r, fd1_w))108return 0;109110if (fd2 != -1 && fd2_w)111if (!ossl_rio_poll_builder_add_fd(rpb, fd2, /*r = */ 0, fd2_w))112return 0;113114/*115* Add the notifier FD for the QUIC domain this SSL object is a part of (if116* there is one). This ensures we get woken up if another thread calls into117* that QUIC domain and some readiness event relevant to the SSL_poll call118* on this thread arises without the underlying network socket ever becoming119* readable.120*/121fd_nfy = ossl_quic_get_notifier_fd(ssl);122if (fd_nfy != -1) {123uint64_t revents = 0;124125if (!ossl_rio_poll_builder_add_fd(rpb, fd_nfy, /*r = */ 1, /*w = */ 0))126return 0;127128/* Tell QUIC domain we need to receive notifications. */129ossl_quic_enter_blocking_section(ssl, wctx);130131/*132* Only after the above call returns is it guaranteed that any readiness133* events will cause the above notifier to become readable. Therefore,134* it is possible the object became ready after our initial135* poll_readout() call (before we determined that nothing was ready and136* we needed to block). We now need to do another readout, in which case137* blocking is to be aborted.138*/139if (!ossl_quic_conn_poll_events(ssl, events, /*do_tick = */ 0, &revents)) {140ossl_quic_leave_blocking_section(ssl, wctx);141return 0;142}143144if (revents != 0) {145ossl_quic_leave_blocking_section(ssl, wctx);146*abort_blocking = 1;147return 1;148}149}150151return 1;152}153154static void postpoll_translation_cleanup_ssl_quic(SSL *ssl,155QUIC_REACTOR_WAIT_CTX *wctx)156{157if (ossl_quic_get_notifier_fd(ssl) != -1)158ossl_quic_leave_blocking_section(ssl, wctx);159}160161static void postpoll_translation_cleanup(SSL_POLL_ITEM *items,162size_t num_items,163size_t stride,164QUIC_REACTOR_WAIT_CTX *wctx)165{166SSL_POLL_ITEM *item;167SSL *ssl;168size_t i;169170for (i = 0; i < num_items; ++i) {171item = &ITEM_N(items, stride, i);172173switch (item->desc.type) {174case BIO_POLL_DESCRIPTOR_TYPE_SSL:175ssl = item->desc.value.ssl;176if (ssl == NULL)177break;178179switch (ssl->type) {180#ifndef OPENSSL_NO_QUIC181case SSL_TYPE_QUIC_LISTENER:182case SSL_TYPE_QUIC_CONNECTION:183case SSL_TYPE_QUIC_XSO:184postpoll_translation_cleanup_ssl_quic(ssl, wctx);185break;186#endif187default:188break;189}190break;191default:192break;193}194}195}196197static int poll_translate(SSL_POLL_ITEM *items,198size_t num_items,199size_t stride,200QUIC_REACTOR_WAIT_CTX *wctx,201RIO_POLL_BUILDER *rpb,202OSSL_TIME *p_earliest_wakeup_deadline,203int *abort_blocking,204size_t *p_result_count)205{206int ok = 1;207SSL_POLL_ITEM *item;208size_t result_count = 0;209SSL *ssl;210OSSL_TIME earliest_wakeup_deadline = ossl_time_infinite();211struct timeval timeout;212int is_infinite = 0;213size_t i;214215for (i = 0; i < num_items; ++i) {216item = &ITEM_N(items, stride, i);217218switch (item->desc.type) {219case BIO_POLL_DESCRIPTOR_TYPE_SSL:220ssl = item->desc.value.ssl;221if (ssl == NULL)222/* NULL items are no-ops and have revents reported as 0 */223break;224225switch (ssl->type) {226#ifndef OPENSSL_NO_QUIC227case SSL_TYPE_QUIC_LISTENER:228case SSL_TYPE_QUIC_CONNECTION:229case SSL_TYPE_QUIC_XSO:230if (!poll_translate_ssl_quic(ssl, wctx, rpb, item->events,231abort_blocking))232FAIL_ITEM(i);233234if (*abort_blocking)235return 1;236237if (!SSL_get_event_timeout(ssl, &timeout, &is_infinite))238FAIL_ITEM(i++); /* need to clean up this item too */239240if (!is_infinite)241earliest_wakeup_deadline242= ossl_time_min(earliest_wakeup_deadline,243ossl_time_add(ossl_time_now(),244ossl_time_from_timeval(timeout)));245246break;247#endif248249default:250ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,251"SSL_poll currently only supports QUIC SSL "252"objects");253FAIL_ITEM(i);254}255break;256257case BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD:258ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,259"SSL_poll currently does not support polling "260"sockets");261FAIL_ITEM(i);262263default:264ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,265"SSL_poll does not support unknown poll descriptor "266"type %d",267item->desc.type);268FAIL_ITEM(i);269}270}271272out:273if (!ok)274postpoll_translation_cleanup(items, i, stride, wctx);275276*p_earliest_wakeup_deadline = earliest_wakeup_deadline;277*p_result_count = result_count;278return ok;279}280281static int poll_block(SSL_POLL_ITEM *items,282size_t num_items,283size_t stride,284OSSL_TIME user_deadline,285size_t *p_result_count)286{287int ok = 0, abort_blocking = 0;288RIO_POLL_BUILDER rpb;289QUIC_REACTOR_WAIT_CTX wctx;290OSSL_TIME earliest_wakeup_deadline;291292/*293* Blocking is somewhat involved and involves the following steps:294*295* - Translation, in which the various logical items (SSL objects, etc.) to296* be polled are translated into items an OS polling API understands.297*298* - Synchronisation bookkeeping. This ensures that we can be woken up299* not just by readiness of any underlying file descriptor distilled from300* the provided items but also by other threads, which might do work301* on a relevant QUIC object to cause the object to be ready without the302* underlying file descriptor ever becoming ready from our perspective.303*304* - The blocking call to the OS polling API.305*306* - Currently we do not do reverse translation but simply call307* poll_readout() again to read out all readiness state for all308* descriptors which the user passed.309*310* TODO(QUIC POLLING): In the future we will do reverse translation here311* also to facilitate a more efficient readout.312*/313ossl_quic_reactor_wait_ctx_init(&wctx);314ossl_rio_poll_builder_init(&rpb);315316if (!poll_translate(items, num_items, stride, &wctx, &rpb,317&earliest_wakeup_deadline,318&abort_blocking,319p_result_count))320goto out;321322if (abort_blocking)323goto out;324325earliest_wakeup_deadline = ossl_time_min(earliest_wakeup_deadline,326user_deadline);327328ok = ossl_rio_poll_builder_poll(&rpb, earliest_wakeup_deadline);329330postpoll_translation_cleanup(items, num_items, stride, &wctx);331332out:333ossl_rio_poll_builder_cleanup(&rpb);334ossl_quic_reactor_wait_ctx_cleanup(&wctx);335return ok;336}337#endif338339static int poll_readout(SSL_POLL_ITEM *items,340size_t num_items,341size_t stride,342int do_tick,343size_t *p_result_count)344{345int ok = 1;346size_t i, result_count = 0;347SSL_POLL_ITEM *item;348SSL *ssl;349#ifndef OPENSSL_NO_QUIC350uint64_t events;351#endif352uint64_t revents;353354for (i = 0; i < num_items; ++i) {355item = &ITEM_N(items, stride, i);356#ifndef OPENSSL_NO_QUIC357events = item->events;358#endif359revents = 0;360361switch (item->desc.type) {362case BIO_POLL_DESCRIPTOR_TYPE_SSL:363ssl = item->desc.value.ssl;364if (ssl == NULL)365/* NULL items are no-ops and have revents reported as 0 */366break;367368switch (ssl->type) {369#ifndef OPENSSL_NO_QUIC370case SSL_TYPE_QUIC_LISTENER:371case SSL_TYPE_QUIC_CONNECTION:372case SSL_TYPE_QUIC_XSO:373if (!ossl_quic_conn_poll_events(ssl, events, do_tick, &revents))374/* above call raises ERR */375FAIL_ITEM(i);376377if (revents != 0)378++result_count;379380break;381#endif382383default:384ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,385"SSL_poll currently only supports QUIC SSL "386"objects");387FAIL_ITEM(i);388}389break;390case BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD:391ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,392"SSL_poll currently does not support polling "393"sockets");394FAIL_ITEM(i);395default:396ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,397"SSL_poll does not support unknown poll descriptor "398"type %d",399item->desc.type);400FAIL_ITEM(i);401}402403item->revents = revents;404}405406out:407if (p_result_count != NULL)408*p_result_count = result_count;409410return ok;411}412413int SSL_poll(SSL_POLL_ITEM *items,414size_t num_items,415size_t stride,416const struct timeval *timeout,417uint64_t flags,418size_t *p_result_count)419{420int ok = 1;421size_t result_count = 0;422ossl_unused int do_tick = ((flags & SSL_POLL_FLAG_NO_HANDLE_EVENTS) == 0);423OSSL_TIME deadline;424425/* Trivial case. */426if (num_items == 0) {427if (timeout == NULL)428goto out;429OSSL_sleep(ossl_time2ms(ossl_time_from_timeval(*timeout)));430goto out;431}432433/* Convert timeout to deadline. */434if (timeout == NULL)435deadline = ossl_time_infinite();436else if (timeout->tv_sec == 0 && timeout->tv_usec == 0)437deadline = ossl_time_zero();438else439deadline = ossl_time_add(ossl_time_now(),440ossl_time_from_timeval(*timeout));441442/* Loop until we have something to report. */443for (;;) {444/* Readout phase - poll current state of each item. */445if (!poll_readout(items, num_items, stride, do_tick, &result_count)) {446ok = 0;447goto out;448}449450/*451* If we got anything, or we are in immediate mode (zero timeout), or452* the deadline has expired, we're done.453*/454if (result_count > 0455|| ossl_time_is_zero(deadline) /* (avoids now call) */456|| ossl_time_compare(ossl_time_now(), deadline) >= 0)457goto out;458459/*460* Block until something is ready. Ignore NO_HANDLE_EVENTS from this461* point onwards.462*/463do_tick = 1;464#ifndef OPENSSL_NO_QUIC465if (!poll_block(items, num_items, stride, deadline, &result_count)) {466ok = 0;467goto out;468}469#endif470}471472/* TODO(QUIC POLLING): Support for polling FDs */473474out:475if (p_result_count != NULL)476*p_result_count = result_count;477478return ok;479}480481482