aboutsummaryrefslogtreecommitdiff
path: root/test/fuzzing
diff options
context:
space:
mode:
Diffstat (limited to 'test/fuzzing')
-rw-r--r--test/fuzzing/Makefile27
-rw-r--r--test/fuzzing/corpus/11
-rw-r--r--test/fuzzing/corpus/25
-rw-r--r--test/fuzzing/server_fuzzer.cc88
-rw-r--r--test/fuzzing/server_fuzzer.dict224
-rw-r--r--test/fuzzing/standalone_fuzz_target_runner.cpp35
6 files changed, 380 insertions, 0 deletions
diff --git a/test/fuzzing/Makefile b/test/fuzzing/Makefile
new file mode 100644
index 0000000..d6a3e21
--- /dev/null
+++ b/test/fuzzing/Makefile
@@ -0,0 +1,27 @@
+
+#CXX = clang++
+# Do not add default sanitizer flags here as OSS-fuzz adds its own sanitizer flags.
+CXXFLAGS += -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I../.. -I. -Wall -Wextra -Wtype-limits -Wconversion
+
+OPENSSL_DIR = /usr/local/opt/openssl@1.1
+
+# Using full path to libssl and libcrypto to avoid accidentally picking openssl libs brought in by msan.
+OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -I$(OPENSSL_DIR)/lib /usr/local/lib/libssl.a /usr/local/lib/libcrypto.a
+
+ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
+
+BROTLI_DIR = /usr/local/opt/brotli
+# BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
+
+# Runs all the tests and also fuzz tests against seed corpus.
+all : server_fuzzer
+ ./server_fuzzer corpus/*
+
+# Fuzz target, so that you can choose which $(LIB_FUZZING_ENGINE) to use.
+server_fuzzer : server_fuzzer.cc ../../httplib.h
+# $(CXX) $(CXXFLAGS) -o $@ $< -Wl,-Bstatic $(OPENSSL_SUPPORT) -Wl,-Bdynamic -ldl $(ZLIB_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
+ $(CXX) $(CXXFLAGS) -o $@ $< $(ZLIB_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
+ zip -q -r server_fuzzer_seed_corpus.zip corpus
+
+clean:
+ rm -f server_fuzzer pem *.0 *.o *.1 *.srl *.zip
diff --git a/test/fuzzing/corpus/1 b/test/fuzzing/corpus/1
new file mode 100644
index 0000000..2b9fcc4
--- /dev/null
+++ b/test/fuzzing/corpus/1
@@ -0,0 +1 @@
+PUT /search/sample?a=12 HTTP/1.1 \ No newline at end of file
diff --git a/test/fuzzing/corpus/2 b/test/fuzzing/corpus/2
new file mode 100644
index 0000000..bdb9bcc
--- /dev/null
+++ b/test/fuzzing/corpus/2
@@ -0,0 +1,5 @@
+GET /hello.htm HTTP/1.1
+User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
+Accept-Language: en-us
+Accept-Encoding: gzip, deflate
+Connection: Keep-Alive \ No newline at end of file
diff --git a/test/fuzzing/server_fuzzer.cc b/test/fuzzing/server_fuzzer.cc
new file mode 100644
index 0000000..9fb4d4b
--- /dev/null
+++ b/test/fuzzing/server_fuzzer.cc
@@ -0,0 +1,88 @@
+#include <httplib.h>
+#include <memory>
+
+class FuzzedStream : public httplib::Stream {
+public:
+ FuzzedStream(const uint8_t *data, size_t size)
+ : data_(data), size_(size), read_pos_(0) {}
+
+ ssize_t read(char *ptr, size_t size) override {
+ if (size + read_pos_ > size_) { size = size_ - read_pos_; }
+ memcpy(ptr, data_ + read_pos_, size);
+ read_pos_ += size;
+ return static_cast<ssize_t>(size);
+ }
+
+ ssize_t write(const char *ptr, size_t size) override {
+ response_.append(ptr, size);
+ return static_cast<int>(size);
+ }
+
+ ssize_t write(const char *ptr) { return write(ptr, strlen(ptr)); }
+
+ ssize_t write(const std::string &s) { return write(s.data(), s.size()); }
+
+ std::string get_remote_addr() const { return ""; }
+
+ bool is_readable() const override { return true; }
+
+ bool is_writable() const override { return true; }
+
+ void get_remote_ip_and_port(std::string &ip, int &port) const override {
+ ip = "127.0.0.1";
+ port = 8080;
+ }
+
+ socket_t socket() const override { return 0; }
+
+private:
+ const uint8_t *data_;
+ size_t size_;
+ size_t read_pos_;
+ std::string response_;
+};
+
+class FuzzableServer : public httplib::Server {
+public:
+ void ProcessFuzzedRequest(FuzzedStream &stream) {
+ bool connection_close = false;
+ process_request(stream, /*last_connection=*/false, connection_close,
+ nullptr);
+ }
+};
+
+static FuzzableServer g_server;
+
+extern "C" int LLVMFuzzerInitialize(int * /*argc*/, char *** /*argv*/) {
+ g_server.Get(R"(.*)",
+ [&](const httplib::Request & /*req*/, httplib::Response &res) {
+ res.set_content("response content", "text/plain");
+ });
+ g_server.Post(R"(.*)",
+ [&](const httplib::Request & /*req*/, httplib::Response &res) {
+ res.set_content("response content", "text/plain");
+ });
+ g_server.Put(R"(.*)",
+ [&](const httplib::Request & /*req*/, httplib::Response &res) {
+ res.set_content("response content", "text/plain");
+ });
+ g_server.Patch(R"(.*)",
+ [&](const httplib::Request & /*req*/, httplib::Response &res) {
+ res.set_content("response content", "text/plain");
+ });
+ g_server.Delete(
+ R"(.*)", [&](const httplib::Request & /*req*/, httplib::Response &res) {
+ res.set_content("response content", "text/plain");
+ });
+ g_server.Options(
+ R"(.*)", [&](const httplib::Request & /*req*/, httplib::Response &res) {
+ res.set_content("response content", "text/plain");
+ });
+ return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedStream stream{data, size};
+ g_server.ProcessFuzzedRequest(stream);
+ return 0;
+}
diff --git a/test/fuzzing/server_fuzzer.dict b/test/fuzzing/server_fuzzer.dict
new file mode 100644
index 0000000..47283dc
--- /dev/null
+++ b/test/fuzzing/server_fuzzer.dict
@@ -0,0 +1,224 @@
+# Sources: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
+
+# misc
+"HTTP/1.1"
+
+# verbs
+"CONNECT"
+"DELETE"
+"GET"
+"HEAD"
+"OPTIONS"
+"PATCH"
+"POST"
+"PUT"
+"TRACE"
+
+
+# Webdav/caldav verbs
+"ACL"
+"BASELINE-CONTROL"
+"BIND"
+"CHECKIN"
+"CHECKOUT"
+"COPY"
+"LABEL"
+"LINK"
+"LOCK"
+"MERGE"
+"MKACTIVITY"
+"MKCALENDAR"
+"MKCOL"
+"MKREDIRECTREF"
+"MKWORKSPACE"
+"MOVE"
+"ORDERPATCH"
+"PRI"
+"PROPFIND"
+"PROPPATCH"
+"REBIND"
+"REPORT"
+"SEARCH"
+"UNBIND"
+"UNCHECKOUT"
+"UNLINK"
+"UNLOCK"
+"UPDATE"
+"UPDATEREDIRECTREF"
+"VERSION-CONTROL"
+
+
+# Fields
+"A-IM"
+"Accept"
+"Accept-Charset"
+"Accept-Datetime"
+"Accept-Encoding"
+"Accept-Language"
+"Accept-Patch"
+"Accept-Ranges"
+"Access-Control-Allow-Credentials"
+"Access-Control-Allow-Headers"
+"Access-Control-Allow-Methods"
+"Access-Control-Allow-Origin"
+"Access-Control-Expose-Headers"
+"Access-Control-Max-Age"
+"Access-Control-Request-Headers"
+"Access-Control-Request-Method"
+"Age"
+"Allow"
+"Alt-Svc"
+"Authorization"
+"Cache-Control"
+"Connection"
+"Connection:"
+"Content-Disposition"
+"Content-Encoding"
+"Content-Language"
+"Content-Length"
+"Content-Location"
+"Content-MD5"
+"Content-Range"
+"Content-Security-Policy"
+"Content-Type"
+"Cookie"
+"DNT"
+"Date"
+"Delta-Base"
+"ETag"
+"Expect"
+"Expires"
+"Forwarded"
+"From"
+"Front-End-Https"
+"HTTP2-Settings"
+"Host"
+"IM"
+"If-Match"
+"If-Modified-Since"
+"If-None-Match"
+"If-Range"
+"If-Unmodified-Since"
+"Last-Modified"
+"Link"
+"Location"
+"Max-Forwards"
+"Origin"
+"P3P"
+"Pragma"
+"Proxy-Authenticate"
+"Proxy-Authorization"
+"Proxy-Connection"
+"Public-Key-Pins"
+"Range"
+"Referer"
+"Refresh"
+"Retry-After"
+"Save-Data"
+"Server"
+"Set-Cookie"
+"Status"
+"Strict-Transport-Security"
+"TE"
+"Timing-Allow-Origin"
+"Tk"
+"Trailer"
+"Transfer-Encoding"
+"Upgrade"
+"Upgrade-Insecure-Requests"
+"User-Agent"
+"Vary"
+"Via"
+"WWW-Authenticate"
+"Warning"
+"X-ATT-DeviceId"
+"X-Content-Duration"
+"X-Content-Security-Policy"
+"X-Content-Type-Options"
+"X-Correlation-ID"
+"X-Csrf-Token"
+"X-Forwarded-For"
+"X-Forwarded-Host"
+"X-Forwarded-Proto"
+"X-Frame-Options"
+"X-Http-Method-Override"
+"X-Powered-By"
+"X-Request-ID"
+"X-Requested-With"
+"X-UA-Compatible"
+"X-UIDH"
+"X-Wap-Profile"
+"X-WebKit-CSP"
+"X-XSS-Protection"
+
+# Source: string and character literals in httplib.h
+" "
+"&"
+", "
+"-"
+"--"
+"."
+".."
+":"
+"="
+" = = "
+"0123456789abcdef"
+"%02X"
+"%0A"
+"\\x0a\\x0d"
+"%0D"
+"%20"
+"%27"
+"%2B"
+"%2C"
+"%3A"
+"%3B"
+"application/javascript"
+"application/json"
+"application/pdf"
+"application/xhtml+xml"
+"application/xml"
+"application/x-www-form-urlencoded"
+"Bad Request"
+"boundary="
+"bytes="
+"chunked"
+"close"
+"CONNECT"
+"css"
+"Forbidden"
+"Found"
+"gif"
+"gzip"
+"html"
+"ico"
+"image/gif"
+"image/jpg"
+"image/png"
+"image/svg+xml"
+"image/x-icon"
+"index.html"
+"Internal Server Error"
+"jpeg"
+"js"
+"json"
+"Location"
+"Moved Permanently"
+"multipart/form-data"
+"Not Found"
+"Not Modified"
+"OK"
+"pdf"
+"png"
+"Range"
+"REMOTE_ADDR"
+"See Other"
+"svg"
+"text/"
+"text/css"
+"text/html"
+"text/plain"
+"txt"
+"Unsupported Media Type"
+"xhtml"
+"xml" \ No newline at end of file
diff --git a/test/fuzzing/standalone_fuzz_target_runner.cpp b/test/fuzzing/standalone_fuzz_target_runner.cpp
new file mode 100644
index 0000000..8e34792
--- /dev/null
+++ b/test/fuzzing/standalone_fuzz_target_runner.cpp
@@ -0,0 +1,35 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0 (the "License");
+
+// This runner does not do any fuzzing, but allows us to run the fuzz target
+// on the test corpus or on a single file,
+// e.g. the one that comes from a bug report.
+
+#include <cassert>
+#include <iostream>
+#include <fstream>
+#include <vector>
+
+// Forward declare the "fuzz target" interface.
+// We deliberately keep this inteface simple and header-free.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+// It reads all files passed as parameters and feeds their contents
+// one by one into the fuzz target (LLVMFuzzerTestOneInput).
+int main(int argc, char **argv) {
+ for (int i = 1; i < argc; i++) {
+ std::ifstream in(argv[i]);
+ in.seekg(0, in.end);
+ size_t length = in.tellg();
+ in.seekg (0, in.beg);
+ std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
+ // Allocate exactly length bytes so that we reliably catch buffer overflows.
+ std::vector<char> bytes(length);
+ in.read(bytes.data(), bytes.size());
+ LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
+ bytes.size());
+ std::cout << "Execution successful" << std::endl;
+ }
+ std::cout << "Execution finished" << std::endl;
+ return 0;
+} \ No newline at end of file