Bitcoin Core Fuzz Coverage Report

Coverage Report

Created: 2026-03-24 13:57

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