Bitcoin Core Fuzz Coverage Report

Coverage Report

Created: 2026-05-28 15:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/Users/brunogarcia/projects/bitcoin-core-dev/src/util/sock.cpp
Line
Count
Source
1
// Copyright (c) 2020-present The Bitcoin Core developers
2
// Distributed under the MIT software license, see the accompanying
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5
#include <util/sock.h>
6
7
#include <compat/compat.h>
8
#include <span.h>
9
#include <tinyformat.h>
10
#include <util/check.h>
11
#include <util/log.h>
12
#include <util/syserror.h>
13
#include <util/threadinterrupt.h>
14
#include <util/time.h>
15
16
#include <algorithm>
17
#include <compare>
18
#include <exception>
19
#include <memory>
20
#include <stdexcept>
21
#include <string>
22
#include <utility>
23
#include <vector>
24
25
#ifdef USE_POLL
26
#include <poll.h>
27
#endif
28
29
static inline bool IOErrorIsPermanent(int err)
30
0
{
31
0
    return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK && err != WSAEINPROGRESS;
Line
Count
Source
62
0
#define WSAEAGAIN           EAGAIN
    return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK && err != WSAEINPROGRESS;
Line
Count
Source
64
0
#define WSAEINTR            EINTR
    return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK && err != WSAEINPROGRESS;
Line
Count
Source
61
0
#define WSAEWOULDBLOCK      EWOULDBLOCK
    return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK && err != WSAEINPROGRESS;
Line
Count
Source
65
0
#define WSAEINPROGRESS      EINPROGRESS
32
0
}
33
34
3.75k
Sock::Sock(SOCKET s) : m_socket(s) {}
35
36
Sock::Sock(Sock&& other)
37
0
{
38
0
    m_socket = other.m_socket;
39
0
    other.m_socket = INVALID_SOCKET;
Line
Count
Source
67
0
#define INVALID_SOCKET      (SOCKET)(~0)
40
0
}
41
42
3.75k
Sock::~Sock() { Close(); }
43
44
Sock& Sock::operator=(Sock&& other)
45
0
{
46
0
    Close();
47
0
    m_socket = other.m_socket;
48
0
    other.m_socket = INVALID_SOCKET;
Line
Count
Source
67
0
#define INVALID_SOCKET      (SOCKET)(~0)
49
0
    return *this;
50
0
}
51
52
ssize_t Sock::Send(const void* data, size_t len, int flags) const
53
0
{
54
0
    return send(m_socket, static_cast<const char*>(data), len, flags);
55
0
}
56
57
ssize_t Sock::Recv(void* buf, size_t len, int flags) const
58
0
{
59
0
    return recv(m_socket, static_cast<char*>(buf), len, flags);
60
0
}
61
62
int Sock::Connect(const sockaddr* addr, socklen_t addr_len) const
63
0
{
64
0
    return connect(m_socket, addr, addr_len);
65
0
}
66
67
int Sock::Bind(const sockaddr* addr, socklen_t addr_len) const
68
0
{
69
0
    return bind(m_socket, addr, addr_len);
70
0
}
71
72
int Sock::Listen(int backlog) const
73
0
{
74
0
    return listen(m_socket, backlog);
75
0
}
76
77
std::unique_ptr<Sock> Sock::Accept(sockaddr* addr, socklen_t* addr_len) const
78
0
{
79
#ifdef WIN32
80
    static constexpr auto ERR = INVALID_SOCKET;
81
#else
82
0
    static constexpr auto ERR = SOCKET_ERROR;
Line
Count
Source
68
0
#define SOCKET_ERROR        -1
83
0
#endif
84
85
0
    std::unique_ptr<Sock> sock;
86
87
0
    const auto socket = accept(m_socket, addr, addr_len);
88
0
    if (socket != ERR) {
89
0
        try {
90
0
            sock = std::make_unique<Sock>(socket);
91
0
        } catch (const std::exception&) {
92
#ifdef WIN32
93
            closesocket(socket);
94
#else
95
0
            close(socket);
96
0
#endif
97
0
        }
98
0
    }
99
100
0
    return sock;
101
0
}
102
103
int Sock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
104
0
{
105
0
    return getsockopt(m_socket, level, opt_name, static_cast<char*>(opt_val), opt_len);
106
0
}
107
108
int Sock::SetSockOpt(int level, int opt_name, const void* opt_val, socklen_t opt_len) const
109
0
{
110
0
    return setsockopt(m_socket, level, opt_name, static_cast<const char*>(opt_val), opt_len);
111
0
}
112
113
int Sock::GetSockName(sockaddr* name, socklen_t* name_len) const
114
0
{
115
0
    return getsockname(m_socket, name, name_len);
116
0
}
117
118
bool Sock::SetNonBlocking() const
119
0
{
120
#ifdef WIN32
121
    u_long on{1};
122
    if (ioctlsocket(m_socket, FIONBIO, &on) == SOCKET_ERROR) {
123
        return false;
124
    }
125
#else
126
0
    const int flags{fcntl(m_socket, F_GETFL, 0)};
127
0
    if (flags == SOCKET_ERROR) {
Line
Count
Source
68
0
#define SOCKET_ERROR        -1
128
0
        return false;
129
0
    }
130
0
    if (fcntl(m_socket, F_SETFL, flags | O_NONBLOCK) == SOCKET_ERROR) {
Line
Count
Source
68
0
#define SOCKET_ERROR        -1
131
0
        return false;
132
0
    }
133
0
#endif
134
0
    return true;
135
0
}
136
137
bool Sock::IsSelectable() const
138
0
{
139
#if defined(USE_POLL) || defined(WIN32)
140
    return true;
141
#else
142
0
    return m_socket < FD_SETSIZE;
143
0
#endif
144
0
}
145
146
bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
147
0
{
148
    // We need a `shared_ptr` holding `this` for `WaitMany()`, but don't want
149
    // `this` to be destroyed when the `shared_ptr` goes out of scope at the
150
    // end of this function.
151
    // Create it with an aliasing shared_ptr that points to `this` without
152
    // owning it.
153
0
    std::shared_ptr<const Sock> shared{std::shared_ptr<const Sock>{}, this};
154
155
0
    EventsPerSock events_per_sock{std::make_pair(shared, Events{requested})};
156
157
0
    if (!WaitMany(timeout, events_per_sock)) {
158
0
        return false;
159
0
    }
160
161
0
    if (occurred != nullptr) {
162
0
        *occurred = events_per_sock.begin()->second.occurred;
163
0
    }
164
165
0
    return true;
166
0
}
167
168
bool Sock::WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const
169
0
{
170
#ifdef USE_POLL
171
    std::vector<pollfd> pfds;
172
    for (const auto& [sock, events] : events_per_sock) {
173
        pfds.emplace_back();
174
        auto& pfd = pfds.back();
175
        pfd.fd = sock->m_socket;
176
        if (events.requested & RECV) {
177
            pfd.events |= POLLIN;
178
        }
179
        if (events.requested & SEND) {
180
            pfd.events |= POLLOUT;
181
        }
182
    }
183
184
    if (poll(pfds.data(), pfds.size(), count_milliseconds(timeout)) == SOCKET_ERROR) {
185
        return false;
186
    }
187
188
    assert(pfds.size() == events_per_sock.size());
189
    size_t i{0};
190
    for (auto& [sock, events] : events_per_sock) {
191
        assert(sock->m_socket == static_cast<SOCKET>(pfds[i].fd));
192
        events.occurred = 0;
193
        if (pfds[i].revents & POLLIN) {
194
            events.occurred |= RECV;
195
        }
196
        if (pfds[i].revents & POLLOUT) {
197
            events.occurred |= SEND;
198
        }
199
        if (pfds[i].revents & (POLLERR | POLLHUP)) {
200
            events.occurred |= ERR;
201
        }
202
        ++i;
203
    }
204
205
    return true;
206
#else
207
0
    fd_set recv;
208
0
    fd_set send;
209
0
    fd_set err;
210
0
    FD_ZERO(&recv);
211
0
    FD_ZERO(&send);
212
0
    FD_ZERO(&err);
213
0
    SOCKET socket_max{0};
214
215
0
    for (const auto& [sock, events] : events_per_sock) {
216
0
        if (!sock->IsSelectable()) {
217
0
            return false;
218
0
        }
219
0
        const auto& s = sock->m_socket;
220
0
        if (events.requested & RECV) {
221
0
            FD_SET(s, &recv);
222
0
        }
223
0
        if (events.requested & SEND) {
224
0
            FD_SET(s, &send);
225
0
        }
226
0
        FD_SET(s, &err);
227
0
        socket_max = std::max(socket_max, s);
228
0
    }
229
230
0
    timeval tv = MillisToTimeval(timeout);
231
232
0
    if (select(socket_max + 1, &recv, &send, &err, &tv) == SOCKET_ERROR) {
Line
Count
Source
68
0
#define SOCKET_ERROR        -1
233
0
        return false;
234
0
    }
235
236
0
    for (auto& [sock, events] : events_per_sock) {
237
0
        const auto& s = sock->m_socket;
238
0
        events.occurred = 0;
239
0
        if (FD_ISSET(s, &recv)) {
240
0
            events.occurred |= RECV;
241
0
        }
242
0
        if (FD_ISSET(s, &send)) {
243
0
            events.occurred |= SEND;
244
0
        }
245
0
        if (FD_ISSET(s, &err)) {
246
0
            events.occurred |= ERR;
247
0
        }
248
0
    }
249
250
0
    return true;
251
0
#endif /* USE_POLL */
252
0
}
253
254
void Sock::SendComplete(std::span<const unsigned char> data,
255
                        std::chrono::milliseconds timeout,
256
                        CThreadInterrupt& interrupt) const
257
0
{
258
0
    const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
259
0
    size_t sent{0};
260
261
0
    for (;;) {
262
0
        const ssize_t ret{Send(data.data() + sent, data.size() - sent, MSG_NOSIGNAL)};
263
264
0
        if (ret > 0) {
265
0
            sent += static_cast<size_t>(ret);
266
0
            if (sent == data.size()) {
267
0
                break;
268
0
            }
269
0
        } else {
270
0
            const int err{WSAGetLastError()};
Line
Count
Source
59
0
#define WSAGetLastError()   errno
271
0
            if (IOErrorIsPermanent(err)) {
272
0
                throw std::runtime_error(strprintf("send(): %s", NetworkErrorString(err)));
Line
Count
Source
1172
0
#define strprintf tfm::format
273
0
            }
274
0
        }
275
276
0
        const auto now = GetTime<std::chrono::milliseconds>();
277
278
0
        if (now >= deadline) {
279
0
            throw std::runtime_error(strprintf(
Line
Count
Source
1172
0
#define strprintf tfm::format
280
0
                "Send timeout (sent only %u of %u bytes before that)", sent, data.size()));
281
0
        }
282
283
0
        if (interrupt) {
284
0
            throw std::runtime_error(strprintf(
Line
Count
Source
1172
0
#define strprintf tfm::format
285
0
                "Send interrupted (sent only %u of %u bytes before that)", sent, data.size()));
286
0
        }
287
288
        // Wait for a short while (or the socket to become ready for sending) before retrying
289
        // if nothing was sent.
290
0
        const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
291
0
        (void)Wait(wait_time, SEND);
292
0
    }
293
0
}
294
295
void Sock::SendComplete(std::span<const char> data,
296
                        std::chrono::milliseconds timeout,
297
                        CThreadInterrupt& interrupt) const
298
0
{
299
0
    SendComplete(MakeUCharSpan(data), timeout, interrupt);
300
0
}
301
302
std::string Sock::RecvUntilTerminator(uint8_t terminator,
303
                                      std::chrono::milliseconds timeout,
304
                                      CThreadInterrupt& interrupt,
305
                                      size_t max_data) const
306
0
{
307
0
    const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
308
0
    std::string data;
309
0
    bool terminator_found{false};
310
311
    // We must not consume any bytes past the terminator from the socket.
312
    // One option is to read one byte at a time and check if we have read a terminator.
313
    // However that is very slow. Instead, we peek at what is in the socket and only read
314
    // as many bytes as possible without crossing the terminator.
315
    // Reading 64 MiB of random data with 262526 terminator chars takes 37 seconds to read
316
    // one byte at a time VS 0.71 seconds with the "peek" solution below. Reading one byte
317
    // at a time is about 50 times slower.
318
319
0
    for (;;) {
320
0
        if (data.size() >= max_data) {
321
0
            throw std::runtime_error(
322
0
                strprintf("Received too many bytes without a terminator (%u)", data.size()));
Line
Count
Source
1172
0
#define strprintf tfm::format
323
0
        }
324
325
0
        char buf[512];
326
327
0
        const ssize_t peek_ret{Recv(buf, std::min(sizeof(buf), max_data - data.size()), MSG_PEEK)};
328
329
0
        switch (peek_ret) {
330
0
        case -1: {
331
0
            const int err{WSAGetLastError()};
Line
Count
Source
59
0
#define WSAGetLastError()   errno
332
0
            if (IOErrorIsPermanent(err)) {
333
0
                throw std::runtime_error(strprintf("recv(): %s", NetworkErrorString(err)));
Line
Count
Source
1172
0
#define strprintf tfm::format
334
0
            }
335
0
            break;
336
0
        }
337
0
        case 0:
338
0
            throw std::runtime_error("Connection unexpectedly closed by peer");
339
0
        default:
340
0
            auto end = buf + peek_ret;
341
0
            auto terminator_pos = std::find(buf, end, terminator);
342
0
            terminator_found = terminator_pos != end;
343
344
0
            const size_t try_len{terminator_found ? terminator_pos - buf + 1 :
345
0
                                                    static_cast<size_t>(peek_ret)};
346
347
0
            const ssize_t read_ret{Recv(buf, try_len, 0)};
348
349
0
            if (read_ret < 0 || static_cast<size_t>(read_ret) != try_len) {
350
0
                throw std::runtime_error(
351
0
                    strprintf("recv() returned %u bytes on attempt to read %u bytes but previous "
Line
Count
Source
1172
0
#define strprintf tfm::format
352
0
                              "peek claimed %u bytes are available",
353
0
                              read_ret, try_len, peek_ret));
354
0
            }
355
356
            // Don't include the terminator in the output.
357
0
            const size_t append_len{terminator_found ? try_len - 1 : try_len};
358
359
0
            data.append(buf, buf + append_len);
360
361
0
            if (terminator_found) {
362
0
                return data;
363
0
            }
364
0
        }
365
366
0
        const auto now = GetTime<std::chrono::milliseconds>();
367
368
0
        if (now >= deadline) {
369
0
            throw std::runtime_error(strprintf(
Line
Count
Source
1172
0
#define strprintf tfm::format
370
0
                "Receive timeout (received %u bytes without terminator before that)", data.size()));
371
0
        }
372
373
0
        if (interrupt) {
374
0
            throw std::runtime_error(strprintf(
Line
Count
Source
1172
0
#define strprintf tfm::format
375
0
                "Receive interrupted (received %u bytes without terminator before that)",
376
0
                data.size()));
377
0
        }
378
379
        // Wait for a short while (or the socket to become ready for reading) before retrying.
380
0
        const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
381
0
        (void)Wait(wait_time, RECV);
382
0
    }
383
0
}
384
385
bool Sock::IsConnected(std::string& errmsg) const
386
0
{
387
0
    if (m_socket == INVALID_SOCKET) {
Line
Count
Source
67
0
#define INVALID_SOCKET      (SOCKET)(~0)
388
0
        errmsg = "not connected";
389
0
        return false;
390
0
    }
391
392
0
    char c;
393
0
    switch (Recv(&c, sizeof(c), MSG_PEEK)) {
394
0
    case -1: {
395
0
        const int err = WSAGetLastError();
Line
Count
Source
59
0
#define WSAGetLastError()   errno
396
0
        if (IOErrorIsPermanent(err)) {
397
0
            errmsg = NetworkErrorString(err);
398
0
            return false;
399
0
        }
400
0
        return true;
401
0
    }
402
0
    case 0:
403
0
        errmsg = "closed";
404
0
        return false;
405
0
    default:
406
0
        return true;
407
0
    }
408
0
}
409
410
void Sock::Close()
411
3.75k
{
412
3.75k
    if (m_socket == INVALID_SOCKET) {
Line
Count
Source
67
3.75k
#define INVALID_SOCKET      (SOCKET)(~0)
413
3.75k
        return;
414
3.75k
    }
415
#ifdef WIN32
416
    int ret = closesocket(m_socket);
417
#else
418
0
    int ret = close(m_socket);
419
0
#endif
420
0
    if (ret) {
421
0
        LogWarning("Error closing socket %d: %s", m_socket, NetworkErrorString(WSAGetLastError()));
Line
Count
Source
98
0
#define LogWarning(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Warning, /*should_ratelimit=*/true, __VA_ARGS__)
Line
Count
Source
91
0
#define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(SourceLocation{__func__}, category, level, should_ratelimit, __VA_ARGS__)
422
0
    }
423
0
    m_socket = INVALID_SOCKET;
Line
Count
Source
67
0
#define INVALID_SOCKET      (SOCKET)(~0)
424
0
}
425
426
bool Sock::operator==(SOCKET s) const
427
0
{
428
0
    return m_socket == s;
429
0
};
430
431
std::string NetworkErrorString(int err)
432
0
{
433
#if defined(WIN32)
434
    return Win32ErrorString(err);
435
#else
436
    // On BSD sockets implementations, NetworkErrorString is the same as SysErrorString.
437
0
    return SysErrorString(err);
438
0
#endif
439
0
}