mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2025-05-14 16:58:30 +00:00
Add cpp-httplib to oss-fuzz (#684)
* *Add server fuzzer target and seed corpus * Add fuzz_test option to Makefile * Fix #685 * Try to fix Github actions on Ubuntu * Added ReadTimeoutSSL test * Comment out `-fsanitize=address` * Rebase upstream changes * remove address sanitizer temporarily * Add separate Makefile for fuzzing * 1. Remove special char from dictionary 2. Clean fuzzing/Makefile * Use specific path to avoid accidently linking openssl version brought in by oss-fuzz * remove addition of flags * Refactor Makefile * Add missing newline * Add fuzztest to github workflow * Fix Co-authored-by: yhirose <yuji.hirose.bug@gmail.com>
This commit is contained in:
parent
cc5147ad72
commit
5292142046
9 changed files with 418 additions and 1 deletions
3
.github/workflows/test.yaml
vendored
3
.github/workflows/test.yaml
vendored
|
@ -27,6 +27,9 @@ jobs:
|
||||||
- name: make
|
- name: make
|
||||||
if: matrix.os != 'windows-latest'
|
if: matrix.os != 'windows-latest'
|
||||||
run: cd test && make
|
run: cd test && make
|
||||||
|
- name: check fuzz test target
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
run: cd test && make -f Makefile.fuzz_test
|
||||||
- name: setup msbuild on windows
|
- name: setup msbuild on windows
|
||||||
if: matrix.os == 'windows-latest'
|
if: matrix.os == 'windows-latest'
|
||||||
uses: warrenbuckley/Setup-MSBuild@v1
|
uses: warrenbuckley/Setup-MSBuild@v1
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
#CXX = clang++
|
#CXX = clang++
|
||||||
CXXFLAGS = -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion #-fsanitize=address
|
CXXFLAGS = -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion #-fsanitize=address
|
||||||
|
|
||||||
|
|
36
test/Makefile.fuzz_test
Normal file
36
test/Makefile.fuzz_test
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
|
||||||
|
#CXX = clang++
|
||||||
|
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
|
||||||
|
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# By default, use standalone_fuzz_target_runner.
|
||||||
|
# This runner does no fuzzing, but simply executes the inputs
|
||||||
|
# provided via parameters.
|
||||||
|
# Run e.g. "make all LIB_FUZZING_ENGINE=/path/to/libFuzzer.a"
|
||||||
|
# to link the fuzzer(s) against a real fuzzing engine.
|
||||||
|
# OSS-Fuzz will define its own value for LIB_FUZZING_ENGINE.
|
||||||
|
LIB_FUZZING_ENGINE ?= standalone_fuzz_target_runner.o
|
||||||
|
|
||||||
|
# Runs server_fuzzer.cc based on value of $(LIB_FUZZING_ENGINE).
|
||||||
|
# Usage: make fuzz_test LIB_FUZZING_ENGINE=/path/to/libFuzzer
|
||||||
|
all fuzz_test: server_fuzzer
|
||||||
|
./server_fuzzer fuzzing/corpus/*
|
||||||
|
|
||||||
|
# Fuzz target, so that you can choose which $(LIB_FUZZING_ENGINE) to use.
|
||||||
|
server_fuzzer : fuzzing/server_fuzzer.cc ../httplib.h standalone_fuzz_target_runner.o
|
||||||
|
$(CXX) $(CXXFLAGS) -o $@ $< $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
|
||||||
|
|
||||||
|
# Standalone fuzz runner, which just reads inputs from fuzzing/corpus/ dir and
|
||||||
|
# feeds it to server_fuzzer.
|
||||||
|
standalone_fuzz_target_runner.o : fuzzing/standalone_fuzz_target_runner.cpp
|
||||||
|
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f server_fuzzer pem *.0 *.o *.1 *.srl *.zip
|
26
test/fuzzing/Makefile
Normal file
26
test/fuzzing/Makefile
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
|
||||||
|
#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
|
||||||
|
zip -q -r server_fuzzer_seed_corpus.zip corpus
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f server_fuzzer pem *.0 *.o *.1 *.srl *.zip
|
1
test/fuzzing/corpus/1
Normal file
1
test/fuzzing/corpus/1
Normal file
|
@ -0,0 +1 @@
|
||||||
|
PUT /search/sample?a=12 HTTP/1.1
|
5
test/fuzzing/corpus/2
Normal file
5
test/fuzzing/corpus/2
Normal file
|
@ -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
|
88
test/fuzzing/server_fuzzer.cc
Normal file
88
test/fuzzing/server_fuzzer.cc
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
#include <memory>
|
||||||
|
#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 size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t write(const char* ptr, size_t size) override {
|
||||||
|
response_.append(ptr, size);
|
||||||
|
return static_cast<int>(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int write(const char* ptr) { return write(ptr, strlen(ptr)); }
|
||||||
|
|
||||||
|
int 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
224
test/fuzzing/server_fuzzer.dict
Normal file
224
test/fuzzing/server_fuzzer.dict
Normal file
|
@ -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"
|
35
test/fuzzing/standalone_fuzz_target_runner.cpp
Normal file
35
test/fuzzing/standalone_fuzz_target_runner.cpp
Normal file
|
@ -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;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue