fuzz: Fix uninitialized read in i2p test #21617

pull MarcoFalke wants to merge 1 commits into bitcoin:master from MarcoFalke:2104-fuzzValgrind changing 2 files +11 −5
  1. MarcoFalke commented at 10:45 AM on April 6, 2021: member

    Can be tested with:

    ./test/fuzz/test_runner.py -l DEBUG --valgrind ../btc_qa_assets/fuzz_seed_corpus/ i2p
    
    ==22582== Conditional jump or move depends on uninitialised value(s)
    ==22582==    at 0x6BB2D8: __sanitizer_cov_trace_const_cmp1 (in /tmp/bitcoin-core/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/test/fuzz/fuzz)
    ==22582==    by 0xB305DB: ConnectSocketDirectly(CService const&, Sock const&, int, bool) (netbase.cpp:570)
    ==22582==    by 0x8AAA5D: i2p::sam::Session::Hello() const (i2p.cpp:284)
    ==22582==    by 0x8A6FA0: i2p::sam::Session::CreateIfNotCreatedAlready() (i2p.cpp:352)
    ==22582==    by 0x8A6742: i2p::sam::Session::Listen(i2p::Connection&) (i2p.cpp:134)
    ==22582==    by 0x7A6C42: i2p_fuzz_target(Span<unsigned char const>) (i2p.cpp:37)
    
  2. fuzz: Fix uninitialized read in test 33333755f2
  3. MarcoFalke renamed this:
    fuzz: Fix uninitialized read in test
    fuzz: Fix uninitialized read in i2p test
    on Apr 6, 2021
  4. fanquake added the label Tests on Apr 6, 2021
  5. jonatack commented at 10:53 AM on April 6, 2021: member

    Concept ACK

  6. sipa commented at 9:56 PM on April 6, 2021: member

    utACK 33333755f2edcbe88fcd136f6fef81f94819002e

  7. fanquake requested review from vasild on Apr 6, 2021
  8. in src/test/fuzz/util.cpp:10 in 33333755f2
       6 | @@ -7,6 +7,14 @@
       7 |  #include <util/rbf.h>
       8 |  #include <version.h>
       9 |  
      10 | +bool FuzzedSock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred ) const
    


    vasild commented at 7:52 AM on April 7, 2021:

    nit: extra white space near the end: occurred )


    MarcoFalke commented at 8:39 AM on April 7, 2021:

    Good catch. Someone forgot to run clang-format

  9. in src/test/fuzz/util.cpp:17 in 33333755f2
      12 | +    if (!m_fuzzed_data_provider.ConsumeBool()) {
      13 | +        return false;
      14 | +    }
      15 | +    if (occurred) *occurred = 0;
      16 | +    return true;
      17 | +}
    


    vasild commented at 8:02 AM on April 7, 2021:
    • This would signal a failure without setting errno (it was like this even before this PR, but better fix it)
    • Would result in either an error or timeout, but never "the requested event occurred"
    • !ConsumeBool() is the same as ConsumeBool()
    {
        constexpr std::array wait_errnos{
            EBADF,
            EINTR,
            EINVAL,
        };
        if (m_fuzzed_data_provider.ConsumeBool()) { 
            SetFuzzedErrNo(m_fuzzed_data_provider, wait_errnos);
            return false;
        } 
        if (occurred) { 
            if (m_fuzzed_data_provider.ConsumeBool()) { 
                *occurred = requested;
            } else { 
                *occurred = 0;
            } 
        } 
        return true;
    }
    

    MarcoFalke commented at 8:41 AM on April 7, 2021:

    !ConsumeBool() is the same as ConsumeBool()

    I had a slight preference to not invalidate the existing fuzz inputs by using the ! operator on the bool

    Mind taking the other changes to a new pr?

  10. in src/test/fuzz/util.h:741 in 33333755f2
     737 | @@ -738,12 +738,10 @@ class FuzzedSock : public Sock
     738 |          return 0;
     739 |      }
     740 |  
     741 | -    bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override
     742 | -    {
     743 | -        return m_fuzzed_data_provider.ConsumeBool();
     744 | -    }
     745 | +    bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override;
    


    vasild commented at 8:05 AM on April 7, 2021:

    I like separating the interface from the implementation - it makes it more readable for consumers who want to use it without being bothered with implementation details (actually it is better to not see the implementation so that one does not accidentally start relying on undocumented implementation details).

    Here is an extra commit that moves the rest of the FuzzedSock's implementation to util.cpp:

    https://github.com/vasild/bitcoin/commit/33c203d6878d9b6adee046f36311c58a13e79941

    <details> <summary>fuzz: split FuzzedSock interface and implementation</summary>

    commit 33c203d6878d9b6adee046f36311c58a13e79941 (HEAD -> pull/21617_1617705813_33333755f__2104-fuzzValgrind, vasild/2104-fuzzValgrind)
    Parent: 33333755f2edcbe88fcd136f6fef81f94819002e
    Author:     Vasil Dimov <vd@FreeBSD.org>
    AuthorDate: Wed Apr 7 10:18:39 2021 +0200
    Commit:     Vasil Dimov <vd@FreeBSD.org>
    CommitDate: Wed Apr 7 10:18:39 2021 +0200
    gpg: Signature made Wed Apr  7 10:18:59 2021 CEST
    gpg:                using RSA key E64D8D45614DB07545D9CCC154DF06F64B55CBBF
    gpg: Good signature from "Vasil Dimov <vd@myforest.net>" [ultimate]
    gpg:                 aka "Vasil Dimov <vd@FreeBSD.org>" [ultimate]
    gpg:                 aka "Vasil Dimov <vasild@gmail.com>" [ultimate]
    
    
        fuzz: split FuzzedSock interface and implementation
    
    diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp
    index cf5244e31..2ab227e29 100644
    --- a/src/test/fuzz/util.cpp
    +++ b/src/test/fuzz/util.cpp
    @@ -4,21 +4,194 @@
     
     #include <test/fuzz/util.h>
     #include <test/util/script.h>
     #include <util/rbf.h>
     #include <version.h>
     
    +FuzzedSock::FuzzedSock(FuzzedDataProvider& fuzzed_data_provider)
    +    : m_fuzzed_data_provider{fuzzed_data_provider}
    +{
    +    m_socket = fuzzed_data_provider.ConsumeIntegral<SOCKET>();
    +}
    +
    +FuzzedSock::~FuzzedSock()
    +{
    +    // Sock::~Sock() will be called after FuzzedSock::~FuzzedSock() and it will call
    +    // Sock::Reset() (not FuzzedSock::Reset()!) which will call CloseSocket(m_socket).
    +    // Avoid closing an arbitrary file descriptor (m_socket is just a random number which
    +    // may concide with a real opened file descriptor).
    +    Reset();
    +}
    +
    +FuzzedSock& FuzzedSock::operator=(Sock&& other)
    +{
    +    assert(false && "Move of Sock into FuzzedSock not allowed.");
    +    return *this;
    +}
    +
    +void FuzzedSock::Reset()
    +{
    +    m_socket = INVALID_SOCKET;
    +}
    +
    +ssize_t FuzzedSock::Send(const void* data, size_t len, int flags) const
    +{
    +    constexpr std::array send_errnos{
    +        EACCES,
    +        EAGAIN,
    +        EALREADY,
    +        EBADF,
    +        ECONNRESET,
    +        EDESTADDRREQ,
    +        EFAULT,
    +        EINTR,
    +        EINVAL,
    +        EISCONN,
    +        EMSGSIZE,
    +        ENOBUFS,
    +        ENOMEM,
    +        ENOTCONN,
    +        ENOTSOCK,
    +        EOPNOTSUPP,
    +        EPIPE,
    +        EWOULDBLOCK,
    +    };
    +    if (m_fuzzed_data_provider.ConsumeBool()) {
    +        return len;
    +    }
    +    const ssize_t r = m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(-1, len);
    +    if (r == -1) {
    +        SetFuzzedErrNo(m_fuzzed_data_provider, send_errnos);
    +    }
    +    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.assign({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 = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(1);
    +        if (!random_bytes.empty()) {
    +            m_peek_data = random_bytes[0];
    +            pad_to_len_bytes = false;
    +        }
    +    } else {
    +        random_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(
    +            m_fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, len));
    +    }
    +    if (random_bytes.empty()) {
    +        const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
    +        if (r == -1) {
    +            SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
    +        }
    +        return r;
    +    }
    +    std::memcpy(buf, random_bytes.data(), random_bytes.size());
    +    if (pad_to_len_bytes) {
    +        if (len > random_bytes.size()) {
    +            std::memset((char*)buf + random_bytes.size(), 0, len - random_bytes.size());
    +        }
    +        return len;
    +    }
    +    if (m_fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) {
    +        std::this_thread::sleep_for(std::chrono::milliseconds{2});
    +    }
    +    return random_bytes.size();
    +}
    +
    +int FuzzedSock::Connect(const sockaddr*, socklen_t) const
    +{
    +    // Have a permanent error at connect_errnos[0] because when the fuzzed data is exhausted
    +    // SetFuzzedErrNo() will always return the first element and we want to avoid Connect()
    +    // returning -1 and setting errno to EAGAIN repeatedly.
    +    constexpr std::array connect_errnos{
    +        ECONNREFUSED,
    +        EAGAIN,
    +        ECONNRESET,
    +        EHOSTUNREACH,
    +        EINPROGRESS,
    +        EINTR,
    +        ENETUNREACH,
    +        ETIMEDOUT,
    +    };
    +    if (m_fuzzed_data_provider.ConsumeBool()) {
    +        SetFuzzedErrNo(m_fuzzed_data_provider, connect_errnos);
    +        return -1;
    +    }
    +    return 0;
    +}
    +
    +int FuzzedSock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
    +{
    +    constexpr std::array getsockopt_errnos{
    +        ENOMEM,
    +        ENOBUFS,
    +    };
    +    if (m_fuzzed_data_provider.ConsumeBool()) {
    +        SetFuzzedErrNo(m_fuzzed_data_provider, getsockopt_errnos);
    +        return -1;
    +    }
    +    if (opt_val == nullptr) {
    +        return 0;
    +    }
    +    std::memcpy(opt_val,
    +                ConsumeFixedLengthByteVector(m_fuzzed_data_provider, *opt_len).data(),
    +                *opt_len);
    +    return 0;
    +}
    +
     bool FuzzedSock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred ) const
     {
         if (!m_fuzzed_data_provider.ConsumeBool()) {
             return false;
         }
         if (occurred) *occurred = 0;
         return true;
     }
     
    +bool FuzzedSock::IsConnected(std::string& errmsg) const
    +{
    +    if (m_fuzzed_data_provider.ConsumeBool()) {
    +        return true;
    +    }
    +    errmsg = "disconnected at random by the fuzzer";
    +    return false;
    +}
    +
     void FillNode(FuzzedDataProvider& fuzzed_data_provider, CNode& node, bool init_version) noexcept
     {
         const ServiceFlags remote_services = ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS);
         const NetPermissionFlags permission_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS);
         const int32_t version = fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(MIN_PEER_PROTO_VERSION, std::numeric_limits<int32_t>::max());
         const bool filter_txs = fuzzed_data_provider.ConsumeBool();
    diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
    index adcdd7174..8f4f87fbd 100644
    --- a/src/test/fuzz/util.h
    +++ b/src/test/fuzz/util.h
    @@ -572,185 +572,31 @@ class FuzzedSock : public Sock
          * If `MSG_PEEK` is used, then our `Recv()` returns some random data as usual, but on the next
          * `Recv()` call we must return the same data, thus we remember it here.
          */
         mutable std::optional<uint8_t> m_peek_data;
     
     public:
    -    explicit FuzzedSock(FuzzedDataProvider& fuzzed_data_provider) : m_fuzzed_data_provider{fuzzed_data_provider}
    -    {
    -          m_socket = fuzzed_data_provider.ConsumeIntegral<SOCKET>();
    -    }
    +    explicit FuzzedSock(FuzzedDataProvider& fuzzed_data_provider);
     
    -    ~FuzzedSock() override
    -    {
    -        // Sock::~Sock() will be called after FuzzedSock::~FuzzedSock() and it will call
    -        // Sock::Reset() (not FuzzedSock::Reset()!) which will call CloseSocket(m_socket).
    -        // Avoid closing an arbitrary file descriptor (m_socket is just a random number which
    -        // may concide with a real opened file descriptor).
    -        Reset();
    -    }
    +    ~FuzzedSock() override;
     
    -    FuzzedSock& operator=(Sock&& other) override
    -    {
    -        assert(false && "Move of Sock into FuzzedSock not allowed.");
    -        return *this;
    -    }
    +    FuzzedSock& operator=(Sock&& other) override;
     
    -    void Reset() override
    -    {
    -        m_socket = INVALID_SOCKET;
    -    }
    +    void Reset() override;
     
    -    ssize_t Send(const void* data, size_t len, int flags) const override
    -    {
    -        constexpr std::array send_errnos{
    -            EACCES,
    -            EAGAIN,
    -            EALREADY,
    -            EBADF,
    -            ECONNRESET,
    -            EDESTADDRREQ,
    -            EFAULT,
    -            EINTR,
    -            EINVAL,
    -            EISCONN,
    -            EMSGSIZE,
    -            ENOBUFS,
    -            ENOMEM,
    -            ENOTCONN,
    -            ENOTSOCK,
    -            EOPNOTSUPP,
    -            EPIPE,
    -            EWOULDBLOCK,
    -        };
    -        if (m_fuzzed_data_provider.ConsumeBool()) {
    -            return len;
    -        }
    -        const ssize_t r = m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(-1, len);
    -        if (r == -1) {
    -            SetFuzzedErrNo(m_fuzzed_data_provider, send_errnos);
    -        }
    -        return r;
    -    }
    +    ssize_t Send(const void* data, size_t len, int flags) const override;
     
    -    ssize_t Recv(void* buf, size_t len, int flags) const override
    -    {
    -        // 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.assign({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 = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(1);
    -            if (!random_bytes.empty()) {
    -                m_peek_data = random_bytes[0];
    -                pad_to_len_bytes = false;
    -            }
    -        } else {
    -            random_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(
    -                m_fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, len));
    -        }
    -        if (random_bytes.empty()) {
    -            const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
    -            if (r == -1) {
    -                SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
    -            }
    -            return r;
    -        }
    -        std::memcpy(buf, random_bytes.data(), random_bytes.size());
    -        if (pad_to_len_bytes) {
    -            if (len > random_bytes.size()) {
    -                std::memset((char*)buf + random_bytes.size(), 0, len - random_bytes.size());
    -            }
    -            return len;
    -        }
    -        if (m_fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) {
    -            std::this_thread::sleep_for(std::chrono::milliseconds{2});
    -        }
    -        return random_bytes.size();
    -    }
    +    ssize_t Recv(void* buf, size_t len, int flags) const override;
     
    -    int Connect(const sockaddr*, socklen_t) const override
    -    {
    -        // Have a permanent error at connect_errnos[0] because when the fuzzed data is exhausted
    -        // SetFuzzedErrNo() will always return the first element and we want to avoid Connect()
    -        // returning -1 and setting errno to EAGAIN repeatedly.
    -        constexpr std::array connect_errnos{
    -            ECONNREFUSED,
    -            EAGAIN,
    -            ECONNRESET,
    -            EHOSTUNREACH,
    -            EINPROGRESS,
    -            EINTR,
    -            ENETUNREACH,
    -            ETIMEDOUT,
    -        };
    -        if (m_fuzzed_data_provider.ConsumeBool()) {
    -            SetFuzzedErrNo(m_fuzzed_data_provider, connect_errnos);
    -            return -1;
    -        }
    -        return 0;
    -    }
    +    int Connect(const sockaddr*, socklen_t) const override;
     
    -    int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override
    -    {
    -        constexpr std::array getsockopt_errnos{
    -            ENOMEM,
    -            ENOBUFS,
    -        };
    -        if (m_fuzzed_data_provider.ConsumeBool()) {
    -            SetFuzzedErrNo(m_fuzzed_data_provider, getsockopt_errnos);
    -            return -1;
    -        }
    -        if (opt_val == nullptr) {
    -            return 0;
    -        }
    -        std::memcpy(opt_val,
    -                    ConsumeFixedLengthByteVector(m_fuzzed_data_provider, *opt_len).data(),
    -                    *opt_len);
    -        return 0;
    -    }
    +    int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override;
     
         bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override;
     
    -    bool IsConnected(std::string& errmsg) const override
    -    {
    -        if (m_fuzzed_data_provider.ConsumeBool()) {
    -            return true;
    -        }
    -        errmsg = "disconnected at random by the fuzzer";
    -        return false;
    -    }
    +    bool IsConnected(std::string& errmsg) const override;
     };
     
     [[nodiscard]] inline FuzzedSock ConsumeSock(FuzzedDataProvider& fuzzed_data_provider)
     {
         return FuzzedSock{fuzzed_data_provider};
     }
    

    </details>

    (feel free to ignore)


    MarcoFalke commented at 8:41 AM on April 7, 2021:

    Concept ACK on the commit. Please open a new pr and ping me for review.

  11. vasild approved
  12. vasild commented at 8:32 AM on April 7, 2021: member

    ACK 33333755f2edcbe88fcd136f6fef81f94819002e

    Even without taking the suggestions above, this would fix the uninitialized read - the main purpose of this PR.

  13. MarcoFalke merged this on Apr 7, 2021
  14. MarcoFalke closed this on Apr 7, 2021

  15. MarcoFalke deleted the branch on Apr 7, 2021
  16. MarcoFalke commented at 8:42 AM on April 7, 2021: member

    I merged this to unbreak our valgrind fuzzer. Happy to review the proposed changes in a follow-up pr.

  17. vasild commented at 9:12 AM on April 7, 2021: member
  18. sidhujag referenced this in commit e1c3fd05be on Apr 7, 2021
  19. MarcoFalke referenced this in commit c6b30ccb2e on Apr 15, 2021
  20. DrahtBot locked this on Aug 16, 2022

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-17 06:14 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me