diff --git a/test/fuzzing/Makefile b/test/fuzzing/Makefile index e0874d95..82773755 100644 --- a/test/fuzzing/Makefile +++ b/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 client_fuzzer multipart_parser_fuzzer +FUZZERS = server_fuzzer url_parser_fuzzer header_parser_fuzzer client_fuzzer multipart_parser_fuzzer websocket_fuzzer # Runs all the tests and also fuzz tests against seed corpus. all : $(FUZZERS) @@ -38,5 +38,8 @@ url_parser_fuzzer : url_parser_fuzzer.cc ../../httplib.h multipart_parser_fuzzer : multipart_parser_fuzzer.cc ../../httplib.h $(CXX) $(CXXFLAGS) -o $@ $< $(ZLIB_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread -lanl +websocket_fuzzer : websocket_fuzzer.cc ../../httplib.h + $(CXX) $(CXXFLAGS) -o $@ $< $(ZLIB_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread -lanl + clean: rm -f server_fuzzer pem *.0 *.o *.1 *.srl *.zip diff --git a/test/fuzzing/websocket_fuzzer.cc b/test/fuzzing/websocket_fuzzer.cc new file mode 100644 index 00000000..36622b03 --- /dev/null +++ b/test/fuzzing/websocket_fuzzer.cc @@ -0,0 +1,54 @@ +#include +#include + +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size < 2) { return 0; } + + auto selector = data[0]; + std::string input(reinterpret_cast(data + 1), size - 1); + + switch (selector % 4) { + case 0: { + // read_websocket_frame. WebSocket::read passes the macro, close() passes + // 125, and 0 means no limit + static const size_t max_lens[] = {0, 125, + CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH}; + auto expect_masked = (selector / 4) % 2 != 0; + auto max_len = max_lens[(selector / 8) % 3]; + httplib::detail::BufferStream strm; + strm.write(input.data(), input.size()); + auto opcode = httplib::ws::Opcode::Text; + std::string payload; + auto fin = false; + httplib::ws::impl::read_websocket_frame(strm, opcode, payload, fin, + expect_masked, max_len); + break; + } + case 1: { + // is_valid_utf8 + httplib::ws::impl::is_valid_utf8(input); + break; + } + case 2: { + // is_valid_close_payload + httplib::ws::impl::is_valid_close_payload(input); + break; + } + case 3: { + // read_websocket_upgrade_response, with a fixed key since + // perform_websocket_handshake draws a random one + static const auto expected_accept = + httplib::detail::websocket_accept_key("dGhlIHNhbXBsZSBub25jZQ=="); + httplib::detail::BufferStream strm; + strm.write(input.data(), input.size()); + httplib::detail::WebSocketUpgradeResponse upgrade; + httplib::detail::read_websocket_upgrade_response(strm, expected_accept, + "chat, stream", upgrade); + break; + } + } + + return 0; +} diff --git a/test/fuzzing/websocket_fuzzer.options b/test/fuzzing/websocket_fuzzer.options new file mode 100644 index 00000000..ab1c02c4 --- /dev/null +++ b/test/fuzzing/websocket_fuzzer.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 70000