/* * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include #include #include "internal/safe_math.h" #include "poll_builder.h" OSSL_SAFE_MATH_UNSIGNED(size_t, size_t) int ossl_rio_poll_builder_init(RIO_POLL_BUILDER *rpb) { #if RIO_POLL_METHOD == RIO_POLL_METHOD_NONE return 0; #elif RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT FD_ZERO(&rpb->rfd); FD_ZERO(&rpb->wfd); FD_ZERO(&rpb->efd); rpb->hwm_fd = -1; #elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL rpb->pfd_heap = NULL; rpb->pfd_num = 0; rpb->pfd_alloc = OSSL_NELEM(rpb->pfds); #endif return 1; } void ossl_rio_poll_builder_cleanup(RIO_POLL_BUILDER *rpb) { if (rpb == NULL) return; #if RIO_POLL_METHOD == RIO_POLL_METHOD_POLL OPENSSL_free(rpb->pfd_heap); #endif } #if RIO_POLL_METHOD == RIO_POLL_METHOD_POLL static int rpb_ensure_alloc(RIO_POLL_BUILDER *rpb, size_t alloc) { struct pollfd *pfd_heap_new; size_t total_size; int error = 0; if (alloc <= rpb->pfd_alloc) return 1; total_size = safe_mul_size_t(alloc, sizeof(struct pollfd), &error); if (error) return 0; pfd_heap_new = OPENSSL_realloc(rpb->pfd_heap, total_size); if (pfd_heap_new == NULL) return 0; if (rpb->pfd_heap == NULL) { /* Copy the contents of the stacked array. */ memcpy(pfd_heap_new, rpb->pfds, sizeof(rpb->pfds)); } rpb->pfd_heap = pfd_heap_new; rpb->pfd_alloc = alloc; return 1; } #endif int ossl_rio_poll_builder_add_fd(RIO_POLL_BUILDER *rpb, int fd, int want_read, int want_write) { #if RIO_POLL_METHOD == RIO_POLL_METHOD_POLL size_t i; struct pollfd *pfds = (rpb->pfd_heap != NULL ? rpb->pfd_heap : rpb->pfds); struct pollfd *pfd; #endif if (fd < 0) return 0; #if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT # ifndef OPENSSL_SYS_WINDOWS /* * On Windows there is no relevant limit to the magnitude of a fd value (see * above). On *NIX the fd_set uses a bitmap and we must check the limit. */ if (fd >= FD_SETSIZE) return 0; # endif if (want_read) openssl_fdset(fd, &rpb->rfd); if (want_write) openssl_fdset(fd, &rpb->wfd); openssl_fdset(fd, &rpb->efd); if (fd > rpb->hwm_fd) rpb->hwm_fd = fd; return 1; #elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL for (i = 0; i < rpb->pfd_num; ++i) { pfd = &pfds[i]; if (pfd->fd == -1 || pfd->fd == fd) break; } if (i >= rpb->pfd_alloc) { if (!rpb_ensure_alloc(rpb, rpb->pfd_alloc * 2)) return 0; pfds = rpb->pfd_heap; } assert((rpb->pfd_heap != NULL && rpb->pfd_heap == pfds) || (rpb->pfd_heap == NULL && rpb->pfds == pfds)); assert(i <= rpb->pfd_num && rpb->pfd_num <= rpb->pfd_alloc); pfds[i].fd = fd; pfds[i].events = 0; if (want_read) pfds[i].events |= POLLIN; if (want_write) pfds[i].events |= POLLOUT; if (i == rpb->pfd_num) ++rpb->pfd_num; return 1; #endif } int ossl_rio_poll_builder_poll(RIO_POLL_BUILDER *rpb, OSSL_TIME deadline) { int rc; #if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT do { struct timeval timeout, *p_timeout = &timeout; /* * select expects a timeout, not a deadline, so do the conversion. * Update for each call to ensure the correct value is used if we repeat * due to EINTR. */ if (ossl_time_is_infinite(deadline)) p_timeout = NULL; else /* * ossl_time_subtract saturates to zero so we don't need to check if * now > deadline. */ timeout = ossl_time_to_timeval(ossl_time_subtract(deadline, ossl_time_now())); rc = select(rpb->hwm_fd + 1, &rpb->rfd, &rpb->wfd, &rpb->efd, p_timeout); } while (rc == -1 && get_last_socket_error_is_eintr()); #elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL do { int timeout_ms; if (ossl_time_is_infinite(deadline)) timeout_ms = -1; else timeout_ms = ossl_time2ms(ossl_time_subtract(deadline, ossl_time_now())); rc = poll(rpb->pfd_heap != NULL ? rpb->pfd_heap : rpb->pfds, rpb->pfd_num, timeout_ms); } while (rc == -1 && get_last_socket_error_is_eintr()); #endif return rc < 0 ? 0 : 1; }