1 1.1 christos /* 2 1.1 christos * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved. 3 1.1 christos * 4 1.1 christos * Licensed under the Apache License 2.0 (the "License"). You may not use 5 1.1 christos * this file except in compliance with the License. You can obtain a copy 6 1.1 christos * in the file LICENSE in the source distribution or at 7 1.1 christos * https://www.openssl.org/source/license.html 8 1.1 christos */ 9 1.1 christos 10 1.1 christos #include <assert.h> 11 1.1 christos #include <errno.h> 12 1.1 christos #include "internal/safe_math.h" 13 1.1 christos #include "poll_builder.h" 14 1.1 christos 15 1.1 christos OSSL_SAFE_MATH_UNSIGNED(size_t, size_t) 16 1.1 christos 17 1.1 christos int ossl_rio_poll_builder_init(RIO_POLL_BUILDER *rpb) 18 1.1 christos { 19 1.1 christos #if RIO_POLL_METHOD == RIO_POLL_METHOD_NONE 20 1.1 christos return 0; 21 1.1 christos #elif RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT 22 1.1 christos FD_ZERO(&rpb->rfd); 23 1.1 christos FD_ZERO(&rpb->wfd); 24 1.1 christos FD_ZERO(&rpb->efd); 25 1.1.1.2 christos rpb->hwm_fd = -1; 26 1.1 christos #elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL 27 1.1.1.2 christos rpb->pfd_heap = NULL; 28 1.1.1.2 christos rpb->pfd_num = 0; 29 1.1.1.2 christos rpb->pfd_alloc = OSSL_NELEM(rpb->pfds); 30 1.1 christos #endif 31 1.1 christos return 1; 32 1.1 christos } 33 1.1 christos 34 1.1 christos void ossl_rio_poll_builder_cleanup(RIO_POLL_BUILDER *rpb) 35 1.1 christos { 36 1.1 christos if (rpb == NULL) 37 1.1 christos return; 38 1.1 christos 39 1.1 christos #if RIO_POLL_METHOD == RIO_POLL_METHOD_POLL 40 1.1 christos OPENSSL_free(rpb->pfd_heap); 41 1.1 christos #endif 42 1.1 christos } 43 1.1 christos 44 1.1 christos #if RIO_POLL_METHOD == RIO_POLL_METHOD_POLL 45 1.1 christos static int rpb_ensure_alloc(RIO_POLL_BUILDER *rpb, size_t alloc) 46 1.1 christos { 47 1.1 christos struct pollfd *pfd_heap_new; 48 1.1 christos size_t total_size; 49 1.1 christos int error = 0; 50 1.1 christos 51 1.1 christos if (alloc <= rpb->pfd_alloc) 52 1.1 christos return 1; 53 1.1 christos 54 1.1 christos total_size = safe_mul_size_t(alloc, sizeof(struct pollfd), &error); 55 1.1 christos if (error) 56 1.1 christos return 0; 57 1.1 christos 58 1.1 christos pfd_heap_new = OPENSSL_realloc(rpb->pfd_heap, total_size); 59 1.1 christos if (pfd_heap_new == NULL) 60 1.1 christos return 0; 61 1.1 christos 62 1.1 christos if (rpb->pfd_heap == NULL) { 63 1.1 christos /* Copy the contents of the stacked array. */ 64 1.1 christos memcpy(pfd_heap_new, rpb->pfds, sizeof(rpb->pfds)); 65 1.1 christos } 66 1.1.1.2 christos rpb->pfd_heap = pfd_heap_new; 67 1.1.1.2 christos rpb->pfd_alloc = alloc; 68 1.1 christos return 1; 69 1.1 christos } 70 1.1 christos #endif 71 1.1 christos 72 1.1 christos int ossl_rio_poll_builder_add_fd(RIO_POLL_BUILDER *rpb, int fd, 73 1.1.1.2 christos int want_read, int want_write) 74 1.1 christos { 75 1.1 christos #if RIO_POLL_METHOD == RIO_POLL_METHOD_POLL 76 1.1 christos size_t i; 77 1.1 christos struct pollfd *pfds = (rpb->pfd_heap != NULL ? rpb->pfd_heap : rpb->pfds); 78 1.1 christos struct pollfd *pfd; 79 1.1 christos #endif 80 1.1 christos 81 1.1 christos if (fd < 0) 82 1.1 christos return 0; 83 1.1 christos 84 1.1 christos #if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT 85 1.1 christos 86 1.1.1.2 christos #ifndef OPENSSL_SYS_WINDOWS 87 1.1 christos /* 88 1.1 christos * On Windows there is no relevant limit to the magnitude of a fd value (see 89 1.1 christos * above). On *NIX the fd_set uses a bitmap and we must check the limit. 90 1.1 christos */ 91 1.1 christos if (fd >= FD_SETSIZE) 92 1.1 christos return 0; 93 1.1.1.2 christos #endif 94 1.1 christos 95 1.1 christos if (want_read) 96 1.1 christos openssl_fdset(fd, &rpb->rfd); 97 1.1 christos 98 1.1 christos if (want_write) 99 1.1 christos openssl_fdset(fd, &rpb->wfd); 100 1.1 christos 101 1.1 christos openssl_fdset(fd, &rpb->efd); 102 1.1 christos if (fd > rpb->hwm_fd) 103 1.1 christos rpb->hwm_fd = fd; 104 1.1 christos 105 1.1 christos return 1; 106 1.1 christos 107 1.1 christos #elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL 108 1.1 christos for (i = 0; i < rpb->pfd_num; ++i) { 109 1.1 christos pfd = &pfds[i]; 110 1.1 christos 111 1.1 christos if (pfd->fd == -1 || pfd->fd == fd) 112 1.1 christos break; 113 1.1 christos } 114 1.1 christos 115 1.1 christos if (i >= rpb->pfd_alloc) { 116 1.1 christos if (!rpb_ensure_alloc(rpb, rpb->pfd_alloc * 2)) 117 1.1 christos return 0; 118 1.1 christos pfds = rpb->pfd_heap; 119 1.1 christos } 120 1.1 christos 121 1.1.1.2 christos assert((rpb->pfd_heap != NULL && rpb->pfd_heap == pfds) || (rpb->pfd_heap == NULL && rpb->pfds == pfds)); 122 1.1 christos assert(i <= rpb->pfd_num && rpb->pfd_num <= rpb->pfd_alloc); 123 1.1.1.2 christos pfds[i].fd = fd; 124 1.1.1.2 christos pfds[i].events = 0; 125 1.1 christos 126 1.1 christos if (want_read) 127 1.1 christos pfds[i].events |= POLLIN; 128 1.1 christos if (want_write) 129 1.1 christos pfds[i].events |= POLLOUT; 130 1.1 christos 131 1.1 christos if (i == rpb->pfd_num) 132 1.1 christos ++rpb->pfd_num; 133 1.1 christos 134 1.1 christos return 1; 135 1.1 christos #endif 136 1.1 christos } 137 1.1 christos 138 1.1 christos int ossl_rio_poll_builder_poll(RIO_POLL_BUILDER *rpb, OSSL_TIME deadline) 139 1.1 christos { 140 1.1 christos int rc; 141 1.1 christos 142 1.1 christos #if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT 143 1.1 christos do { 144 1.1 christos struct timeval timeout, *p_timeout = &timeout; 145 1.1 christos 146 1.1 christos /* 147 1.1 christos * select expects a timeout, not a deadline, so do the conversion. 148 1.1 christos * Update for each call to ensure the correct value is used if we repeat 149 1.1 christos * due to EINTR. 150 1.1 christos */ 151 1.1 christos if (ossl_time_is_infinite(deadline)) 152 1.1 christos p_timeout = NULL; 153 1.1 christos else 154 1.1 christos /* 155 1.1 christos * ossl_time_subtract saturates to zero so we don't need to check if 156 1.1 christos * now > deadline. 157 1.1 christos */ 158 1.1 christos timeout = ossl_time_to_timeval(ossl_time_subtract(deadline, 159 1.1.1.2 christos ossl_time_now())); 160 1.1 christos 161 1.1 christos rc = select(rpb->hwm_fd + 1, &rpb->rfd, &rpb->wfd, &rpb->efd, p_timeout); 162 1.1 christos } while (rc == -1 && get_last_socket_error_is_eintr()); 163 1.1 christos #elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL 164 1.1 christos do { 165 1.1 christos int timeout_ms; 166 1.1 christos 167 1.1 christos if (ossl_time_is_infinite(deadline)) 168 1.1 christos timeout_ms = -1; 169 1.1 christos else 170 1.1 christos timeout_ms = ossl_time2ms(ossl_time_subtract(deadline, 171 1.1.1.2 christos ossl_time_now())); 172 1.1 christos 173 1.1 christos rc = poll(rpb->pfd_heap != NULL ? rpb->pfd_heap : rpb->pfds, 174 1.1.1.2 christos rpb->pfd_num, timeout_ms); 175 1.1 christos } while (rc == -1 && get_last_socket_error_is_eintr()); 176 1.1 christos #endif 177 1.1 christos 178 1.1 christos return rc < 0 ? 0 : 1; 179 1.1 christos } 180