From 52b65186e1321c60061c6bb1fdee924dc7c24f59 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Fri, 21 Mar 2025 10:33:35 +0100 Subject: [PATCH] fuzz: avoid returning non-conforming results from FuzzedSock::GetSockName() It would be a bug in `getsockname(2)` if it returns a result that is smaller than the returned socket address family. For example, if it indicates that the result is less than `sizeof(sockaddr_in6)` and sets `sa_family` equal to `AF_INET6` in the output. In other words, the `name->sa_family` in the output should be consistent with the returned `*name_len`. The current code could fail to do that if: * the caller provides `sockaddr_in6` and an input value of `*name_len=28` * `ConsumeRandomLengthByteVector()` returns a vector of `20` bytes. Then the code would only set the first `20` bytes in `name`. * `name->sa_family` from the fuzz data ends up being `AF_INET6`. To produce consistent `*name_len` and `name->sa_family`, return one of `AF_INET`, `AF_INET6` or `AF_UNIX` for family with the corresponding `*name_len`. For reference: ``` sizeof(sockaddr) = 16 sizeof(sockaddr_in) = 16 sizeof(sockaddr_in6) = 28 sizeof(sockaddr_un) = 110 on Linux, 106 on FreeBSD (unix socket) sizeof(sockaddr_storage) = 128 ``` https://www.man7.org/linux/man-pages/man3/sockaddr.3type.html --- src/test/fuzz/util/net.cpp | 50 +++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/src/test/fuzz/util/net.cpp b/src/test/fuzz/util/net.cpp index 8cbf6bdffec..aa084434bfb 100644 --- a/src/test/fuzz/util/net.cpp +++ b/src/test/fuzz/util/net.cpp @@ -2,6 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include // IWYU pragma: keep + #include #include @@ -23,6 +25,16 @@ #include #include +#ifdef WIN32 +#include +#else +#include +#endif + +#ifdef HAVE_SOCKADDR_UN +#include +#endif + class CNode; CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider, FastRandomContext* rand) noexcept @@ -358,10 +370,40 @@ int FuzzedSock::GetSockName(sockaddr* name, socklen_t* name_len) const return -1; } assert(name_len); - const auto bytes{ConsumeRandomLengthByteVector(m_fuzzed_data_provider, *name_len)}; - if (bytes.size() < (int)sizeof(sockaddr)) return -1; - std::memcpy(name, bytes.data(), bytes.size()); - *name_len = bytes.size(); + + // Return one of AF_INET, AF_INET6 or AF_UNIX. + + std::vector possible_families; + if (static_cast(*name_len) >= sizeof(sockaddr_in)) { + possible_families.push_back(AF_INET); + } + if (static_cast(*name_len) >= sizeof(sockaddr_in6)) { + possible_families.push_back(AF_INET6); + } +#ifdef HAVE_SOCKADDR_UN + if (static_cast(*name_len) >= sizeof(sockaddr_un)) { + possible_families.push_back(AF_UNIX); + } +#endif + + if (possible_families.empty()) { // *name_len is too small. + errno = EINVAL; + return -1; + } + + const int family{possible_families[m_fuzzed_data_provider.ConsumeIntegralInRange(0, possible_families.size() - 1)]}; + + switch (family) { + case AF_INET: *name_len = sizeof(sockaddr_in); break; + case AF_INET6: *name_len = sizeof(sockaddr_in6); break; +#ifdef HAVE_SOCKADDR_UN + case AF_UNIX: *name_len = sizeof(sockaddr_un); break; +#endif + } + + std::memcpy(name, ConsumeFixedLengthByteVector(m_fuzzed_data_provider, *name_len).data(), *name_len); + name->sa_family = family; + return 0; }