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