Coverage Report

Created: 2026-08-31 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl/ssl/rio/poll_builder.c
Line
Count
Source
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
15
OSSL_SAFE_MATH_UNSIGNED(size_t, size_t)
16
17
int ossl_rio_poll_builder_init(RIO_POLL_BUILDER *rpb)
18
0
{
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
0
    rpb->pfd_heap = NULL;
28
0
    rpb->pfd_num = 0;
29
0
    rpb->pfd_alloc = OSSL_NELEM(rpb->pfds);
30
0
#endif
31
0
    return 1;
32
0
}
33
34
void ossl_rio_poll_builder_cleanup(RIO_POLL_BUILDER *rpb)
35
0
{
36
0
    if (rpb == NULL)
37
0
        return;
38
39
0
#if RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
40
0
    OPENSSL_free(rpb->pfd_heap);
41
0
#endif
42
0
}
43
44
#if RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
45
static int rpb_ensure_alloc(RIO_POLL_BUILDER *rpb, size_t alloc)
46
0
{
47
0
    struct pollfd *pfd_heap_new;
48
0
    size_t total_size;
49
0
    int error = 0;
50
51
0
    if (alloc <= rpb->pfd_alloc)
52
0
        return 1;
53
54
0
    total_size = safe_mul_size_t(alloc, sizeof(struct pollfd), &error);
55
0
    if (error)
56
0
        return 0;
57
58
0
    pfd_heap_new = OPENSSL_realloc(rpb->pfd_heap, total_size);
59
0
    if (pfd_heap_new == NULL)
60
0
        return 0;
61
62
0
    if (rpb->pfd_heap == NULL) {
63
        /* Copy the contents of the stacked array. */
64
0
        memcpy(pfd_heap_new, rpb->pfds, sizeof(rpb->pfds));
65
0
    }
66
0
    rpb->pfd_heap = pfd_heap_new;
67
0
    rpb->pfd_alloc = alloc;
68
0
    return 1;
69
0
}
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
0
{
75
0
#if RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
76
0
    size_t i;
77
0
    struct pollfd *pfds = (rpb->pfd_heap != NULL ? rpb->pfd_heap : rpb->pfds);
78
0
    struct pollfd *pfd;
79
0
#endif
80
81
0
    if (fd < 0)
82
0
        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
0
    for (i = 0; i < rpb->pfd_num; ++i) {
109
0
        pfd = &pfds[i];
110
111
0
        if (pfd->fd == -1 || pfd->fd == fd)
112
0
            break;
113
0
    }
114
115
0
    if (i >= rpb->pfd_alloc) {
116
0
        if (!rpb_ensure_alloc(rpb, rpb->pfd_alloc * 2))
117
0
            return 0;
118
0
        pfds = rpb->pfd_heap;
119
0
    }
120
121
0
    assert((rpb->pfd_heap != NULL && rpb->pfd_heap == pfds) || (rpb->pfd_heap == NULL && rpb->pfds == pfds));
122
0
    assert(i <= rpb->pfd_num && rpb->pfd_num <= rpb->pfd_alloc);
123
    /* Check the index first because an appended entry is uninitialised. */
124
0
    if (i == rpb->pfd_num || pfds[i].fd == -1)
125
0
        pfds[i].events = 0;
126
0
    pfds[i].fd = fd;
127
128
0
    if (want_read)
129
0
        pfds[i].events |= POLLIN;
130
0
    if (want_write)
131
0
        pfds[i].events |= POLLOUT;
132
133
0
    if (i == rpb->pfd_num)
134
0
        ++rpb->pfd_num;
135
136
0
    return 1;
137
0
#endif
138
0
}
139
140
/* Returns 1 if no file descriptors have been added to the poll builder. */
141
static int rio_poll_builder_is_empty(const RIO_POLL_BUILDER *rpb)
142
0
{
143
#if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT
144
    return rpb->hwm_fd < 0;
145
#elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
146
    return rpb->pfd_num == 0;
147
#else
148
    return 1;
149
#endif
150
0
}
151
152
int ossl_rio_poll_builder_poll(RIO_POLL_BUILDER *rpb, OSSL_TIME deadline)
153
0
{
154
0
    int rc;
155
156
    /*
157
     * Waiting with no file descriptors is legitimate: a DTLS connection whose
158
     * BIO cannot provide a poll descriptor has no readiness to wait for, but
159
     * still has a retransmission deadline to wake for. Handle that here rather
160
     * than leaving it to the OS, because poll() treats an empty descriptor set
161
     * as a plain sleep whereas Windows' select() rejects it outright.
162
     *
163
     * With no descriptors and no deadline nothing could ever wake us, so that
164
     * is a caller error rather than an indefinite sleep.
165
     */
166
0
    if (rio_poll_builder_is_empty(rpb)) {
167
0
        if (ossl_time_is_infinite(deadline))
168
0
            return 0;
169
170
0
        OSSL_sleep(ossl_time2ms(ossl_time_subtract(deadline, ossl_time_now())));
171
0
        return 1;
172
0
    }
173
174
#if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT
175
    do {
176
        struct timeval timeout, *p_timeout = &timeout;
177
178
        /*
179
         * select expects a timeout, not a deadline, so do the conversion.
180
         * Update for each call to ensure the correct value is used if we repeat
181
         * due to EINTR.
182
         */
183
        if (ossl_time_is_infinite(deadline))
184
            p_timeout = NULL;
185
        else
186
            /*
187
             * ossl_time_subtract saturates to zero so we don't need to check if
188
             * now > deadline.
189
             */
190
            timeout = ossl_time_to_timeval(ossl_time_subtract(deadline,
191
                ossl_time_now()));
192
193
        rc = select(rpb->hwm_fd + 1, &rpb->rfd, &rpb->wfd, &rpb->efd, p_timeout);
194
    } while (rc == -1 && get_last_socket_error_is_eintr());
195
#elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
196
0
    do {
197
0
        int timeout_ms;
198
199
0
        if (ossl_time_is_infinite(deadline))
200
0
            timeout_ms = -1;
201
0
        else
202
0
            timeout_ms = ossl_time2ms(ossl_time_subtract(deadline,
203
0
                ossl_time_now()));
204
205
0
        rc = poll(rpb->pfd_heap != NULL ? rpb->pfd_heap : rpb->pfds,
206
0
            rpb->pfd_num, timeout_ms);
207
0
    } while (rc == -1 && get_last_socket_error_is_eintr());
208
0
#endif
209
210
0
    return rc < 0 ? 0 : 1;
211
0
}