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
This commit is contained in:
Vasil Dimov 2025-03-21 10:33:35 +01:00
parent 2db00278ea
commit 52b65186e1
No known key found for this signature in database
GPG key ID: 54DF06F64B55CBBF

View file

@ -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 <bitcoin-build-config.h> // IWYU pragma: keep
#include <test/fuzz/util/net.h>
#include <compat/compat.h>
@ -23,6 +25,16 @@
#include <thread>
#include <vector>
#ifdef WIN32
#include <windows.h>
#else
#include <sys/socket.h>
#endif
#ifdef HAVE_SOCKADDR_UN
#include <sys/un.h>
#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<int> possible_families;
if (static_cast<size_t>(*name_len) >= sizeof(sockaddr_in)) {
possible_families.push_back(AF_INET);
}
if (static_cast<size_t>(*name_len) >= sizeof(sockaddr_in6)) {
possible_families.push_back(AF_INET6);
}
#ifdef HAVE_SOCKADDR_UN
if (static_cast<size_t>(*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<size_t>(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;
}