aboutsummaryrefslogtreecommitdiff
path: root/projects/nginx
diff options
context:
space:
mode:
authorYunshu Ouyang <61579667+yooyoo9@users.noreply.github.com>2020-08-30 19:21:28 +0200
committerGitHub <noreply@github.com>2020-08-30 10:21:28 -0700
commit740536e25e3fad2fb897d77f1d8c5981eb50e587 (patch)
treeba81e0240ee93392a48a3d9c3cf464d281e48972 /projects/nginx
parent16b04c4a99ed149f79c66d506b1f017eb284d27c (diff)
downloadoss-fuzz-740536e25e3fad2fb897d77f1d8c5981eb50e587.tar.gz
[Nginx] Initial integration, new fuzzer added (#4144)
* [postgresql] Added auto_css to project.yaml * [postgresql] Added new fuzzer * [postgresql] Cleaned up files, changed project.yaml * Dockerfile - changed to official repo * Renamed fix.diff * [postgresql] Fixed parser_fuzzer, added new json_parser_fuzzer * [nginx] updated project.yaml * [nginx] added first fuzzer * [nginx] added build file for fuzzers * Added license header * Removed dictionary * Removed dictionary * Moved fuzzers to fuzzer directory * fixed new lines * Updated years, removed maintainer field in Dockerfile * Removed line spaces * Updated year * Changed existing fuzzer * Changed Makefile * delete newlines * remove patch * update year * add socket wrapper functions * modified fuzzer initialization * add new grpc module fuzzer * Removed grpc fuzzer * Fixed http request fuzzer * Add nginx patch * Add Makefile for fuzzers * Fix fuzzer * Added client side fuzzing * fixed fuzzer * Removed memory and undefined sanitizers * Added dictionary * Changed fuzzer from c to c++ * Use protobuf for input to fuzzer * Improved style * Added configuration file needed by fuzzer * Added licence header * Added makefile for fuzzers * Fixed dictionary * Modified make_fuzzers * moved dictionary * Undo last commit
Diffstat (limited to 'projects/nginx')
-rw-r--r--projects/nginx/Dockerfile27
-rw-r--r--projects/nginx/add_fuzzers.diff12
-rw-r--r--projects/nginx/build.sh32
-rw-r--r--projects/nginx/fuzz/http_request_fuzzer.cc321
-rw-r--r--projects/nginx/fuzz/http_request_fuzzer.dict152
-rw-r--r--projects/nginx/fuzz/http_request_proto.proto23
-rw-r--r--projects/nginx/fuzz/wrappers.c34
-rw-r--r--projects/nginx/make_fuzzers39
-rw-r--r--projects/nginx/project.yaml6
9 files changed, 646 insertions, 0 deletions
diff --git a/projects/nginx/Dockerfile b/projects/nginx/Dockerfile
new file mode 100644
index 000000000..d567f2f5c
--- /dev/null
+++ b/projects/nginx/Dockerfile
@@ -0,0 +1,27 @@
+# Copyright 2020 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+################################################################################
+
+FROM gcr.io/oss-fuzz-base/base-builder
+
+RUN apt-get update && apt-get install -y libpcre3-dev zlib1g-dev mercurial ninja-build cmake liblzma-dev libz-dev binutils libtool
+RUN hg clone http://hg.nginx.org/nginx/
+RUN git clone --depth 1 https://github.com/google/libprotobuf-mutator.git
+RUN git clone --depth 1 https://github.com/google/fuzzer-test-suite
+RUN (mkdir LPM && cd LPM && cmake ../libprotobuf-mutator -GNinja -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON -DLIB_PROTO_MUTATOR_TESTING=OFF -DCMAKE_BUILD_TYPE=Release && ninja)
+
+WORKDIR nginx
+COPY fuzz $SRC/fuzz
+COPY build.sh add_fuzzers.diff make_fuzzers $SRC/
diff --git a/projects/nginx/add_fuzzers.diff b/projects/nginx/add_fuzzers.diff
new file mode 100644
index 000000000..28b41afde
--- /dev/null
+++ b/projects/nginx/add_fuzzers.diff
@@ -0,0 +1,12 @@
+diff --git a/auto/configure b/auto/configure
+index 7e6e33a7..bb368cfb 100755
+--- a/auto/configure
++++ b/auto/configure
+@@ -100,6 +100,7 @@ have=NGX_HTTP_SCGI_TEMP_PATH value="\"$NGX_HTTP_SCGI_TEMP_PATH\""
+ . auto/define
+
+ . auto/make
++. auto/make_fuzzers
+ . auto/lib/make
+ . auto/install
+
diff --git a/projects/nginx/build.sh b/projects/nginx/build.sh
new file mode 100644
index 000000000..064a91739
--- /dev/null
+++ b/projects/nginx/build.sh
@@ -0,0 +1,32 @@
+#!/bin/bash -eu
+# Copyright 2020 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+################################################################################
+hg import $SRC/add_fuzzers.diff --no-commit
+
+cp -r $SRC/fuzz src/
+cp $SRC/make_fuzzers auto/make_fuzzers
+
+cd src/fuzz
+rm -rf genfiles && mkdir genfiles && $SRC/LPM/external.protobuf/bin/protoc http_request_proto.proto --cpp_out=genfiles
+cd ../..
+
+auto/configure \
+ --with-ld-opt="-Wl,--wrap=listen -Wl,--wrap=setsockopt -Wl,--wrap=bind -Wl,--wrap=shutdown -Wl,--wrap=connect" \
+ --with-http_v2_module
+make -f objs/Makefile fuzzers
+
+cp objs/*_fuzzer $OUT/
+cp $SRC/fuzz/*.dict $OUT/
diff --git a/projects/nginx/fuzz/http_request_fuzzer.cc b/projects/nginx/fuzz/http_request_fuzzer.cc
new file mode 100644
index 000000000..bed04d7d0
--- /dev/null
+++ b/projects/nginx/fuzz/http_request_fuzzer.cc
@@ -0,0 +1,321 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+extern "C" {
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_http.h>
+}
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "http_request_proto.pb.h"
+#include "libprotobuf-mutator/src/libfuzzer/libfuzzer_macro.h"
+
+static char configuration[] =
+"error_log stderr emerg;\n"
+"events {\n"
+" use epoll;\n"
+" worker_connections 2;\n"
+" multi_accept off;\n"
+" accept_mutex off;\n"
+"}\n"
+"http {\n"
+" server_tokens off;\n"
+" default_type application/octet-stream;\n"
+" map $http_upgrade $connection_upgrade {\n"
+" default upgrade;\n"
+" '' close;\n"
+" }\n"
+" error_log stderr emerg;\n"
+" access_log off;\n"
+" map $subdomain $nss {\n"
+" default local_upstream;\n"
+" }\n"
+" upstream local_upstream {\n"
+" server 127.0.0.1:1010 max_fails=0;\n"
+" server 127.0.0.1:1011 max_fails=0;\n"
+" server 127.0.0.1:1012 max_fails=0;\n"
+" server 127.0.0.1:1013 max_fails=0;\n"
+" server 127.0.0.1:1014 max_fails=0;\n"
+" server 127.0.0.1:1015 max_fails=0;\n"
+" server 127.0.0.1:1016 max_fails=0;\n"
+" server 127.0.0.1:1017 max_fails=0;\n"
+" server 127.0.0.1:1018 max_fails=0;\n"
+" server 127.0.0.1:1019 max_fails=0;\n"
+" }\n"
+" client_max_body_size 256M;\n"
+" client_body_temp_path /tmp/;\n"
+" proxy_temp_path /tmp/;\n"
+" proxy_buffer_size 24K;\n"
+" proxy_max_temp_file_size 0;\n"
+" proxy_buffers 8 4K;\n"
+" proxy_busy_buffers_size 28K;\n"
+" proxy_buffering off;\n"
+" server {\n"
+" listen unix:nginx.sock;\n"
+" server_name ~^(?<subdomain>.+)\\.url.com$;\n"
+" proxy_next_upstream off;\n"
+" proxy_read_timeout 5m;\n"
+" proxy_http_version 1.1;\n"
+" proxy_set_header Host $http_host;\n"
+" proxy_set_header X-Real-IP $remote_addr;\n"
+" proxy_set_header X-Real-Port $remote_port;\n"
+" location / {\n"
+" proxy_pass http://$nss;\n"
+" proxy_set_header Host $http_host;\n"
+" proxy_set_header X-Real-IP $remote_addr;\n"
+" proxy_set_header X-Real-Port $remote_port;\n"
+" proxy_set_header Connection '';\n"
+" chunked_transfer_encoding off;\n"
+" proxy_buffering off;\n"
+" proxy_cache off;\n"
+" }\n"
+" }\n"
+"}\n"
+"\n";
+
+
+static ngx_cycle_t *cycle;
+static ngx_log_t ngx_log;
+static ngx_open_file_t ngx_log_file;
+static char *my_argv[2];
+static char arg1[] = {0, 0xA, 0};
+
+extern char **environ;
+
+static const char *config_file = "http_config.conf";
+
+struct fuzzing_data {
+ const uint8_t *data;
+ size_t data_len;
+};
+
+static struct fuzzing_data request;
+static struct fuzzing_data reply;
+
+static ngx_http_upstream_t *upstream;
+static ngx_http_request_t *req_reply;
+static ngx_http_cleanup_t cln_new = {};
+static int cln_added;
+
+// Called when finalizing the request to upstream
+// Do not need to clean the request pool
+static void cleanup_reply(void *data) { req_reply = NULL; }
+
+// Called by the http parser to read the buffer
+static ssize_t request_recv_handler(ngx_connection_t *c, u_char *buf,
+ size_t size) {
+ if (request.data_len < size)
+ size = request.data_len;
+ memcpy(buf, request.data, size);
+ request.data += size;
+ request.data_len -= size;
+ return size;
+}
+
+// Feed fuzzing input for the reply from upstream
+static ssize_t reply_recv_handler(ngx_connection_t *c, u_char *buf,
+ size_t size) {
+ req_reply = (ngx_http_request_t *)(c->data);
+ if (!cln_added) { // add cleanup so that we know whether everything is cleanup
+ // correctly
+ cln_added = 1;
+ cln_new.handler = cleanup_reply;
+ cln_new.next = req_reply->cleanup;
+ cln_new.data = NULL;
+ req_reply->cleanup = &cln_new;
+ }
+ upstream = req_reply->upstream;
+
+ if (reply.data_len < size)
+ size = reply.data_len;
+ memcpy(buf, reply.data, size);
+ reply.data += size;
+ reply.data_len -= size;
+ if (size == 0)
+ c->read->ready = 0;
+ return size;
+}
+
+static ngx_int_t add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) {
+ return NGX_OK;
+}
+
+static ngx_int_t init_event(ngx_cycle_t *cycle, ngx_msec_t timer) {
+ return NGX_OK;
+}
+
+// Used when sending data, do nothing
+static ngx_chain_t *send_chain(ngx_connection_t *c, ngx_chain_t *in,
+ off_t limit) {
+ c->read->ready = 1;
+ c->recv = reply_recv_handler;
+ return in->next;
+}
+
+// Create a base state for Nginx without starting the server
+extern "C" int InitializeNginx(void) {
+ ngx_log_t *log;
+ ngx_cycle_t init_cycle;
+
+ if (access("nginx.sock", F_OK) != -1) {
+ remove("nginx.sock");
+ }
+
+ ngx_debug_init();
+ ngx_strerror_init();
+ ngx_time_init();
+ ngx_regex_init();
+
+ // Just output logs to stderr
+ ngx_log.file = &ngx_log_file;
+ ngx_log.log_level = NGX_LOG_EMERG;
+ ngx_log_file.fd = ngx_stderr;
+ log = &ngx_log;
+
+ ngx_memzero(&init_cycle, sizeof(ngx_cycle_t));
+ init_cycle.log = log;
+ ngx_cycle = &init_cycle;
+
+ init_cycle.pool = ngx_create_pool(1024, log);
+
+ // Set custom argv/argc
+ my_argv[0] = arg1;
+ my_argv[1] = NULL;
+ ngx_argv = ngx_os_argv = my_argv;
+ ngx_argc = 0;
+
+ // Weird trick to free a leaking buffer always caught by ASAN
+ // We basically let ngx overwrite the environment variable, free the leak and
+ // restore the environment as before.
+ char *env_before = environ[0];
+ environ[0] = my_argv[0] + 1;
+ ngx_os_init(log);
+ free(environ[0]);
+ environ[0] = env_before;
+
+ ngx_crc32_table_init();
+ ngx_preinit_modules();
+
+ FILE *fptr = fopen(config_file, "w");
+ fprintf(fptr, "%s", configuration);
+ fclose(fptr);
+ init_cycle.conf_file.len = strlen(config_file);
+ init_cycle.conf_file.data = (unsigned char *) config_file;
+
+ cycle = ngx_init_cycle(&init_cycle);
+
+ ngx_os_status(cycle->log);
+ ngx_cycle = cycle;
+
+ ngx_event_actions.add = add_event;
+ ngx_event_actions.init = init_event;
+ ngx_io.send_chain = send_chain;
+ ngx_event_flags = 1;
+ ngx_event_timer_init(cycle->log);
+ return 0;
+}
+
+extern "C" long int invalid_call(ngx_connection_s *a, ngx_chain_s *b,
+ long int c) {
+ return 0;
+}
+
+DEFINE_PROTO_FUZZER(const HttpProto &input) {
+ static int init = InitializeNginx();
+ assert(init == 0);
+
+ // have two free connections, one for client, one for upstream
+ ngx_event_t read_event1 = {};
+ ngx_event_t write_event1 = {};
+ ngx_connection_t local1 = {};
+ ngx_event_t read_event2 = {};
+ ngx_event_t write_event2 = {};
+ ngx_connection_t local2 = {};
+ ngx_connection_t *c;
+ ngx_listening_t *ls;
+
+ req_reply = NULL;
+ upstream = NULL;
+ cln_added = 0;
+
+ const char *req_string = input.request().c_str();
+ size_t req_len = strlen(req_string);
+ const char *rep_string = input.reply().c_str();
+ size_t rep_len = strlen(rep_string);
+ request.data = (const uint8_t *)req_string;
+ request.data_len = req_len;
+ reply.data = (const uint8_t *)rep_string;
+ reply.data_len = rep_len;
+
+ // Use listening entry created from configuration
+ ls = (ngx_listening_t *)ngx_cycle->listening.elts;
+
+ // Fake event ready for dispatch on read
+ local1.read = &read_event1;
+ local1.write = &write_event1;
+ local2.read = &read_event2;
+ local2.write = &write_event2;
+ local2.send_chain = send_chain;
+
+ // Create fake free connection to feed the http handler
+ ngx_cycle->free_connections = &local1;
+ local1.data = &local2;
+ ngx_cycle->free_connection_n = 2;
+
+ // Initialize connection
+ c = ngx_get_connection(
+ 255, &ngx_log); // 255 - (hopefully unused) socket descriptor
+
+ c->shared = 1;
+ c->type = SOCK_STREAM;
+ c->pool = ngx_create_pool(256, ngx_cycle->log);
+ c->sockaddr = ls->sockaddr;
+ c->listening = ls;
+ c->recv = request_recv_handler; // Where the input will be read
+ c->send_chain = send_chain;
+ c->send = (ngx_send_pt)invalid_call;
+ c->recv_chain = (ngx_recv_chain_pt)invalid_call;
+ c->log = &ngx_log;
+ c->pool->log = &ngx_log;
+ c->read->log = &ngx_log;
+ c->write->log = &ngx_log;
+ c->socklen = ls->socklen;
+ c->local_sockaddr = ls->sockaddr;
+ c->local_socklen = ls->socklen;
+
+ read_event1.ready = 1;
+ write_event1.ready = write_event1.delayed = 1;
+
+ // Will redirect to http parser
+ ngx_http_init_connection(c);
+
+ // Clean-up in case of error
+ if (req_reply && upstream && upstream->cleanup) {
+ (*(upstream->cleanup))(req_reply);
+ if (!c->destroyed)
+ ngx_http_close_connection(c);
+ } else if (!c->destroyed) {
+ ngx_http_request_t *r = (ngx_http_request_t *)(c->data);
+ ngx_http_free_request(r, 0);
+ ngx_http_close_connection(c);
+ }
+}
diff --git a/projects/nginx/fuzz/http_request_fuzzer.dict b/projects/nginx/fuzz/http_request_fuzzer.dict
new file mode 100644
index 000000000..6a21c63bf
--- /dev/null
+++ b/projects/nginx/fuzz/http_request_fuzzer.dict
@@ -0,0 +1,152 @@
+# 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"
diff --git a/projects/nginx/fuzz/http_request_proto.proto b/projects/nginx/fuzz/http_request_proto.proto
new file mode 100644
index 000000000..22a53afdf
--- /dev/null
+++ b/projects/nginx/fuzz/http_request_proto.proto
@@ -0,0 +1,23 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+syntax = "proto2";
+// Proto description of the http request fuzzer input format
+
+message HttpProto{
+ required string request = 1;
+ required string reply = 2;
+}
diff --git a/projects/nginx/fuzz/wrappers.c b/projects/nginx/fuzz/wrappers.c
new file mode 100644
index 000000000..ec8e3b7db
--- /dev/null
+++ b/projects/nginx/fuzz/wrappers.c
@@ -0,0 +1,34 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+#include <sys/socket.h>
+#include <sys/types.h>
+
+int __wrap_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
+ return 0;
+}
+
+int __wrap_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
+ return 0;
+}
+
+int __wrap_shutdown(int socket, int how) { return 0; }
+
+ssize_t __wrap_listen(int fd, void *buf, size_t bytes) { return 0; }
+
+int __wrap_setsockopt(int fd, int level, int optname, const void *optval,
+ socklen_t optlen) {
+ return 0;
+}
diff --git a/projects/nginx/make_fuzzers b/projects/nginx/make_fuzzers
new file mode 100644
index 000000000..a62d4d6ec
--- /dev/null
+++ b/projects/nginx/make_fuzzers
@@ -0,0 +1,39 @@
+sed -i 's/main(/fuzz_without_main(/g' src/core/nginx.c
+
+mkdir -p $NGX_OBJS/src/fuzz
+
+ngx_all_objs_fuzz=`echo src/fuzz/wrappers.c $ngx_all_srcs \
+ | sed -e "s#\([^ ]*\.\)cc#$NGX_OBJS\/\1$ngx_objext#g" \
+ -e "s#\([^ ]*\.\)c#$NGX_OBJS\/\1$ngx_objext#g"`
+
+ngx_deps_fuzz=`echo $ngx_all_objs_fuzz $ngx_modules_obj $ngx_res $LINK_DEPS \
+ | sed -e "s/ *\([^ ][^ ]*\)/$ngx_regex_cont\1/g" \
+ -e "s/\//$ngx_regex_dirsep/g"`
+
+ngx_objs=`echo objs/src/fuzz/wrappers.o $ngx_all_objs $ngx_modules_obj \
+ | sed -e "s/ *\([^ ][^ ]*\)/$ngx_long_regex_cont\1/g" \
+ -e "s/\//$ngx_regex_dirsep/g"`
+
+cat << END >> $NGX_MAKEFILE
+
+fuzzers: objs/http_request_fuzzer
+
+objs/src/fuzz/wrappers.o:
+ \$(CC) $ngx_compile_opt \$(CFLAGS) -o objs/src/fuzz/wrappers.o src/fuzz/wrappers.c
+
+objs/http_request_fuzzer: $ngx_deps_fuzz
+ \$(CXX) \$(CXXFLAGS) src/fuzz/http_request_fuzzer.cc \
+ src/fuzz/genfiles/http_request_proto.pb.cc \
+ -o objs/http_request_fuzzer \
+ \$(CORE_INCS) \$(HTTP_INCS) \
+ -I src/fuzz/genfiles/ -I \$(SRC)/ \
+ -I \$(SRC)/libprotobuf-mutator/ \
+ -I \$(SRC)/LPM/external.protobuf/include \
+ \$(SRC)/LPM/src/libfuzzer/libprotobuf-mutator-libfuzzer.a \
+ \$(SRC)/LPM/src/libprotobuf-mutator.a \
+ \$(SRC)/LPM/external.protobuf/lib/libprotobuf.a \
+ $ngx_binexit$ngx_long_cont$ngx_objs \
+ \$(LIB_FUZZING_ENGINE) $ngx_libs$ngx_link$ngx_main_link -lcrypt
+$ngx_long_end
+
+END
diff --git a/projects/nginx/project.yaml b/projects/nginx/project.yaml
index 9b23bbedf..0aa90207a 100644
--- a/projects/nginx/project.yaml
+++ b/projects/nginx/project.yaml
@@ -1,2 +1,8 @@
homepage: "http://nginx.org"
+language: c
primary_contact: "xim.andrew@gmail.com"
+auto_ccs:
+ - ouyangyunshu@google.com
+ - mmoroz@google.com
+sanitizers:
+ - address