Path: blob/main/crypto/openssl/ssl/rio/poll_immediate.c
48261 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", item->desc.type);267FAIL_ITEM(i);268}269}270271out:272if (!ok)273postpoll_translation_cleanup(items, i, stride, wctx);274275*p_earliest_wakeup_deadline = earliest_wakeup_deadline;276*p_result_count = result_count;277return ok;278}279280static int poll_block(SSL_POLL_ITEM *items,281size_t num_items,282size_t stride,283OSSL_TIME user_deadline,284size_t *p_result_count)285{286int ok = 0, abort_blocking = 0;287RIO_POLL_BUILDER rpb;288QUIC_REACTOR_WAIT_CTX wctx;289OSSL_TIME earliest_wakeup_deadline;290291/*292* Blocking is somewhat involved and involves the following steps:293*294* - Translation, in which the various logical items (SSL objects, etc.) to295* be polled are translated into items an OS polling API understands.296*297* - Synchronisation bookkeeping. This ensures that we can be woken up298* not just by readiness of any underlying file descriptor distilled from299* the provided items but also by other threads, which might do work300* on a relevant QUIC object to cause the object to be ready without the301* underlying file descriptor ever becoming ready from our perspective.302*303* - The blocking call to the OS polling API.304*305* - Currently we do not do reverse translation but simply call306* poll_readout() again to read out all readiness state for all307* descriptors which the user passed.308*309* TODO(QUIC POLLING): In the future we will do reverse translation here310* also to facilitate a more efficient readout.311*/312ossl_quic_reactor_wait_ctx_init(&wctx);313ossl_rio_poll_builder_init(&rpb);314315if (!poll_translate(items, num_items, stride, &wctx, &rpb,316&earliest_wakeup_deadline,317&abort_blocking,318p_result_count))319goto out;320321if (abort_blocking)322goto out;323324earliest_wakeup_deadline = ossl_time_min(earliest_wakeup_deadline,325user_deadline);326327ok = ossl_rio_poll_builder_poll(&rpb, earliest_wakeup_deadline);328329postpoll_translation_cleanup(items, num_items, stride, &wctx);330331out:332ossl_rio_poll_builder_cleanup(&rpb);333ossl_quic_reactor_wait_ctx_cleanup(&wctx);334return ok;335}336#endif337338static int poll_readout(SSL_POLL_ITEM *items,339size_t num_items,340size_t stride,341int do_tick,342size_t *p_result_count)343{344int ok = 1;345size_t i, result_count = 0;346SSL_POLL_ITEM *item;347SSL *ssl;348#ifndef OPENSSL_NO_QUIC349uint64_t events;350#endif351uint64_t revents;352353for (i = 0; i < num_items; ++i) {354item = &ITEM_N(items, stride, i);355#ifndef OPENSSL_NO_QUIC356events = item->events;357#endif358revents = 0;359360switch (item->desc.type) {361case BIO_POLL_DESCRIPTOR_TYPE_SSL:362ssl = item->desc.value.ssl;363if (ssl == NULL)364/* NULL items are no-ops and have revents reported as 0 */365break;366367switch (ssl->type) {368#ifndef OPENSSL_NO_QUIC369case SSL_TYPE_QUIC_LISTENER:370case SSL_TYPE_QUIC_CONNECTION:371case SSL_TYPE_QUIC_XSO:372if (!ossl_quic_conn_poll_events(ssl, events, do_tick, &revents))373/* above call raises ERR */374FAIL_ITEM(i);375376if (revents != 0)377++result_count;378379break;380#endif381382default:383ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,384"SSL_poll currently only supports QUIC SSL "385"objects");386FAIL_ITEM(i);387}388break;389case BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD:390ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,391"SSL_poll currently does not support polling "392"sockets");393FAIL_ITEM(i);394default:395ERR_raise_data(ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED,396"SSL_poll does not support unknown poll descriptor "397"type %d", item->desc.type);398FAIL_ITEM(i);399}400401item->revents = revents;402}403404out:405if (p_result_count != NULL)406*p_result_count = result_count;407408return ok;409}410411int SSL_poll(SSL_POLL_ITEM *items,412size_t num_items,413size_t stride,414const struct timeval *timeout,415uint64_t flags,416size_t *p_result_count)417{418int ok = 1;419size_t result_count = 0;420ossl_unused int do_tick = ((flags & SSL_POLL_FLAG_NO_HANDLE_EVENTS) == 0);421OSSL_TIME deadline;422423/* Trivial case. */424if (num_items == 0) {425if (timeout == NULL)426goto out;427OSSL_sleep(ossl_time2ms(ossl_time_from_timeval(*timeout)));428goto out;429}430431/* Convert timeout to deadline. */432if (timeout == NULL)433deadline = ossl_time_infinite();434else if (timeout->tv_sec == 0 && timeout->tv_usec == 0)435deadline = ossl_time_zero();436else437deadline = ossl_time_add(ossl_time_now(),438ossl_time_from_timeval(*timeout));439440/* Loop until we have something to report. */441for (;;) {442/* Readout phase - poll current state of each item. */443if (!poll_readout(items, num_items, stride, do_tick, &result_count)) {444ok = 0;445goto out;446}447448/*449* If we got anything, or we are in immediate mode (zero timeout), or450* the deadline has expired, we're done.451*/452if (result_count > 0453|| ossl_time_is_zero(deadline) /* (avoids now call) */454|| ossl_time_compare(ossl_time_now(), deadline) >= 0)455goto out;456457/*458* Block until something is ready. Ignore NO_HANDLE_EVENTS from this459* point onwards.460*/461do_tick = 1;462#ifndef OPENSSL_NO_QUIC463if (!poll_block(items, num_items, stride, deadline, &result_count)) {464ok = 0;465goto out;466}467#endif468}469470/* TODO(QUIC POLLING): Support for polling FDs */471472out:473if (p_result_count != NULL)474*p_result_count = result_count;475476return ok;477}478479480