Repository: bitcoin/bitcoin
File: src/test/fuzz/util/net.cpp (C++)

Code before:
    }
    return r;
}

ssize_t FuzzedSock::Recv(void* buf, size_t len, int flags) const
{
    // Have a permanent error at recv_errnos[0] because when the fuzzed data is exhausted
    // SetFuzzedErrNo() will always return the first element and we want to avoid Recv()
    // returning -1 and setting errno to EAGAIN repeatedly.
    constexpr std::array recv_errnos{
        ECONNREFUSED,
        EAGAIN,
        EBADF,
        EFAULT,
        EINTR,
        EINVAL,
        ENOMEM,
        ENOTCONN,
        ENOTSOCK,
        EWOULDBLOCK,
    };
    assert(buf != nullptr || len == 0);
    if (len == 0 || m_fuzzed_data_provider.ConsumeBool()) {
        const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
        if (r == -1) {
            SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
        }
        return r;
    }
    std::vector<uint8_t> random_bytes;
    bool pad_to_len_bytes{m_fuzzed_data_provider.ConsumeBool()};
    if (m_peek_data.has_value()) {
        // `MSG_PEEK` was used in the preceding `Recv()` call, return `m_peek_data`.
        random_bytes = m_peek_data.value();
        if ((flags & MSG_PEEK) == 0) {
            m_peek_data.reset();
        }
        pad_to_len_bytes = false;
    } else if ((flags & MSG_PEEK) != 0) {
        // New call with `MSG_PEEK`.
        random_bytes = ConsumeRandomLengthByteVector(m_fuzzed_data_provider, len);
        if (!random_bytes.empty()) {
            m_peek_data = random_bytes;
            pad_to_len_bytes = false;
        }
    } else {
        random_bytes = ConsumeRandomLengthByteVector(m_fuzzed_data_provider, len);
    }
    if (random_bytes.empty()) {
        const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
        if (r == -1) {

Review comment (suggestion):
Unrelated to this PR but manual timeouts in a fuzz test are a bad idea. If a target requires the use of `GetTime` (or similar) then the harness needs to set mock time (as the test is otherwise non-deterministic).

We should get rid of this and replace it with calls to `SetMockTime` (we might need to make mock time more granular, i.e. currently it only supports seconds iirc).

Code after:
    }
    return r;
}

ssize_t FuzzedSock::Recv(void* buf, size_t len, int flags) const
{
    // Have a permanent error at recv_errnos[0] because when the fuzzed data is exhausted
    // SetFuzzedErrNo() will always return the first element and we want to avoid Recv()
    // returning -1 and setting errno to EAGAIN repeatedly.
    constexpr std::array recv_errnos{
        ECONNREFUSED,
        EAGAIN,
        EBADF,
        EFAULT,
        EINTR,
        EINVAL,
        ENOMEM,
        ENOTCONN,
        ENOTSOCK,
        EWOULDBLOCK,
    };
    assert(buf != nullptr || len == 0);

    // Do the latency before any of the "return" statements.
    if (m_fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) {
        std::this_thread::sleep_for(std::chrono::milliseconds{2});
    }

    if (len == 0 || m_fuzzed_data_provider.ConsumeBool()) {
        const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
        if (r == -1) {
            SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
        }
        return r;
    }

    size_t copied_so_far{0};

    if (!m_peek_data.empty()) {
        // `MSG_PEEK` was used in the preceding `Recv()` call, copy the first bytes from `m_peek_data`.
        const size_t copy_len{std::min(len, m_peek_data.size())};
        std::memcpy(buf, m_peek_data.data(), copy_len);
        copied_so_far += copy_len;
        if ((flags & MSG_PEEK) == 0) {
            m_peek_data.erase(m_peek_data.begin(), m_peek_data.begin() + copy_len);
        }
    }

    if (copied_so_far == len) {
        return copied_so_far;
    }