From 5dd605d3a2b25bf38eafcc93428d30afb1b78cbb Mon Sep 17 00:00:00 2001 From: yhirose Date: Mon, 30 Nov 2020 21:46:36 -0500 Subject: [PATCH] Fix #762 --- httplib.h | 58 +++++++++++++++++++--------- test/fuzzing/server_fuzzer.cc | 72 +++++++++++++++++------------------ 2 files changed, 74 insertions(+), 56 deletions(-) diff --git a/httplib.h b/httplib.h index cfbf5ab..d5dda01 100644 --- a/httplib.h +++ b/httplib.h @@ -1340,14 +1340,6 @@ inline std::string from_i_to_hex(size_t n) { return ret; } -inline bool start_with(const std::string &a, const std::string &b) { - if (a.size() < b.size()) { return false; } - for (size_t i = 0; i < b.size(); i++) { - if (::tolower(a[i]) != ::tolower(b[i])) { return false; } - } - return true; -} - inline size_t to_utf8(int code, char *buff) { if (code < 0x0080) { buff[0] = (code & 0x7F); @@ -3126,7 +3118,7 @@ public: static const std::string header_name = "content-type:"; const auto header = buf_.substr(0, pos); - if (start_with(header, header_name)) { + if (start_with_case_ignore(header, header_name)) { file_.content_type = trim_copy(header.substr(header_name.size())); } else { std::smatch m; @@ -3148,15 +3140,7 @@ public: auto pattern = crlf_ + dash_; if (pattern.size() > buf_.size()) { return true; } - auto pos = buf_.find(pattern); - if (pos == std::string::npos) { - pos = buf_.size(); - while (pos > 0) { - auto c = buf_[pos - 1]; - if (c != '\r' && c != '\n' && c != '-') { break; } - pos--; - } - } + auto pos = find_string(buf_, pattern); if (!content_callback(buf_.data(), pos)) { is_valid_ = false; @@ -3166,7 +3150,6 @@ public: off_ += pos; buf_.erase(0, pos); } - { auto pattern = crlf_ + dash_ + boundary_; if (pattern.size() > buf_.size()) { return true; } @@ -3230,6 +3213,43 @@ private: file_.content_type.clear(); } + bool start_with_case_ignore(const std::string &a, + const std::string &b) const { + if (a.size() < b.size()) { return false; } + for (size_t i = 0; i < b.size(); i++) { + if (::tolower(a[i]) != ::tolower(b[i])) { return false; } + } + return true; + } + + bool start_with(const std::string &a, size_t off, + const std::string &b) const { + if (a.size() - off < b.size()) { return false; } + for (size_t i = 0; i < b.size(); i++) { + if (a[i + off] != b[i]) { return false; } + } + return true; + } + + size_t find_string(const std::string &s, const std::string &pattern) const { + auto c = pattern.front(); + + size_t off = 0; + while (off < s.size()) { + auto pos = s.find(c, off); + if (pos == std::string::npos) { return s.size(); } + + auto rem = s.size() - pos; + if (pattern.size() > rem) { return pos; } + + if (start_with(s, pos, pattern)) { return pos; } + + off = pos + 1; + } + + return s.size(); + } + std::string boundary_; std::string buf_; diff --git a/test/fuzzing/server_fuzzer.cc b/test/fuzzing/server_fuzzer.cc index 5ea1032..9fb4d4b 100644 --- a/test/fuzzing/server_fuzzer.cc +++ b/test/fuzzing/server_fuzzer.cc @@ -1,28 +1,26 @@ -#include #include +#include class FuzzedStream : public httplib::Stream { - public: - FuzzedStream(const uint8_t* data, size_t size) +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_; - } + 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; + return static_cast(size); } - ssize_t write(const char* ptr, size_t size) override { + ssize_t write(const char *ptr, size_t size) override { response_.append(ptr, size); return static_cast(size); } - int write(const char* ptr) { return write(ptr, strlen(ptr)); } + ssize_t write(const char *ptr) { return write(ptr, strlen(ptr)); } - int write(const std::string& s) { return write(s.data(), s.size()); } + ssize_t write(const std::string &s) { return write(s.data(), s.size()); } std::string get_remote_addr() const { return ""; } @@ -37,16 +35,16 @@ class FuzzedStream : public httplib::Stream { socket_t socket() const override { return 0; } - private: - const uint8_t* data_; +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) { +public: + void ProcessFuzzedRequest(FuzzedStream &stream) { bool connection_close = false; process_request(stream, /*last_connection=*/false, connection_close, nullptr); @@ -55,36 +53,36 @@ class FuzzableServer : public httplib::Server { static FuzzableServer g_server; -extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { +extern "C" int LLVMFuzzerInitialize(int * /*argc*/, char *** /*argv*/) { g_server.Get(R"(.*)", - [&](const httplib::Request& req, httplib::Response& res) { + [&](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.Post(R"(.*)", - [&](const httplib::Request& req, httplib::Response& res) { + 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.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"); - }); + 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) { +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { FuzzedStream stream{data, size}; g_server.ProcessFuzzedRequest(stream); return 0; -} \ No newline at end of file +}