wsd: add HttpEcho fuzzer
This is a full round-trip http fuzzer. It can achieve >1000 iterations per second on a single 2 Ghz core, even while going through the network loopback layer. The advantage is that more networking code is fuzzed this way, including not just the http code, but also the sockets. Change-Id: I75d21bd0e25221ee6621097a2605d62c4bb2ae4d Signed-off-by: Ashod Nakashian <ashod.nakashian@collabora.co.uk>pull/5163/head
parent
c9cb1f4e01
commit
a87d1ae54f
14
Makefile.am
14
Makefile.am
|
@ -151,7 +151,8 @@ if ENABLE_LIBFUZZER
|
|||
noinst_PROGRAMS += \
|
||||
admin_fuzzer \
|
||||
clientsession_fuzzer \
|
||||
httpresponse_fuzzer
|
||||
httpresponse_fuzzer \
|
||||
httpecho_fuzzer
|
||||
endif
|
||||
|
||||
connect_SOURCES = tools/Connect.cpp \
|
||||
|
@ -210,6 +211,17 @@ httpresponse_fuzzer_SOURCES = \
|
|||
fuzzer/HttpResponse.cpp
|
||||
httpresponse_fuzzer_LDFLAGS = -fsanitize=fuzzer $(AM_LDFLAGS)
|
||||
|
||||
httpecho_fuzzer_CPPFLAGS = \
|
||||
-DKIT_IN_PROCESS=1 \
|
||||
$(AM_CPPFLAGS) \
|
||||
-I${top_srcdir}/test
|
||||
httpecho_fuzzer_SOURCES = \
|
||||
$(loolwsd_sources) \
|
||||
$(loolforkit_sources) \
|
||||
$(shared_sources) \
|
||||
fuzzer/HttpEcho.cpp
|
||||
httpecho_fuzzer_LDFLAGS = -fsanitize=fuzzer $(AM_LDFLAGS)
|
||||
|
||||
endif # ENABLE_LIBFUZZER
|
||||
|
||||
clientnb_SOURCES = net/clientnb.cpp \
|
||||
|
|
|
@ -151,7 +151,7 @@ AC_ARG_ENABLE(fuzzers,
|
|||
AS_HELP_STRING([--enable-fuzzers],
|
||||
[Enables building libfuzzer targets for fuzz testing. It is useful to enable this switch
|
||||
only in a separate build tree, since the switch disables the creation of a coolwsd
|
||||
binary.])
|
||||
binary. For best results: --with-sanitizer=address,undefined])
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE([androidapp],
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
#include "ConfigUtil.hpp"
|
||||
#include "Socket.hpp"
|
||||
#include <test/HttpTestServer.hpp>
|
||||
|
||||
#include <Poco/URI.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <test/lokassert.hpp>
|
||||
|
||||
#if ENABLE_SSL
|
||||
#include "Ssl.hpp"
|
||||
#include <net/SslSocket.hpp>
|
||||
#endif
|
||||
#include <net/ServerSocket.hpp>
|
||||
#include <net/DelaySocket.hpp>
|
||||
#include <net/HttpRequest.hpp>
|
||||
#include <FileUtil.hpp>
|
||||
#include <Util.hpp>
|
||||
|
||||
class HttpRequestTests final
|
||||
{
|
||||
void testSimpleGetSync();
|
||||
|
||||
std::string _localUri;
|
||||
SocketPoll _pollServerThread;
|
||||
std::shared_ptr<ServerSocket> _socket;
|
||||
std::shared_ptr<http::Session> _httpSession;
|
||||
SocketPoll _poller;
|
||||
bool _completed;
|
||||
|
||||
class ServerSocketFactory final : public SocketFactory
|
||||
{
|
||||
std::shared_ptr<Socket> create(const int physicalFd) override
|
||||
{
|
||||
return StreamSocket::create<StreamSocket>("localhost", physicalFd, false,
|
||||
std::make_shared<ServerRequestHandler>());
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
HttpRequestTests()
|
||||
: _pollServerThread("HttpServerPoll")
|
||||
, _poller("HttpSynReqPoll")
|
||||
{
|
||||
_poller.runOnClientThread();
|
||||
|
||||
std::map<std::string, std::string> logProperties;
|
||||
const auto log_level = std::getenv("LOG_LEVEL");
|
||||
if (log_level)
|
||||
{
|
||||
Log::initialize("fuz", log_level ? log_level : "error", isatty(fileno(stderr)), false,
|
||||
logProperties);
|
||||
}
|
||||
|
||||
std::shared_ptr<SocketFactory> factory = std::make_shared<ServerSocketFactory>();
|
||||
int port = 9990;
|
||||
for (int i = 0; i < 40; ++i, ++port)
|
||||
{
|
||||
// Try listening on this port.
|
||||
_socket = ServerSocket::create(ServerSocket::Type::Local, port, Socket::Type::IPv4,
|
||||
_pollServerThread, factory);
|
||||
if (_socket)
|
||||
break;
|
||||
}
|
||||
|
||||
_localUri = "http://127.0.0.1:" + std::to_string(port);
|
||||
_pollServerThread.startThread();
|
||||
_pollServerThread.insertNewSocket(_socket);
|
||||
|
||||
_httpSession = http::Session::create(localUri());
|
||||
if (!_httpSession)
|
||||
throw std::runtime_error("Failed to create http::Session to " + localUri());
|
||||
|
||||
_httpSession->setTimeout(std::chrono::milliseconds(500));
|
||||
_httpSession->setFinishedHandler(
|
||||
[&](const std::shared_ptr<http::Session>&)
|
||||
{
|
||||
_completed = true;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
~HttpRequestTests()
|
||||
{
|
||||
_pollServerThread.stop();
|
||||
_socket.reset();
|
||||
}
|
||||
|
||||
const std::string& localUri() const { return _localUri; }
|
||||
std::shared_ptr<http::Session> session() const { return _httpSession; }
|
||||
SocketPoll& poller() { return _poller; };
|
||||
bool isCompleted() const { return _completed; };
|
||||
void resetCompleted() { _completed = false; };
|
||||
};
|
||||
|
||||
#define CHECK(X) \
|
||||
do \
|
||||
{ \
|
||||
if (!(X)) \
|
||||
{ \
|
||||
assert(!(X)); \
|
||||
__builtin_trap(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||
{
|
||||
static HttpRequestTests test;
|
||||
|
||||
http::Request httpRequest("/inject/" + Util::bytesToHexString(data, size));
|
||||
|
||||
test.resetCompleted();
|
||||
|
||||
const std::shared_ptr<const http::Response> httpResponse =
|
||||
test.session()->syncRequest(httpRequest, test.poller());
|
||||
|
||||
CHECK(httpResponse->done());
|
||||
CHECK(test.isCompleted()); // The onFinished callback must always be called.
|
||||
|
||||
if (httpResponse->state() == http::Response::State::Complete)
|
||||
{
|
||||
CHECK(!httpResponse->statusLine().httpVersion().empty());
|
||||
CHECK(!httpResponse->statusLine().reasonPhrase().empty());
|
||||
|
||||
CHECK(httpResponse->statusLine().statusCode() >= 100);
|
||||
CHECK(httpResponse->statusLine().statusCode() < 600);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
|
@ -1173,7 +1173,8 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
_response.reset(new Response(onFinished));
|
||||
_response.reset();
|
||||
_response = std::make_shared<Response>(onFinished);
|
||||
|
||||
_request = std::move(req);
|
||||
|
||||
|
|
Loading…
Reference in New Issue