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