Pārlūkot izejas kodu

Add client fuzzing harness (#2437)

Cover client request processing logic. The goal is to enable this
running on OSS-Fuzz.

Signed-off-by: David Korczynski <david@adalogics.com>
DavidKorczynski 3 mēneši atpakaļ
vecāks
revīzija
0cbeafe6a4
2 mainītis faili ar 93 papildinājumiem un 1 dzēšanām
  1. 5 1
      test/fuzzing/Makefile
  2. 88 0
      test/fuzzing/client_fuzzer.cc

+ 5 - 1
test/fuzzing/Makefile

@@ -13,7 +13,7 @@ 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
 
-FUZZERS = server_fuzzer url_parser_fuzzer header_parser_fuzzer
+FUZZERS = server_fuzzer url_parser_fuzzer header_parser_fuzzer client_fuzzer
 
 # Runs all the tests and also fuzz tests against seed corpus.
 all : $(FUZZERS)
@@ -25,6 +25,10 @@ server_fuzzer : server_fuzzer.cc ../../httplib.h
 	$(CXX) $(CXXFLAGS) -o $@  $<  $(ZLIB_SUPPORT)  $(LIB_FUZZING_ENGINE) -pthread -lanl
 	zip -q -r server_fuzzer_seed_corpus.zip corpus
 
+client_fuzzer : client_fuzzer.cc ../../httplib.h
+	$(CXX) $(CXXFLAGS) -o $@  $<  $(ZLIB_SUPPORT)  $(LIB_FUZZING_ENGINE) -pthread -lanl
+	zip -q -r client_fuzzer_seed_corpus.zip corpus
+
 header_parser_fuzzer : header_parser_fuzzer.cc ../../httplib.h
 	$(CXX) $(CXXFLAGS) -o $@  $<  $(ZLIB_SUPPORT)  $(LIB_FUZZING_ENGINE) -pthread -lanl
 

+ 88 - 0
test/fuzzing/client_fuzzer.cc

@@ -0,0 +1,88 @@
+#include <cstdint>
+#include <cstring>
+#include <httplib.h>
+
+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 {
+    request_.append(ptr, size);
+    return static_cast<ssize_t>(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()); }
+
+  bool is_readable() const override { return true; }
+
+  bool wait_readable() const override { return true; }
+
+  bool wait_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;
+  }
+
+  void get_local_ip_and_port(std::string &ip, int &port) const override {
+    ip = "127.0.0.1";
+    port = 8080;
+  }
+
+  socket_t socket() const override { return 0; }
+
+  time_t duration() const override { return 0; };
+
+private:
+  const uint8_t *data_;
+  size_t size_;
+  size_t read_pos_;
+  std::string request_;
+};
+
+class FuzzableClient : public httplib::ClientImpl {
+public:
+  FuzzableClient() : httplib::ClientImpl("localhost", 8080) {}
+
+  void ProcessFuzzedResponse(FuzzedStream &stream, const std::string &method) {
+    httplib::Request req;
+    req.method = method;
+    req.path = "/";
+    httplib::Response res;
+    bool close_connection = false;
+    httplib::Error error = httplib::Error::Success;
+
+    process_request(stream, req, res, close_connection, error);
+  }
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  if (size < 1) return 0;
+
+  FuzzedStream stream{data + 1, size - 1};
+  FuzzableClient client;
+
+  // Use the first byte to select method
+  std::string method;
+  switch (data[0] % 6) {
+  case 0: method = "GET"; break;
+  case 1: method = "POST"; break;
+  case 2: method = "PUT"; break;
+  case 3: method = "PATCH"; break;
+  case 4: method = "DELETE"; break;
+  case 5: method = "OPTIONS"; break;
+  }
+
+  client.ProcessFuzzedResponse(stream, method);
+  return 0;
+}