Coverage Report

Created: 2026-07-23 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl35/ssl/rio/rio_notifier.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 "internal/sockets.h"
11
#include <openssl/bio.h>
12
#include <openssl/err.h>
13
#include "internal/rio_notifier.h"
14
15
/*
16
 * Sets a socket as close-on-exec, except that this is a no-op if we are certain
17
 * we do not need to do this or the OS does not support the concept.
18
 */
19
static int set_cloexec(int fd)
20
0
{
21
#if !defined(SOCK_CLOEXEC) && defined(FD_CLOEXEC)
22
    return fcntl(fd, F_SETFD, FD_CLOEXEC) >= 0;
23
#else
24
0
    return 1;
25
0
#endif
26
0
}
27
28
#if defined(OPENSSL_SYS_WINDOWS)
29
30
static void ossl_wsa_cleanup(void)
31
{
32
    WSACleanup();
33
}
34
35
static int do_wsa_startup(void)
36
{
37
    WORD versionreq = 0x0202; /* Version 2.2 */
38
    WSADATA wsadata;
39
40
    if (WSAStartup(versionreq, &wsadata) != 0)
41
        return 0;
42
    return 1;
43
}
44
45
static ossl_inline int ensure_wsa_startup(void)
46
{
47
    return do_wsa_startup();
48
}
49
50
static void wsa_done(void)
51
{
52
    ossl_wsa_cleanup();
53
}
54
55
#endif
56
57
#if RIO_NOTIFIER_METHOD == RIO_NOTIFIER_METHOD_SOCKET
58
59
/* Create a close-on-exec socket. */
60
static int create_socket(int domain, int socktype, int protocol)
61
{
62
    int fd;
63
#if defined(OPENSSL_SYS_WINDOWS)
64
    static const int on = 1;
65
66
    /*
67
     * Use WSASocketA to create a socket which is immediately marked as
68
     * non-inheritable, avoiding race conditions if another thread is about to
69
     * call CreateProcess.
70
     * NOTE: windows xp (0x501) doesn't support the non-inheritance flag here
71
     * but preventing inheritance isn't mandatory, just a safety precaution
72
     * so we can get away with not including it for older platforms
73
     */
74
75
#ifdef WSA_FLAG_NO_HANDLE_INHERIT
76
    fd = (int)WSASocketA(domain, socktype, protocol, NULL, 0,
77
        WSA_FLAG_NO_HANDLE_INHERIT);
78
79
    /*
80
     * Its also possible that someone is building a binary on a newer windows
81
     * SDK, but running it on a runtime that doesn't support inheritance
82
     * suppression.  In that case the above will return INVALID_SOCKET, and
83
     * our response for those older platforms is to try the call again
84
     * without the flag
85
     */
86
    if (fd == INVALID_SOCKET)
87
        fd = (int)WSASocketA(domain, socktype, protocol, NULL, 0, 0);
88
#else
89
    fd = (int)WSASocketA(domain, socktype, protocol, NULL, 0, 0);
90
#endif
91
    if (fd == INVALID_SOCKET) {
92
        int err = get_last_socket_error();
93
94
        ERR_raise_data(ERR_LIB_SYS, err,
95
            "calling WSASocketA() = %d", err);
96
        return INVALID_SOCKET;
97
    }
98
99
    /* Prevent interference with the socket from other processes on Windows. */
100
    if (setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *)&on, sizeof(on)) < 0) {
101
        ERR_raise_data(ERR_LIB_SYS, get_last_socket_error(),
102
            "calling setsockopt()");
103
        BIO_closesocket(fd);
104
        return INVALID_SOCKET;
105
    }
106
107
#else
108
#if defined(SOCK_CLOEXEC)
109
    socktype |= SOCK_CLOEXEC;
110
#endif
111
112
    fd = BIO_socket(domain, socktype, protocol, 0);
113
    if (fd == INVALID_SOCKET) {
114
        ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(),
115
            "calling BIO_socket()");
116
        return INVALID_SOCKET;
117
    }
118
119
    /*
120
     * Make socket close-on-exec unless this was already done above at socket
121
     * creation time.
122
     */
123
    if (!set_cloexec(fd)) {
124
        ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(),
125
            "calling set_cloexec()");
126
        BIO_closesocket(fd);
127
        return INVALID_SOCKET;
128
    }
129
#endif
130
131
    return fd;
132
}
133
134
/*
135
 * The SOCKET notifier method manually creates a connected TCP socket pair by
136
 * temporarily creating a TCP listener on a random port and connecting back to
137
 * it.
138
 *
139
 * Win32 does not support socketpair(2), and Win32 pipes are not compatible with
140
 * Winsock select(2). This means our only means of making select(2) wakeable is
141
 * to artificially create a loopback TCP connection and send bytes to it.
142
 */
143
int ossl_rio_notifier_init(RIO_NOTIFIER *nfy)
144
{
145
    int rc, lfd = -1, rfd = -1, wfd = -1;
146
    struct sockaddr_in sa = { 0 }, accept_sa;
147
    socklen_t sa_len = sizeof(sa), accept_sa_len = sizeof(accept_sa);
148
149
#if defined(OPENSSL_SYS_WINDOWS)
150
    if (!ensure_wsa_startup()) {
151
        ERR_raise_data(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR,
152
            "Cannot start Windows sockets");
153
        return 0;
154
    }
155
#endif
156
    /* Create a close-on-exec socket. */
157
    lfd = create_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
158
    if (lfd == INVALID_SOCKET) {
159
        ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(),
160
            "calling create_socket()");
161
        return 0;
162
    }
163
164
    /* Bind the socket to a random loopback port. */
165
    sa.sin_family = AF_INET;
166
    sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
167
    rc = bind(lfd, (const struct sockaddr *)&sa, sizeof(sa));
168
    if (rc < 0) {
169
        ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(),
170
            "calling bind()");
171
        goto err;
172
    }
173
174
    /* Determine what random port was allocated. */
175
    rc = getsockname(lfd, (struct sockaddr *)&sa, &sa_len);
176
    if (rc < 0) {
177
        ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(),
178
            "calling getsockname()");
179
        goto err;
180
    }
181
182
    /* Start listening. */
183
    rc = listen(lfd, 1);
184
    if (rc < 0) {
185
        ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(),
186
            "calling listen()");
187
        goto err;
188
    }
189
190
    /* Create another socket to connect to the listener. */
191
    wfd = create_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
192
    if (wfd == INVALID_SOCKET) {
193
        ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(),
194
            "calling create_socket()");
195
        goto err;
196
    }
197
198
    /*
199
     * Disable Nagle's algorithm on the writer so that wakeups happen
200
     * immediately.
201
     */
202
    if (!BIO_set_tcp_ndelay(wfd, 1)) {
203
        ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(),
204
            "calling BIO_set_tcp_ndelay()");
205
        goto err;
206
    }
207
208
    /*
209
     * Connect the writer to the listener.
210
     */
211
    rc = connect(wfd, (struct sockaddr *)&sa, sizeof(sa));
212
    if (rc < 0) {
213
        ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(),
214
            "calling connect()");
215
        goto err;
216
    }
217
218
    /*
219
     * The connection accepted from the listener is the read side.
220
     */
221
    rfd = accept(lfd, (struct sockaddr *)&accept_sa, &accept_sa_len);
222
    if (rfd == INVALID_SOCKET) {
223
        ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(),
224
            "calling accept()");
225
        goto err;
226
    }
227
228
    rc = getsockname(wfd, (struct sockaddr *)&sa, &sa_len);
229
    if (rc < 0) {
230
        ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(),
231
            "calling getsockname()");
232
        goto err;
233
    }
234
235
    /* Close the listener, which we don't need anymore. */
236
    BIO_closesocket(lfd);
237
    lfd = -1;
238
239
    /*
240
     * Sanity check - ensure someone else didn't connect to our listener during
241
     * the brief window of possibility above.
242
     */
243
    if (accept_sa.sin_family != AF_INET || accept_sa.sin_port != sa.sin_port) {
244
        ERR_raise_data(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR,
245
            "connected address differs from accepted address");
246
        goto err;
247
    }
248
249
    /* Make both sides of the connection non-blocking. */
250
    if (!BIO_socket_nbio(rfd, 1)) {
251
        ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(),
252
            "calling BIO_socket_nbio()");
253
        goto err;
254
    }
255
256
    if (!BIO_socket_nbio(wfd, 1)) {
257
        ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(),
258
            "calling BIO_socket_nbio()");
259
        goto err;
260
    }
261
262
    nfy->rfd = rfd;
263
    nfy->wfd = wfd;
264
    return 1;
265
266
err:
267
    if (lfd != INVALID_SOCKET)
268
        BIO_closesocket(lfd);
269
    if (wfd != INVALID_SOCKET)
270
        BIO_closesocket(wfd);
271
    if (rfd != INVALID_SOCKET)
272
        BIO_closesocket(rfd);
273
    return 0;
274
}
275
276
#elif RIO_NOTIFIER_METHOD == RIO_NOTIFIER_METHOD_SOCKETPAIR
277
278
int ossl_rio_notifier_init(RIO_NOTIFIER *nfy)
279
0
{
280
0
    int fds[2], domain = AF_INET, type = SOCK_STREAM;
281
282
0
#if defined(SOCK_CLOEXEC)
283
0
    type |= SOCK_CLOEXEC;
284
0
#endif
285
0
#if defined(SOCK_NONBLOCK)
286
0
    type |= SOCK_NONBLOCK;
287
0
#endif
288
289
0
#if defined(OPENSSL_SYS_UNIX) && defined(AF_UNIX)
290
0
    domain = AF_UNIX;
291
0
#endif
292
293
0
    if (socketpair(domain, type, 0, fds) < 0) {
294
0
        ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(),
295
0
            "calling socketpair()");
296
0
        return 0;
297
0
    }
298
299
0
    if (!set_cloexec(fds[0]) || !set_cloexec(fds[1])) {
300
0
        ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(),
301
0
            "calling set_cloexec()");
302
0
        goto err;
303
0
    }
304
305
#if !defined(SOCK_NONBLOCK)
306
    if (!BIO_socket_nbio(fds[0], 1) || !BIO_socket_nbio(fds[1], 1)) {
307
        ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(),
308
            "calling BIO_socket_nbio()");
309
        goto err;
310
    }
311
#endif
312
313
0
    if (domain == AF_INET && !BIO_set_tcp_ndelay(fds[1], 1)) {
314
0
        ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(),
315
0
            "calling BIO_set_tcp_ndelay()");
316
0
        goto err;
317
0
    }
318
319
0
    nfy->rfd = fds[0];
320
0
    nfy->wfd = fds[1];
321
0
    return 1;
322
323
0
err:
324
0
    BIO_closesocket(fds[1]);
325
0
    BIO_closesocket(fds[0]);
326
0
    return 0;
327
0
}
328
329
#endif
330
331
void ossl_rio_notifier_cleanup(RIO_NOTIFIER *nfy)
332
0
{
333
0
    if (nfy->rfd < 0)
334
0
        return;
335
336
0
    BIO_closesocket(nfy->wfd);
337
0
    BIO_closesocket(nfy->rfd);
338
0
    nfy->rfd = nfy->wfd = -1;
339
0
}
340
341
int ossl_rio_notifier_signal(RIO_NOTIFIER *nfy)
342
0
{
343
0
    static const unsigned char ch = 0;
344
0
    ossl_ssize_t wr;
345
346
0
    do
347
        /*
348
         * Note: If wr returns 0 the buffer is already full so we don't need to
349
         * do anything.
350
         */
351
0
        wr = writesocket(nfy->wfd, (void *)&ch, sizeof(ch));
352
0
    while (wr < 0 && get_last_socket_error_is_eintr());
353
354
0
    return 1;
355
0
}
356
357
int ossl_rio_notifier_unsignal(RIO_NOTIFIER *nfy)
358
0
{
359
0
    unsigned char buf[16];
360
0
    ossl_ssize_t rd;
361
362
    /*
363
     * signal() might have been called multiple times. Drain the buffer until
364
     * it's empty.
365
     */
366
0
    do
367
0
        rd = readsocket(nfy->rfd, (void *)buf, sizeof(buf));
368
0
    while (rd == sizeof(buf)
369
0
        || (rd < 0 && get_last_socket_error_is_eintr()));
370
371
0
    if (rd < 0 && !BIO_fd_non_fatal_error(get_last_socket_error()))
372
0
        return 0;
373
374
0
    return 1;
375
0
}