diff --git a/projects/postgresql/Dockerfile b/projects/postgresql/Dockerfile index ca0db2740..ac3dcf96d 100644 --- a/projects/postgresql/Dockerfile +++ b/projects/postgresql/Dockerfile @@ -25,4 +25,4 @@ WORKDIR postgresql RUN mkdir bld COPY fuzzer $SRC/fuzzer -COPY build.sh $SRC/ +COPY build.sh add_fuzzers.diff $SRC/ diff --git a/projects/postgresql/add_fuzzers.diff b/projects/postgresql/add_fuzzers.diff new file mode 100644 index 000000000..2d3ce7b3b --- /dev/null +++ b/projects/postgresql/add_fuzzers.diff @@ -0,0 +1,68 @@ +diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c +index c9424f167c..aa2897ec63 100644 +--- a/src/backend/tcop/postgres.c ++++ b/src/backend/tcop/postgres.c +@@ -101,6 +101,10 @@ int max_stack_depth = 100; + /* wait N seconds to allow attach from a debugger */ + int PostAuthDelay = 0; + ++#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION ++bool fuzzer_first_run = true; ++#endif ++ + + + /* ---------------- +@@ -505,11 +509,14 @@ static int + ReadCommand(StringInfo inBuf) + { + int result; +- ++#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION ++ result = SocketBackend(inBuf); ++#else + if (whereToSendOutput == DestRemote) + result = SocketBackend(inBuf); + else + result = InteractiveBackend(inBuf); ++#endif + return result; + } + +@@ -3784,6 +3791,10 @@ PostgresMain(int argc, char *argv[], + volatile bool send_ready_for_query = true; + bool disable_idle_in_transaction_timeout = false; + ++#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION ++ if(fuzzer_first_run) ++ { ++#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ + /* Initialize startup process environment if necessary. */ + if (!IsUnderPostmaster) + InitStandaloneProcess(argv[0]); +@@ -4151,6 +4162,11 @@ PostgresMain(int argc, char *argv[], + if (!ignore_till_sync) + send_ready_for_query = true; /* initially, or after error */ + ++#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION ++ fuzzer_first_run=false; ++ } ++#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ ++ + /* + * Non-error queries loop here. + */ +diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c +index d0b368530e..02a3e9066e 100644 +--- a/src/backend/utils/error/elog.c ++++ b/src/backend/utils/error/elog.c +@@ -513,7 +513,9 @@ errfinish(const char *filename, int lineno, const char *funcname) + pq_endcopyout(true); + + /* Emit the message to the right places */ ++#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + EmitErrorReport(); ++#endif + + /* Now free up subsidiary data attached to stack entry, and release it */ + if (edata->message) diff --git a/projects/postgresql/build.sh b/projects/postgresql/build.sh index 0cc580065..12fb754a4 100644 --- a/projects/postgresql/build.sh +++ b/projects/postgresql/build.sh @@ -15,6 +15,7 @@ # ################################################################################ cp -r $SRC/fuzzer src/backend/ +git apply ../add_fuzzers.diff useradd fuzzuser chown -R fuzzuser . diff --git a/projects/postgresql/fuzzer/Makefile b/projects/postgresql/fuzzer/Makefile index 281630e82..656b8d69c 100644 --- a/projects/postgresql/fuzzer/Makefile +++ b/projects/postgresql/fuzzer/Makefile @@ -37,15 +37,17 @@ OBJS_FUZZERS = $(filter-out ../main/objfiles.txt, $(OBJS)) createdb: dbfuzz fuzzer: simple_query_fuzzer \ - json_parser_fuzzer - + json_parser_fuzzer \ + protocol_fuzzer simple_query_fuzzer json_parser_fuzzer: %: %.o fuzzer_initialize.o $(OBJS_FUZZERS) $(CXX) $(CFLAGS) $(call expand_subsys,$^) -o $@ $(LIB_FUZZING_ENGINE) -simple_query_fuzzer.o json_parser_fuzzer.o fuzzer_initialize.o: %.o: %.c +simple_query_fuzzer.o json_parser_fuzzer.o protocol_fuzzer.o fuzzer_initialize.o: %.o: %.c $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $^ +protocol_fuzzer: %: %.o $(OBJS_FUZZERS) + $(CXX) $(CFLAGS) $(call expand_subsys,$^) -o $@ $(LIB_FUZZING_ENGINE) -Wl,--wrap=exit -Wl,--wrap=pq_getbyte dbfuzz: dbfuzz.o | submake-libpgport temp-install $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@ \ diff --git a/projects/postgresql/fuzzer/protocol_fuzzer.c b/projects/postgresql/fuzzer/protocol_fuzzer.c new file mode 100644 index 000000000..7621644f7 --- /dev/null +++ b/projects/postgresql/fuzzer/protocol_fuzzer.c @@ -0,0 +1,115 @@ +// 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 "postgres.h" + +#include "access/xlog.h" +#include "access/xact.h" +#include "common/ip.h" +#include "common/username.h" +#include "executor/spi.h" +#include "jit/jit.h" +#include "libpq/auth.h" +#include "libpq/libpq.h" +#include "libpq/pqsignal.h" +#include "miscadmin.h" +#include "optimizer/optimizer.h" +#include "parser/analyze.h" +#include "parser/parser.h" +#include "storage/proc.h" +#include "tcop/tcopprot.h" +#include "utils/datetime.h" +#include "utils/memutils.h" +#include "utils/memdebug.h" +#include "utils/pidfile.h" +#include "utils/portal.h" +#include "utils/snapmgr.h" +#include "utils/ps_status.h" +#include "utils/timeout.h" + +#include +#include +#include +#include + +const char *progname = "progname"; +static sigjmp_buf postgre_exit; +static bool postgre_started; +static char *buffer; +static size_t buffersize; +static char *bufferpointer; +static char *av[6]; + +int LLVMFuzzerInitialize(int *argc, char ***argv) { + char *exe_path = (*argv)[0]; + //dirname() can modify its argument + char *exe_path_copy = strdup(exe_path); + char *dir = dirname(exe_path_copy); + chdir(dir); + free(exe_path_copy); + + av[0] = "tmp_install/usr/local/pgsql/bin/postgres"; + av[1] = "--single"; + av[2] = "-D/tmp/protocol_db/data"; + av[3] = "-F"; + av[4] = "-k\"/tmp\""; + av[5] = NULL; + + system("rm -rf /tmp/protocol_db; mkdir /tmp/protocol_db; cp -r data /tmp/protocol_db"); + system("cp -r tmp_install /tmp/"); + + MemoryContextInit(); + if(!sigsetjmp(postgre_exit, 0)){ + postgre_started = true; + PostgresMain(5, av, "dbfuzz", "fuzzuser"); + } + pq_endmsgread(); + return 0; +} + +void __wrap_exit(int status){ + if(postgre_started) + siglongjmp(postgre_exit, 1); + else + __real_exit(status); +} + +int __wrap_pq_getbyte(void){ + if(!buffersize) return EOF; + unsigned char cur = buffer[0]; + bufferpointer++; buffersize--; + return cur; +} + +/* +** Main entry point. The fuzzer invokes this function with each +** fuzzed input. +*/ +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + buffersize = size; + buffer = (char *) calloc(size, sizeof(char)); + bufferpointer = buffer; + memcpy(buffer, data, size); + + if(!sigsetjmp(postgre_exit, 0)){ + postgre_started = true; + PostgresMain(5, av, "dbfuzz", "fuzzuser"); + } + pq_endmsgread(); + postgre_started = false; + free(buffer); + return 0; +}