diff --git a/.gitignore b/.gitignore index 9bb4e9e..f9b9eeb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ mirall example_clock example_simple +fastcgi_conn_afl *.o diff --git a/Makefile b/Makefile index 6d72446..febfbca 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,28 @@ +FCGI_CXX ?= clang++ +FCGI_CXXFLAGS ?= -O3 -std=gnu++2a -Wall -Werror +FCGI_LDLIBS ?= -lgflags -lglog -lpthread + all: example_simple example_clock objects = sse.o sse_stream.o fastcgi.o fastcgi_conn.o fastcgi_request.o fastcgi_parse.o stream_buffer.o buffer.o -example_simple: example_simple.o $(objects) Makefile - clang++ -O3 -std=gnu++2a -o example_simple example_simple.o $(objects) -lgflags -lglog -lpthread +example_simple: example_simple.o $(objects) + $(FCGI_CXX) $(FCGI_CXXFLAGS) -o $@ $+ $(FCGI_LDLIBS) -example_clock: example_clock.o $(objects) Makefile - clang++ -O3 -std=gnu++2a -o example_clock example_clock.o $(objects) -lgflags -lglog -lpthread +example_clock: example_clock.o $(objects) + $(FCGI_CXX) $(FCGI_CXXFLAGS) -o $@ $+ $(FCGI_LDLIBS) %.o: %.cc *.h Makefile - clang++ -O3 -std=gnu++2a -Wall -Werror -c -o $@ $< + $(FCGI_CXX) $(FCGI_CXXFLAGS) -c -o $@ $< clean: - rm --force exmaple_simple example_clock *.o + rm --force example_simple example_clock fastcgi_conn_afl *.o +afl: + $(MAKE) clean + FCGI_CXX=afl-g++ $(MAKE) afl_int + +afl_int: fastcgi_conn_afl + +fastcgi_conn_afl: fastcgi_conn_afl.o $(objects) + $(FCGI_CXX) $(FCGI_CXXFLAGS) -o $@ $+ $(FCGI_LDLIBS) diff --git a/buffer.h b/buffer.h index 80ccd68..1d11aa2 100644 --- a/buffer.h +++ b/buffer.h @@ -2,6 +2,7 @@ #include #include +#include class ConstBuffer { public: diff --git a/fastcgi.cc b/fastcgi.cc index 4fb71da..db33226 100644 --- a/fastcgi.cc +++ b/fastcgi.cc @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -74,6 +75,9 @@ void FastCGIServer::NewConn(int listen_sock, int epoll_fd) { PCHECK(client_sock >= 0) << "accept()"; CHECK_EQ(client_addr.sin6_family, AF_INET6); + int flags = 1; + PCHECK(setsockopt(client_sock, SOL_TCP, TCP_NODELAY, &flags, sizeof(flags)) == 0); + { auto *conn = new FastCGIConn(client_sock, client_addr, callback_, headers_); struct epoll_event ev{ diff --git a/fastcgi_conn.cc b/fastcgi_conn.cc index 417fc92..cb2ddc7 100644 --- a/fastcgi_conn.cc +++ b/fastcgi_conn.cc @@ -1,6 +1,5 @@ #include #include -#include #include #include "fastcgi_conn.h" @@ -17,9 +16,6 @@ FastCGIConn::FastCGIConn(int sock, const sockaddr_in6& client_addr, const std::f PCHECK(inet_ntop(AF_INET6, &client_addr.sin6_addr, client_addr_str, sizeof(client_addr_str))); LOG(INFO) << "new connection: [" << client_addr_str << "]:" << ntohs(client_addr.sin6_port); - - int flags = 1; - PCHECK(setsockopt(sock_, SOL_TCP, TCP_NODELAY, &flags, sizeof(flags)) == 0); } FastCGIConn::~FastCGIConn() { @@ -28,7 +24,7 @@ FastCGIConn::~FastCGIConn() { } bool FastCGIConn::Write(const std::vector& vecs) { - size_t total_size = 0; + ssize_t total_size = 0; for (const auto& vec : vecs) { total_size += vec.iov_len; } @@ -48,7 +44,11 @@ int FastCGIConn::Read() { break; } - CHECK_EQ(header->version, 1); + if (header->version != 1) { + LOG(ERROR) << "invalid FastCGI protocol version: " << header->version; + return sock_; + } + if (buf_.ReadMaxLen() < header->ContentLength()) { break; } @@ -56,9 +56,17 @@ int FastCGIConn::Read() { switch (header->type) { case 1: { - CHECK_EQ(header->ContentLength(), sizeof(FastCGIBeginRequest)); + if (header->ContentLength() != sizeof(FastCGIBeginRequest)) { + LOG(ERROR) << "FCGI_BeginRequestBody is the wrong length: " << header->ContentLength(); + return sock_; + } + const auto *begin_request = CHECK_NOTNULL(buf_.ReadObj()); - CHECK_EQ(begin_request->Role(), 1); + + if (begin_request->Role() != 1) { + LOG(ERROR) << "unsupported FastCGI role: " << begin_request->Role(); + return sock_; + } request_.reset(new FastCGIRequest(header->RequestId(), this)); } @@ -66,7 +74,11 @@ int FastCGIConn::Read() { case 4: { - CHECK_EQ(header->RequestId(), request_->RequestId()); + if (header->RequestId() != request_->RequestId()) { + LOG(ERROR) << "out of order FCGI_PARAMS record, or client is multiplexing requests (which we don't support)"; + return sock_; + } + ConstBuffer param_buf(buf_.Read(header->ContentLength()), header->ContentLength()); while (param_buf.ReadMaxLen() > 0) { const auto *param_header = param_buf.ReadObj(); @@ -81,7 +93,11 @@ int FastCGIConn::Read() { case 5: { - CHECK_EQ(header->RequestId(), request_->RequestId()); + if (header->RequestId() != request_->RequestId()) { + LOG(ERROR) << "out of order FCGI_STDIN record, or client is multiplexing requests (which we don't support)"; + return sock_; + } + if (header->ContentLength() == 0) { // Magic signal for completed request (mirrors the HTTP/1.1 protocol) requests_++; @@ -94,8 +110,8 @@ int FastCGIConn::Read() { break; default: - CHECK(false) << "unknown record type: " << header->type; - break; + LOG(ERROR) << "unknown record type: " << header->type; + return sock_; } if (!buf_.Discard(header->padding_length)) { diff --git a/fastcgi_conn.h b/fastcgi_conn.h index 939dfeb..3f0a964 100644 --- a/fastcgi_conn.h +++ b/fastcgi_conn.h @@ -1,14 +1,13 @@ #pragma once #include +#include #include #include +#include "fastcgi_request.h" #include "stream_buffer.h" -struct sockaddr_in6; -class FastCGIRequest; - class FastCGIConn { public: FastCGIConn(int sock, const sockaddr_in6& client_addr, const std::function)>& callback, const std::unordered_set& headers); diff --git a/fastcgi_conn_afl.cc b/fastcgi_conn_afl.cc new file mode 100644 index 0000000..d2f9328 --- /dev/null +++ b/fastcgi_conn_afl.cc @@ -0,0 +1,16 @@ +#include "fastcgi_conn.h" + +int main(int argc, char* argv[]) { + setenv("GLOG_logtostderr", "1", true); + google::InitGoogleLogging(argv[0]); + + { + FastCGIConn conn(STDIN_FILENO, {}, [](std::unique_ptr req) { req->End(); }, {}); + static_cast(conn.Read()); + } + + gflags::ShutDownCommandLineFlags(); + google::ShutdownGoogleLogging(); + + return 0; +}