Coverage Report

Created: 2026-04-12 07:08

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