1 /*
2 * Copyright 2024-2025 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
OSSL_SAFE_MATH_UNSIGNED(size_t,size_t)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
ossl_rio_poll_builder_cleanup(RIO_POLL_BUILDER * rpb)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
rpb_ensure_alloc(RIO_POLL_BUILDER * rpb,size_t alloc)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
ossl_rio_poll_builder_add_fd(RIO_POLL_BUILDER * rpb,int fd,int want_read,int want_write)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 pfds[i].fd = fd;
124 pfds[i].events = 0;
125
126 if (want_read)
127 pfds[i].events |= POLLIN;
128 if (want_write)
129 pfds[i].events |= POLLOUT;
130
131 if (i == rpb->pfd_num)
132 ++rpb->pfd_num;
133
134 return 1;
135 #endif
136 }
137
ossl_rio_poll_builder_poll(RIO_POLL_BUILDER * rpb,OSSL_TIME deadline)138 int ossl_rio_poll_builder_poll(RIO_POLL_BUILDER *rpb, OSSL_TIME deadline)
139 {
140 int rc;
141
142 #if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT
143 do {
144 struct timeval timeout, *p_timeout = &timeout;
145
146 /*
147 * select expects a timeout, not a deadline, so do the conversion.
148 * Update for each call to ensure the correct value is used if we repeat
149 * due to EINTR.
150 */
151 if (ossl_time_is_infinite(deadline))
152 p_timeout = NULL;
153 else
154 /*
155 * ossl_time_subtract saturates to zero so we don't need to check if
156 * now > deadline.
157 */
158 timeout = ossl_time_to_timeval(ossl_time_subtract(deadline,
159 ossl_time_now()));
160
161 rc = select(rpb->hwm_fd + 1, &rpb->rfd, &rpb->wfd, &rpb->efd, p_timeout);
162 } while (rc == -1 && get_last_socket_error_is_eintr());
163 #elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
164 do {
165 int timeout_ms;
166
167 if (ossl_time_is_infinite(deadline))
168 timeout_ms = -1;
169 else
170 timeout_ms = ossl_time2ms(ossl_time_subtract(deadline,
171 ossl_time_now()));
172
173 rc = poll(rpb->pfd_heap != NULL ? rpb->pfd_heap : rpb->pfds,
174 rpb->pfd_num, timeout_ms);
175 } while (rc == -1 && get_last_socket_error_is_eintr());
176 #endif
177
178 return rc < 0 ? 0 : 1;
179 }
180