Skip to content

Commit

Permalink
udp listener fuzzer (#15974)
Browse files Browse the repository at this point in the history
* test: common: network: add udp listener fuzzer.

Signed-off-by: davkor <[email protected]>
  • Loading branch information
DavidKorczynski authored Apr 21, 2021
1 parent a12869f commit 0c37028
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 0 deletions.
24 changes: 24 additions & 0 deletions test/common/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,30 @@ envoy_cc_test(
],
)

envoy_cc_fuzz_test(
name = "udp_fuzz",
srcs = ["udp_fuzz.cc"],
corpus = "udp_corpus",
deps = [
"udp_listener_impl_test_base_lib",
"//source/common/event:dispatcher_lib",
"//source/common/network:address_lib",
"//source/common/network:listener_lib",
"//source/common/network:socket_option_lib",
"//source/common/network:udp_packet_writer_handler_lib",
"//source/common/network:utility_lib",
"//source/common/stats:stats_lib",
"//test/common/network:listener_impl_test_base_lib",
"//test/mocks/network:network_mocks",
"//test/mocks/server:server_mocks",
"//test/test_common:environment_lib",
"//test/test_common:network_utility_lib",
"//test/test_common:threadsafe_singleton_injector_lib",
"//test/test_common:utility_lib",
"@envoy_api//envoy/config/core/v3:pkg_cc_proto",
],
)

envoy_cc_fuzz_test(
name = "utility_fuzz_test",
srcs = ["utility_fuzz_test.cc"],
Expand Down
1 change: 1 addition & 0 deletions test/common/network/udp_corpus/seed1.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

183 changes: 183 additions & 0 deletions test/common/network/udp_fuzz.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
#include <fuzzer/FuzzedDataProvider.h>

#include "envoy/config/core/v3/base.pb.h"

#include "common/api/os_sys_calls_impl.h"
#include "common/network/address_impl.h"
#include "common/network/socket_option_factory.h"
#include "common/network/socket_option_impl.h"
#include "common/network/udp_listener_impl.h"
#include "common/network/udp_packet_writer_handler_impl.h"
#include "common/network/utility.h"

#include "test/common/network/udp_listener_impl_test_base.h"
#include "test/fuzz/fuzz_runner.h"
#include "test/mocks/api/mocks.h"
#include "test/mocks/network/mocks.h"
#include "test/mocks/server/mocks.h"
#include "test/test_common/environment.h"
#include "test/test_common/network_utility.h"
#include "test/test_common/threadsafe_singleton_injector.h"
#include "test/test_common/utility.h"

namespace Envoy {
namespace {

class OverrideOsSysCallsImpl : public Api::OsSysCallsImpl {
public:
MOCK_METHOD(bool, supportsUdpGro, (), (const));
MOCK_METHOD(bool, supportsMmsg, (), (const));
};

class UdpFuzz;

class FuzzUdpListenerCallbacks : public Network::UdpListenerCallbacks {
public:
FuzzUdpListenerCallbacks(UdpFuzz* upf) : my_upf_(upf) {}
~FuzzUdpListenerCallbacks() override = default;
void onData(Network::UdpRecvData&& data) override;
void onReadReady() override;
void onWriteReady(const Network::Socket& socket) override;
void onReceiveError(Api::IoError::IoErrorCode error_code) override;
void onDataWorker(Network::UdpRecvData&& data) override;
void post(Network::UdpRecvData&& data) override;
void onDatagramsDropped(uint32_t dropped) override;
uint32_t workerIndex() const override;
Network::UdpPacketWriter& udpPacketWriter() override;

private:
UdpFuzz* my_upf_;
};

class UdpFuzz {
public:
UdpFuzz(const uint8_t* buf, size_t len) {
// Prepare environment
api_ = Api::createApiForTest();
dispatcher_ = api_->allocateDispatcher("test_thread");
ip_version_ = TestEnvironment::getIpVersionsForTest()[0];

server_socket_ = createServerSocket(true, ip_version_);
server_socket_->addOptions(Network::SocketOptionFactory::buildIpPacketInfoOptions());
server_socket_->addOptions(Network::SocketOptionFactory::buildRxQueueOverFlowOptions());

// Create packet writer
udp_packet_writer_ = std::make_unique<Network::UdpDefaultWriter>(server_socket_->ioHandle());

// Set up callbacks
FuzzUdpListenerCallbacks fuzzCallbacks(this);

// Create listener with default config
envoy::config::core::v3::UdpSocketConfig config;

FuzzedDataProvider provider(buf, len);
uint16_t SocketType = provider.ConsumeIntegralInRange<uint16_t>(0, 2);
if (SocketType == 0) {
config.mutable_prefer_gro()->set_value(true);
ON_CALL(override_syscall_, supportsUdpGro()).WillByDefault(Return(true));
} else if (SocketType == 1) {
ON_CALL(override_syscall_, supportsMmsg()).WillByDefault(Return(true));
} else {
ON_CALL(override_syscall_, supportsMmsg()).WillByDefault(Return(false));
ON_CALL(override_syscall_, supportsUdpGro()).WillByDefault(Return(false));
}

std::unique_ptr<Network::UdpListenerImpl> listener_ =
std::make_unique<Network::UdpListenerImpl>(dispatcherImpl(), server_socket_, fuzzCallbacks,
dispatcherImpl().timeSource(), config);

Network::Address::Instance* send_to_addr_ = new Network::Address::Ipv4Instance(
"127.0.0.1", server_socket_->addressProvider().localAddress()->ip()->port());

// Now do all of the fuzzing
static const int MaxPackets = 15;
total_packets_ = provider.ConsumeIntegralInRange<uint16_t>(1, MaxPackets);
Network::Test::UdpSyncPeer client_(ip_version_);
for (uint16_t i = 0; i < total_packets_; i++) {
std::string packet_ =
provider.ConsumeBytesAsString(provider.ConsumeIntegralInRange<uint32_t>(1, 3000));
if (packet_.empty()) {
packet_ = "EMPTY_PACKET";
}
client_.write(packet_, *send_to_addr_);
}
dispatcher_->run(Event::Dispatcher::RunType::Block);

// cleanup
delete send_to_addr_;
}

Event::DispatcherImpl& dispatcherImpl() {
// We need access to the concrete impl type in order to instantiate a
// Test[Udp]Listener, which instantiates a [Udp]ListenerImpl, which requires
// a DispatcherImpl to access DispatcherImpl::base_, which is not part of
// the Dispatcher API.
Event::DispatcherImpl* impl = dynamic_cast<Event::DispatcherImpl*>(dispatcher_.get());
return *impl;
}

Network::SocketSharedPtr createServerSocket(bool bind, Network::Address::IpVersion version) {
// Set IP_FREEBIND to allow sendmsg to send with non-local IPv6 source
// address.
return std::make_shared<Network::UdpListenSocket>(
Network::Test::getCanonicalLoopbackAddress(version),
#ifdef IP_FREEBIND
Network::SocketOptionFactory::buildIpFreebindOptions(),
#else
nullptr,
#endif
bind);
}

Network::SocketSharedPtr server_socket_;
Event::DispatcherPtr dispatcher_;
Api::ApiPtr api_;
Network::UdpPacketWriterPtr udp_packet_writer_;
uint16_t sent_packets_ = 0;
uint16_t total_packets_;
Network::Address::IpVersion ip_version_;
NiceMock<OverrideOsSysCallsImpl> override_syscall_;
TestThreadsafeSingletonInjector<Api::OsSysCallsImpl> os_calls{&override_syscall_};
};

void FuzzUdpListenerCallbacks::onData(Network::UdpRecvData&& data) {
my_upf_->sent_packets_++;
if (my_upf_->sent_packets_ == my_upf_->total_packets_) {
my_upf_->dispatcher_->exit();
}
UNREFERENCED_PARAMETER(data);
}

void FuzzUdpListenerCallbacks::onReadReady() {}

void FuzzUdpListenerCallbacks::onWriteReady(const Network::Socket& socket) {
UNREFERENCED_PARAMETER(socket);
}

void FuzzUdpListenerCallbacks::onReceiveError(Api::IoError::IoErrorCode error_code) {
my_upf_->sent_packets_++;
if (my_upf_->sent_packets_ == my_upf_->total_packets_) {
my_upf_->dispatcher_->exit();
}
UNREFERENCED_PARAMETER(error_code);
}
Network::UdpPacketWriter& FuzzUdpListenerCallbacks::udpPacketWriter() {
return *my_upf_->udp_packet_writer_;
}
uint32_t FuzzUdpListenerCallbacks::workerIndex() const { return 0; }
void FuzzUdpListenerCallbacks::onDataWorker(Network::UdpRecvData&& data) {
UNREFERENCED_PARAMETER(data);
}
void FuzzUdpListenerCallbacks::post(Network::UdpRecvData&& data) { UNREFERENCED_PARAMETER(data); }

void FuzzUdpListenerCallbacks::onDatagramsDropped(uint32_t dropped) {
my_upf_->sent_packets_++;
if (my_upf_->sent_packets_ == my_upf_->total_packets_) {
my_upf_->dispatcher_->exit();
}
UNREFERENCED_PARAMETER(dropped);
}

DEFINE_FUZZER(const uint8_t* buf, size_t len) { UdpFuzz udp_instance(buf, len); }
} // namespace
} // namespace Envoy

0 comments on commit 0c37028

Please sign in to comment.