diff --git a/fuzzing/EpollEchoServerPubSub.cpp b/fuzzing/EpollEchoServerPubSub.cpp new file mode 100644 index 0000000..a3cace8 --- /dev/null +++ b/fuzzing/EpollEchoServerPubSub.cpp @@ -0,0 +1,98 @@ +/* We rely on wrapped syscalls */ +#include "libEpollFuzzer/epoll_fuzzer.h" + +#include "App.h" +#include + +/* We keep this one for teardown later on */ +struct us_listen_socket_t *listen_socket; + +/* This test is run by libEpollFuzzer */ +void test() { + + /* ws->getUserData returns one of these */ + struct PerSocketData { + /* Fill with user data */ + std::vector topics; + int nr = 0; + }; + + /* Keep in mind that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support. + * You may swap to using uWS:App() if you don't need SSL */ + uWS::SSLApp *app = new uWS::SSLApp({ + /* There are example certificates in uWebSockets.js repo */ + .key_file_name = "../misc/key.pem", + .cert_file_name = "../misc/cert.pem", + .passphrase = "1234" + }); + + app->ws("/*", { + /* Settings */ + .compression = uWS::DISABLED, + .maxPayloadLength = 16 * 1024 * 1024, + .idleTimeout = 60, + .maxBackpressure = 16 * 1024 * 1024, + .closeOnBackpressureLimit = false, + .resetIdleTimeoutOnSend = true, + .sendPingsAutomatically = false, + /* Handlers */ + .upgrade = nullptr, + .open = [](auto *ws) { + /* Open event here, you may access ws->getUserData() which points to a PerSocketData struct */ + + PerSocketData *perSocketData = (PerSocketData *) ws->getUserData(); + + for (int i = 0; i < 100; i++) { + std::string topic = std::to_string(ws); + perSocketData->topics.push_back(topic); + ws->subscribe(topic); + } + }, + .message = [&app](auto *ws, std::string_view message, uWS::OpCode opCode) { + PerSocketData *perSocketData = (PerSocketData *) ws->getUserData(); + + app->publish(perSocketData->topics[++nr % 100], message, opCode); + }, + .drain = [](auto */*ws*/) { + /* Check ws->getBufferedAmount() here */ + //std::cout << "drain" << std::endl; + }, + .ping = [](auto */*ws*/, std::string_view ) { + /* Not implemented yet */ + }, + .pong = [](auto */*ws*/, std::string_view ) { + /* Not implemented yet */ + }, + .close = [](auto */*ws*/, int /*code*/, std::string_view /*message*/) { + /* You may access ws->getUserData() here */ + } + }).listen(9001, [](auto *listen_s) { + if (listen_s) { + //std::cout << "Listening on port " << 9001 << std::endl; + listen_socket = listen_s; + } + }); + + app->run(); + + delete app; + + uWS::Loop::get()->free(); +} + +/* Thus function should shutdown the event-loop and let the test fall through */ +void teardown() { + /* If we are called twice there's a bug (it potentially could if + * all open sockets cannot be error-closed in one epoll_wait call). + * But we only allow 1k FDs and we have a buffer of 1024 from epoll_wait */ + if (!listen_socket) { + exit(-1); + } + + /* We might have open sockets still, and these will be error-closed by epoll_wait */ + // us_socket_context_close - close all open sockets created with this socket context + if (listen_socket) { + us_listen_socket_close(0, listen_socket); + listen_socket = NULL; + } +} diff --git a/fuzzing/EpollEchoServerPubSub.dict b/fuzzing/EpollEchoServerPubSub.dict new file mode 100644 index 0000000..48b49eb --- /dev/null +++ b/fuzzing/EpollEchoServerPubSub.dict @@ -0,0 +1,12 @@ +"get" +"post" +"get /" +"http/1.1" +"upgrade: websocket" +"\x0D\x0A" +"sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ==" +"sec-websocket-version: 13" +"get / http/1.1" +"sec-websocket-extensions: permessage-deflate" +"sec-websocket-protocol: " +" " \ No newline at end of file diff --git a/fuzzing/Makefile b/fuzzing/Makefile index 77d70ad..4108b7c 100644 --- a/fuzzing/Makefile +++ b/fuzzing/Makefile @@ -19,6 +19,8 @@ oss-fuzz: $(CXX) $(CXXFLAGS) $(WRAPPED_SYSCALLS) -std=c++17 -O3 -DUWS_MOCK_ZLIB -I../src -I../uSockets/src EpollHelloWorld.cpp -o $(OUT)/EpollHelloWorld $(LIB_FUZZING_ENGINE) *.o rm -f EpollHelloWorld.o $(CXX) $(CXXFLAGS) $(WRAPPED_SYSCALLS) -std=c++17 -O3 -DUWS_MOCK_ZLIB -I../src -I../uSockets/src EpollEchoServer.cpp -o $(OUT)/EpollEchoServer $(LIB_FUZZING_ENGINE) *.o + rm -f EpollEchoServer.o + $(CXX) $(CXXFLAGS) $(WRAPPED_SYSCALLS) -std=c++17 -O3 -DUWS_MOCK_ZLIB -I../src -I../uSockets/src EpollEchoServerPubSub.cpp -o $(OUT)/EpollEchoServerPubSub $(LIB_FUZZING_ENGINE) *.o # "Unit tests" $(CXX) $(CXXFLAGS) -std=c++17 -O3 Extensions.cpp -o $(OUT)/Extensions $(LIB_FUZZING_ENGINE) $(CXX) $(CXXFLAGS) -std=c++17 -O3 QueryParser.cpp -o $(OUT)/QueryParser $(LIB_FUZZING_ENGINE) diff --git a/src/AsyncSocket.h b/src/AsyncSocket.h index 1511419..b092ea9 100644 --- a/src/AsyncSocket.h +++ b/src/AsyncSocket.h @@ -73,8 +73,18 @@ protected: return us_socket_close(SSL, (us_socket_t *) this, 0, nullptr); } + void corkUnchecked() { + /* What if another socket is corked? */ + getLoopData()->corkedSocket = this; + } + /* Cork this socket. Only one socket may ever be corked per-loop at any given time */ void cork() { + /* Extra check for invalid corking of others */ + if (getLoopData()->corkOffset && getLoopData()->corkedSocket != this) { + std::abort(); + } + /* What if another socket is corked? */ getLoopData()->corkedSocket = this; } diff --git a/src/HttpResponse.h b/src/HttpResponse.h index fa7a88e..ba1bd2f 100644 --- a/src/HttpResponse.h +++ b/src/HttpResponse.h @@ -287,7 +287,7 @@ public: /* For whatever reason we were corked, update cork to the new socket */ if (wasCorked) { - webSocket->AsyncSocket::cork(); + webSocket->AsyncSocket::corkUnchecked(); } /* Initialize websocket with any moved backpressure intact */