You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by as...@apache.org on 2018/03/22 06:20:27 UTC

[23/28] qpid-proton git commit: PROTON-1412 add fuzzers to proton-c tests

PROTON-1412 add fuzzers to proton-c tests


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/aadabc31
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/aadabc31
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/aadabc31

Branch: refs/heads/master
Commit: aadabc31e350fcb29ae8d7ff52ce8404e5d6a6c7
Parents: 40e3dcb
Author: Ji?? Dan?k <dn...@mail.muni.cz>
Authored: Fri Nov 18 17:17:20 2016 +0100
Committer: Andrew Stitcher <as...@apache.org>
Committed: Wed Mar 21 23:31:46 2018 -0400

----------------------------------------------------------------------
 proton-c/src/tests/fuzz/CMakeLists.txt          |  56 +++
 proton-c/src/tests/fuzz/Dockerfile              |  48 +++
 proton-c/src/tests/fuzz/README.md               |  68 ++++
 .../src/tests/fuzz/fuzz-connection-driver.c     | 222 ++++++++++++
 proton-c/src/tests/fuzz/fuzz-message-decode.c   |  42 +++
 proton-c/src/tests/fuzz/fuzz-proactor-receive.c | 362 +++++++++++++++++++
 proton-c/src/tests/fuzz/fuzz-sniff-header.c     |  33 ++
 proton-c/src/tests/fuzz/fuzz-url.c              |  43 +++
 proton-c/src/tests/fuzz/libFuzzingEngine.h      |  40 ++
 9 files changed, 914 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadabc31/proton-c/src/tests/fuzz/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/src/tests/fuzz/CMakeLists.txt b/proton-c/src/tests/fuzz/CMakeLists.txt
new file mode 100644
index 0000000..8e5057f
--- /dev/null
+++ b/proton-c/src/tests/fuzz/CMakeLists.txt
@@ -0,0 +1,56 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+#
+
+add_definitions(${COMPILE_WARNING_FLAGS} ${COMPILE_PLATFORM_FLAGS})
+
+add_library (StandaloneFuzzTargetMain StandaloneFuzzTargetMain.c)
+if (BUILD_WITH_CXX)
+  # ignore the flag for this file, it complicates things for no gain
+endif (BUILD_WITH_CXX)
+
+macro (pn_add_fuzz_test test)
+  add_executable (${test} ${ARGN})
+  if (FUZZING_ENGINE)
+    target_link_libraries (${test} qpid-proton FuzzingEngine)
+  else (FUZZING_ENGINE)
+    target_link_libraries (${test} qpid-proton StandaloneFuzzTargetMain)
+  endif (FUZZING_ENGINE)
+  if (BUILD_WITH_CXX)
+    set_source_files_properties (${ARGN} PROPERTIES LANGUAGE CXX)
+  else (BUILD_WITH_CXX)
+    set_target_properties(${test} PROPERTIES LINKER_LANGUAGE CXX)
+  endif (BUILD_WITH_CXX)
+  
+  if (FUZZING_ENGINE)
+    add_test (${test} ${CMAKE_CURRENT_BINARY_DIR}/${test} -runs=1 ${CMAKE_CURRENT_SOURCE_DIR}/${test})
+  else (FUZZING_ENGINE)
+    # StandaloneFuzzTargetMain cannot walk directory trees
+    file(GLOB_RECURSE files ${CMAKE_CURRENT_SOURCE_DIR}/${test}/*)
+    add_test (${test} ${CMAKE_CURRENT_BINARY_DIR}/${test} ${files})
+  endif (FUZZING_ENGINE)
+endmacro(pn_add_fuzz_test)
+
+pn_add_fuzz_test (fuzz-connection-driver fuzz-connection-driver.c)
+pn_add_fuzz_test (fuzz-message-decode fuzz-message-decode.c)
+# pni_sniff_header is internal, it has to be compiled in anew
+pn_add_fuzz_test (fuzz-sniff-header fuzz-sniff-header.c ${PROJECT_SOURCE_DIR}/proton-c/src/core/autodetect.c)
+pn_add_fuzz_test (fuzz-url fuzz-url.c)
+if(HAS_PROACTOR)
+  pn_add_fuzz_test (fuzz-proactor-receive fuzz-proactor-receive.c)
+endif(HAS_PROACTOR)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadabc31/proton-c/src/tests/fuzz/Dockerfile
----------------------------------------------------------------------
diff --git a/proton-c/src/tests/fuzz/Dockerfile b/proton-c/src/tests/fuzz/Dockerfile
new file mode 100644
index 0000000..053879a
--- /dev/null
+++ b/proton-c/src/tests/fuzz/Dockerfile
@@ -0,0 +1,48 @@
+# Copyright 2017 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 ossfuzz/base-builder
+MAINTAINER jdanek@redhat.com
+RUN apt-get install -y \
+    cmake \
+    libuv1-dev
+    # (optional) add vim
+
+# (optional) customize enviromental variables
+#ENV FUZZING_ENGINE
+#ENV SANITIZER_FLAGS
+
+# copy qpid-proton from filesystem into the container
+COPY . ./qpid-proton
+WORKDIR /src/qpid-proton
+
+# refresh the build directory if it exists already
+RUN rm build -rf || true
+
+# /usr/local/bin/compile compiles libFuzzer, then calls /src/build.sh
+# and sets correct environment variables for it
+RUN echo cmake .. -DCMAKE_BUILD_TYPE=Debug -DFUZZ_TEST=ON -DFUZZING_ENGINE=ON > /src/build.sh
+
+# build it
+RUN mkdir build
+WORKDIR /src/qpid-proton/build
+RUN /usr/local/bin/compile
+WORKDIR /src/qpid-proton/build/proton-c/src/tests/fuzz
+RUN make
+RUN ls
+
+# run corpus through fuzzer and irrespective of result start bash
+ENTRYPOINT make test; bash

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadabc31/proton-c/src/tests/fuzz/README.md
----------------------------------------------------------------------
diff --git a/proton-c/src/tests/fuzz/README.md b/proton-c/src/tests/fuzz/README.md
new file mode 100644
index 0000000..08ddd06
--- /dev/null
+++ b/proton-c/src/tests/fuzz/README.md
@@ -0,0 +1,68 @@
+# Fuzz testing for qpid-proton-c
+
+## Dockerfile
+
+Easiest way to build and run the fuzzing is using attached Dockerfile. Run the following command from the top directory of the project
+
+    docker build -f proton-c/src/tests/fuzz/Dockerfile -t qpid-proton-fuzz .
+ 
+Then run the built image and execute a fuzzer to check that all works
+
+    docker run --cap-add SYS_PTRACE -it qpid-proton-fuzz
+    ./fuzz-url /src/proton-c/src/tests/fuzz/fuzz-url/crash /src/proton-c/src/tests/fuzz/fuzz-url/corpus
+    
+You can bind a local directory to the container with the `-v local:remote` option. The `--rm` option is also useful. See https://docs.docker.com/engine/reference/run/.
+
+The docker image is based on `ossfuzz/basebuilder`, which is Ubuntu 16.04 Xenial with clang 5.0 and libc++.
+
+## Building
+
+There are two cmake options to control compilation of fuzzers
+
+* `FUZZ_TEST` (default: `OFF`) adds fuzzers to the build and to regression tests
+* `FUZZING_ENGINE` (default: `OFF`) links fuzzers with `libFuzzingEngine` when `ON`
+
+When `FUZZING_ENGINE` is `OFF`, fuzzers are linked with a simple driver suitable only for regression testing.
+
+### with a simple driver
+
+There are no special prerequisites and no extra configuration is necessary.
+
+### with libFuzzer
+
+1. Download and compile libFuzzer. Use http://llvm.org/docs/LibFuzzer.html for detailed instructions.
+2. Rename/link `libFuzzer.a` (from previous step) to `libFuzzingEngine.a`
+3. Build qpid-proton with the following configuration
+  * set `CC` and `CXX` variables to the same compiler you used to build libFuzzer (some recent clang)
+  * set `CFLAGS` and `CXXFLAGS` with the coverage and sanitizer(s) you want to use, see libFuzzer documentation for details
+  * set `LDFLAGS` to add the directory with `libFuzzingEngine.a` to your link path if necessary
+  * set `FUZZ_TEST=ON` and `FUZZING_ENGINE=ON`
+
+For example:
+
+    FLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls"
+    
+    CC=~/third_party/llvm-build/Release+Asserts/bin/clang \
+    CXX=~/third_party/llvm-build/Release+Asserts/bin/clang++ \
+    CFLAGS="$FLAGS" \
+    CXXFLAGS="$FLAGS" \
+    LDFLAGS="-L/path/to/libFuzzingEngine/directory" \
+    cmake .. -DCMAKE_BUILD_TYPE=Debug -DFUZZ_TEST=ON -DFUZZING_ENGINE=ON
+
+## Running
+
+Execute one of the `fuzz-*` binaries. 
+
+### With simple driver
+
+When given file paths as command line arguments, it will run the fuzzed function with each of the inputs in turn once and then exit.
+
+### WIth libFuzzer
+
+When given a folder as first argument, it will load corpus from the folder and also store newly discovered inputs (that extend code coverage) there. See http://llvm.org/docs/LibFuzzer.html for details.
+
+# Justifications
+
+The reason for renaming the fuzzing library to `libFuzzingEngine` is to be compatible with https://github.com/google/oss-fuzz.
+
+In fuzzing data directories, corpus and crashes are two different subdirectories, because fuzzers can perform corpus minimization. Crashes should not be subjected to corpus minimization, so they need to be kept separately.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadabc31/proton-c/src/tests/fuzz/fuzz-connection-driver.c
----------------------------------------------------------------------
diff --git a/proton-c/src/tests/fuzz/fuzz-connection-driver.c b/proton-c/src/tests/fuzz/fuzz-connection-driver.c
new file mode 100644
index 0000000..dcc5757
--- /dev/null
+++ b/proton-c/src/tests/fuzz/fuzz-connection-driver.c
@@ -0,0 +1,222 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "proton/connection_driver.h"
+#include "proton/engine.h"
+#include "proton/log.h"
+#include "proton/message.h"
+
+#include "libFuzzingEngine.h"
+
+// This fuzzer is a variant of the receive.c proactor example
+
+#define MAX_SIZE 1024
+
+typedef char str[MAX_SIZE];
+
+typedef struct app_data_t {
+  str container_id;
+  pn_rwbytes_t message_buffer;
+  int message_count;
+  int received;
+} app_data_t;
+
+static void fdc_write(pn_connection_driver_t *driver);
+size_t fcd_read(pn_connection_driver_t *driver, uint8_t **data, size_t *size);
+static void decode_message(pn_delivery_t *dlv);
+static void handle(app_data_t *app, pn_event_t *event);
+static void check_condition(pn_event_t *e, pn_condition_t *cond);
+
+// const bool VERBOSE = true;
+const bool VERBOSE = false;
+// const bool ERRORS = true;
+const bool ERRORS = false;
+
+// I could not get rid of the error messages on stderr in any other way
+void devnull(pn_transport_t *transport, const char *message) {}
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+  if (VERBOSE)
+    printf("BEGIN LLVMFuzzerTestOneInput\n");
+  app_data_t app = {{0}};
+  snprintf(app.container_id, sizeof(app.container_id), "%s:%06x",
+           "fuzz_connection_driver", rand() & 0xffffff);
+
+  pn_connection_driver_t driver;
+  if (pn_connection_driver_init(&driver, NULL, NULL) != 0) {
+    printf("pn_connection_driver_init\n");
+    exit(1);
+  }
+
+  pn_log_enable(false);
+  pn_log_logger(NULL);
+  pn_transport_trace(driver.transport, PN_TRACE_OFF);
+  pn_transport_set_tracer(driver.transport, devnull);
+
+  uint8_t *data = (uint8_t *)Data;
+  size_t size = Size;
+
+  fdc_write(&driver);
+
+  pn_event_t *event;
+  while ((event = pn_connection_driver_next_event(&driver)) != NULL) {
+    handle(&app, event);
+  }
+
+  fdc_write(&driver);
+
+  do {
+    fdc_write(&driver);
+    fcd_read(&driver, &data, &size);
+    if (VERBOSE)
+      printf("size is %d, data is %p\n", (int)size, (void *)data);
+    pn_event_t *event;
+    while ((event = pn_connection_driver_next_event(&driver)) != NULL) {
+      handle(&app, event);
+    }
+  } while (size > 0);
+
+  pn_connection_driver_close(&driver);
+  pn_connection_driver_destroy(
+      &driver); // TODO: documentation says pn_connection_driver_free
+  if (VERBOSE)
+    printf("END LLVMFuzzerTestOneInput\n");
+  return 0;
+}
+
+static void handle(app_data_t *app, pn_event_t *event) {
+  switch (pn_event_type(event)) {
+
+  case PN_CONNECTION_INIT: {
+    pn_connection_t *c = pn_event_connection(event);
+    pn_connection_set_container(c, app->container_id);
+    pn_connection_open(c);
+    pn_session_t *s = pn_session(c);
+    pn_session_open(s);
+    pn_link_t *l = pn_receiver(s, "my_receiver");
+    pn_terminus_set_address(pn_link_source(l), NULL);
+    pn_link_open(l);
+    pn_link_flow(l, 20);
+  } break;
+
+  case PN_DELIVERY: {
+    /* A message has been received */
+    pn_link_t *link = NULL;
+    pn_delivery_t *dlv = pn_event_delivery(event);
+    if (pn_delivery_readable(dlv) && !pn_delivery_partial(dlv)) {
+      link = pn_delivery_link(dlv);
+      decode_message(dlv);
+      /* Accept the delivery */
+      pn_delivery_update(dlv, PN_ACCEPTED);
+      /* done with the delivery, move to the next and free it */
+      pn_link_advance(link);
+      pn_delivery_settle(dlv); /* dlv is now freed */
+    }
+  } break;
+
+  case PN_TRANSPORT_ERROR:
+    check_condition(event, pn_transport_condition(pn_event_transport(event)));
+    pn_connection_close(pn_event_connection(event));
+    break;
+
+  case PN_CONNECTION_REMOTE_CLOSE:
+    check_condition(event,
+                    pn_connection_remote_condition(pn_event_connection(event)));
+    pn_connection_close(pn_event_connection(event));
+    break;
+
+  case PN_SESSION_REMOTE_CLOSE:
+    check_condition(event,
+                    pn_session_remote_condition(pn_event_session(event)));
+    pn_connection_close(pn_event_connection(event));
+    break;
+
+  case PN_LINK_REMOTE_CLOSE:
+  case PN_LINK_REMOTE_DETACH:
+    check_condition(event, pn_link_remote_condition(pn_event_link(event)));
+    pn_connection_close(pn_event_connection(event));
+    break;
+
+  default:
+    break;
+  }
+}
+
+static void check_condition(pn_event_t *e, pn_condition_t *cond) {
+  if (VERBOSE)
+    printf("beginning check_condition\n");
+  if (pn_condition_is_set(cond)) {
+    if (VERBOSE || ERRORS)
+      fprintf(stderr, "%s: %s: %s\n", pn_event_type_name(pn_event_type(e)),
+              pn_condition_get_name(cond), pn_condition_get_description(cond));
+  }
+}
+
+static void decode_message(pn_delivery_t *dlv) {
+  static char buffer[MAX_SIZE];
+  ssize_t len;
+  // try to decode the message body
+  if (pn_delivery_pending(dlv) < MAX_SIZE) {
+    // read in the raw data
+    len = pn_link_recv(pn_delivery_link(dlv), buffer, MAX_SIZE);
+    if (len > 0) {
+      // decode it into a proton message
+      pn_message_t *m = pn_message();
+      if (PN_OK == pn_message_decode(m, buffer, len)) {
+        pn_string_t *s = pn_string(NULL);
+        pn_inspect(pn_message_body(m), s);
+        if (ERRORS)
+          printf("%s\n", pn_string_get(s));
+        pn_free(s);
+      }
+      pn_message_free(m);
+    }
+  }
+}
+
+// reads up to `size` bytes from `data`,
+// updates `data` pointer and `size` to the unread portion of original `data`,
+// returns new value of `size`
+size_t fcd_read(pn_connection_driver_t *driver, uint8_t **data, size_t *size) {
+  pn_rwbytes_t buf = pn_connection_driver_read_buffer(driver);
+  size_t s = (*size < buf.size) ? *size : buf.size;
+  if (buf.start == NULL) {
+    exit(1);
+  }
+  memcpy(buf.start, *data, s);
+
+  pn_connection_driver_read_done(driver, s);
+  *data += s;
+  *size -= s;
+
+  return *size;
+}
+
+// drops the data in the buffer and reports them as written
+static void fdc_write(pn_connection_driver_t *driver) {
+  pn_bytes_t buffer = pn_connection_driver_write_buffer(driver);
+  pn_connection_driver_write_done(driver, buffer.size);
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadabc31/proton-c/src/tests/fuzz/fuzz-message-decode.c
----------------------------------------------------------------------
diff --git a/proton-c/src/tests/fuzz/fuzz-message-decode.c b/proton-c/src/tests/fuzz/fuzz-message-decode.c
new file mode 100644
index 0000000..30b9076
--- /dev/null
+++ b/proton-c/src/tests/fuzz/fuzz-message-decode.c
@@ -0,0 +1,42 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 <stdint.h>
+
+#include "proton/message.h"
+
+#include "libFuzzingEngine.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+  if (Size < 1) {
+    // pn_message_decode would die on assert
+    return 0;
+  }
+  pn_message_t *msg = pn_message();
+  int ret = pn_message_decode(msg, (const char *)Data, Size);
+  if (ret == 0) {
+    // FUTURE: do something like encode msg and compare again with Data
+  }
+  if (msg != NULL) {
+    pn_message_free(msg);
+  }
+  return 0;
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadabc31/proton-c/src/tests/fuzz/fuzz-proactor-receive.c
----------------------------------------------------------------------
diff --git a/proton-c/src/tests/fuzz/fuzz-proactor-receive.c b/proton-c/src/tests/fuzz/fuzz-proactor-receive.c
new file mode 100644
index 0000000..499c439
--- /dev/null
+++ b/proton-c/src/tests/fuzz/fuzz-proactor-receive.c
@@ -0,0 +1,362 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ *
+ */
+
+#define _POSIX_C_SOURCE 200809L
+
+#include <proton/connection.h>
+#include <proton/connection_driver.h>
+#include <proton/delivery.h>
+#include <proton/link.h>
+#include <proton/message.h>
+#include <proton/proactor.h>
+#include <proton/session.h>
+#include <proton/transport.h>
+#include <proton/url.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <errno.h>
+#include <strings.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include "libFuzzingEngine.h"
+
+// bool VERBOSE = true;
+bool VERBOSE = false;
+// bool ERRORS = true;
+bool ERRORS = false;
+
+#define MAX_SIZE 1024
+
+typedef char str[MAX_SIZE];
+
+typedef struct app_data_t {
+  str address;
+  str container_id;
+  pn_rwbytes_t message_buffer;
+  int message_count;
+  int received;
+  pn_proactor_t *proactor;
+  bool finished;
+} app_data_t;
+
+static const int BATCH = 1000; /* Batch size for unlimited receive */
+
+static int exit_code = 0;
+
+static void check_condition(pn_event_t *e, pn_condition_t *cond) {
+  if (VERBOSE)
+    printf("beginning check_condition\n");
+  if (pn_condition_is_set(cond)) {
+    exit_code = 1;
+    if (VERBOSE || ERRORS)
+      fprintf(stderr, "%s: %s: %s\n", pn_event_type_name(pn_event_type(e)),
+              pn_condition_get_name(cond), pn_condition_get_description(cond));
+  }
+}
+
+static void decode_message(pn_delivery_t *dlv) {
+  static char buffer[MAX_SIZE];
+  ssize_t len;
+  // try to decode the message body
+  if (pn_delivery_pending(dlv) < MAX_SIZE) {
+    // read in the raw data
+    len = pn_link_recv(pn_delivery_link(dlv), buffer, MAX_SIZE);
+    if (len > 0) {
+      // decode it into a proton message
+      pn_message_t *m = pn_message();
+      if (PN_OK == pn_message_decode(m, buffer, len)) {
+        pn_string_t *s = pn_string(NULL);
+        pn_inspect(pn_message_body(m), s);
+        printf("%s\n", pn_string_get(s));
+        pn_free(s);
+      }
+      pn_message_free(m);
+    }
+  }
+}
+
+static void handle(app_data_t *app, pn_event_t *event) {
+  switch (pn_event_type(event)) {
+
+  case PN_CONNECTION_INIT: {
+    pn_connection_t *c = pn_event_connection(event);
+    pn_connection_set_container(c, app->container_id);
+    pn_connection_open(c);
+    pn_session_t *s = pn_session(c);
+    pn_session_open(s);
+    pn_link_t *l = pn_receiver(s, "my_receiver");
+    pn_terminus_set_address(pn_link_source(l), app->address);
+    pn_link_open(l);
+    /* cannot receive without granting credit: */
+    pn_link_flow(l, app->message_count ? app->message_count : BATCH);
+  } break;
+
+  case PN_DELIVERY: {
+    /* A message has been received */
+    pn_link_t *link = NULL;
+    pn_delivery_t *dlv = pn_event_delivery(event);
+    if (pn_delivery_readable(dlv) && !pn_delivery_partial(dlv)) {
+      link = pn_delivery_link(dlv);
+      decode_message(dlv);
+      /* Accept the delivery */
+      pn_delivery_update(dlv, PN_ACCEPTED);
+      /* done with the delivery, move to the next and free it */
+      pn_link_advance(link);
+      pn_delivery_settle(dlv); /* dlv is now freed */
+
+      if (app->message_count == 0) {
+        /* receive forever - see if more credit is needed */
+        if (pn_link_credit(link) < BATCH / 2) {
+          /* Grant enough credit to bring it up to BATCH: */
+          pn_link_flow(link, BATCH - pn_link_credit(link));
+        }
+      } else if (++app->received >= app->message_count) {
+        /* done receiving, close the endpoints */
+        printf("%d messages received\n", app->received);
+        pn_session_t *ssn = pn_link_session(link);
+        pn_link_close(link);
+        pn_session_close(ssn);
+        pn_connection_close(pn_session_connection(ssn));
+      }
+    }
+  } break;
+
+  case PN_TRANSPORT_ERROR:
+    check_condition(event, pn_transport_condition(pn_event_transport(event)));
+    pn_connection_close(pn_event_connection(event));
+    break;
+
+  case PN_CONNECTION_REMOTE_CLOSE:
+    check_condition(event,
+                    pn_connection_remote_condition(pn_event_connection(event)));
+    pn_connection_close(pn_event_connection(event));
+    break;
+
+  case PN_SESSION_REMOTE_CLOSE:
+    check_condition(event,
+                    pn_session_remote_condition(pn_event_session(event)));
+    pn_connection_close(pn_event_connection(event));
+    break;
+
+  case PN_LINK_REMOTE_CLOSE:
+  case PN_LINK_REMOTE_DETACH:
+    check_condition(event, pn_link_remote_condition(pn_event_link(event)));
+    pn_connection_close(pn_event_connection(event));
+    break;
+
+  case PN_PROACTOR_INACTIVE:
+    app->finished = true;
+    break;
+
+  default:
+    break;
+  }
+}
+
+double now(void) {
+  struct timespec spec;
+  if (clock_gettime(CLOCK_MONOTONIC, &spec) != 0) {
+    perror("clock_gettime");
+    exit(errno);
+  }
+  return (double)spec.tv_sec + (double)spec.tv_nsec / 1000000.0;
+}
+
+int sut(void) {
+  /* Default values for application and connection. */
+  app_data_t app = {{0}};
+  app.message_count = 2;
+
+  snprintf(app.container_id, sizeof(app.container_id), "%s:%d",
+           "fuzz_proactor_recv", getpid());
+
+  const char *address = "127.0.0.1:amqp";
+  strncpy(app.address, "jms.queue.example", sizeof(app.address));
+
+  if (VERBOSE)
+    printf("before proactor\n");
+  /* Create the proactor and connect */
+  app.proactor = pn_proactor();
+  pn_proactor_connect(app.proactor, pn_connection(), address);
+
+  if (VERBOSE)
+    printf("before loop\n");
+  double thence = now();
+  do {
+    if (VERBOSE)
+      printf("before set proactor timeout\n");
+    pn_proactor_set_timeout(app.proactor, 100);
+    if (VERBOSE)
+      printf("before proactor wait\n");
+    pn_event_batch_t *events = pn_proactor_wait(app.proactor);
+    pn_event_t *e;
+    if (VERBOSE)
+      printf("before proactor next batch\n");
+    while ((e = pn_event_batch_next(events))) {
+      handle(&app, e);
+    }
+    pn_proactor_done(app.proactor, events);
+
+    if (VERBOSE)
+      printf("before reloop\n");
+    double deltat = now() - thence;
+    if (VERBOSE)
+      printf("deltat %f", deltat);
+    if (deltat > 1) {
+      app.finished = true;
+    }
+  } while (!app.finished);
+
+  if (VERBOSE)
+    printf("after loop\n");
+  pn_proactor_free(app.proactor);
+  free(app.message_buffer.start);
+  return exit_code;
+}
+
+void serve_data(const uint8_t *Data, size_t Size) {
+  int sockfd;
+  if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+    perror("socket");
+    _Exit(errno);
+  }
+  int reuseaddr = 1;
+  if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
+                 sizeof(reuseaddr)) == -1) {
+    perror("setsockopt");
+    _Exit(errno);
+  }
+  struct sockaddr_in self;
+  memset(&self, 0, sizeof(self));
+  self.sin_family = AF_INET;
+  self.sin_port = htons(5672);
+  self.sin_addr.s_addr = INADDR_ANY;
+  if (bind(sockfd, (struct sockaddr *)&self, sizeof(self)) != 0) {
+    perror("bind");
+
+    // Lets unblock the old child that listens by starting new client to read
+    // from it. It breaks the fuzzing somewhat, but it is better to mess up one
+    // than many inputs.
+    if (VERBOSE)
+      printf("unblocking old bound child\n");
+    kill(getppid(), SIGUSR1);
+
+    _Exit(errno);
+  }
+  if (VERBOSE)
+    printf("bound\n");
+  if (listen(sockfd, 1) != 0) {
+    perror("listen");
+    _Exit(errno);
+  }
+
+  if (VERBOSE)
+    printf("listened, lets run sut\n");
+  kill(getppid(), SIGUSR1);
+
+  struct sockaddr_in client_addr;
+  socklen_t addrlen = sizeof(client_addr);
+  int clientfd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen);
+  if (VERBOSE)
+    printf("%s:%d connected\n", inet_ntoa(client_addr.sin_addr),
+           ntohs(client_addr.sin_port));
+  if (VERBOSE)
+    printf("will send\n");
+  send(clientfd, Data, Size, 0);
+  //     sleep(1);
+  close(clientfd);
+  close(sockfd);
+  if (VERBOSE)
+    printf("done serving\n");
+}
+
+void run_sut(int s) {
+  if (VERBOSE)
+    printf("running sut\n");
+  sut();
+  if (VERBOSE)
+    printf("finished running sut\n");
+}
+
+void signal_callback_handler(int signum) {
+  if (VERBOSE)
+    printf("Caught signal SIGPIPE %d\n", signum);
+}
+
+bool DoInitialization(void) {
+  struct sigaction sa;
+  sa.sa_handler = run_sut;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = SA_RESTART; /* Restart functions if interrupted by handler */
+  if (sigaction(SIGUSR1, &sa, NULL) == -1) {
+    perror("sigaction");
+    exit(2);
+  }
+  sa.sa_handler = signal_callback_handler;
+  if (sigaction(SIGPIPE, &sa, NULL) == -1) {
+    perror("sigaction");
+    exit(2);
+  }
+  return true;
+}
+
+int LLVMFuzzerInitialize(int *argc, char ***argv) {
+  DoInitialization();
+  return 0;
+}
+
+int prev_pid = 0;
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+  // sometimes, esp. with AFL, but libFuzz too,
+  // the old socket is still bound for new run and
+  // it skips all new runs...
+  if (prev_pid != 0) {
+    kill(SIGKILL, prev_pid);
+  }
+
+  pid_t pid = fork();
+  if (pid < 0) {
+    perror("fork");
+    exit(errno);
+  }
+  if (pid == 0) { // child
+    serve_data(Data, Size);
+    _Exit(0);
+  } else { // parent
+    prev_pid = pid;
+    if (VERBOSE)
+      printf("waiting for child\n");
+    siginfo_t status;
+    waitid(P_PID, pid, &status, WEXITED);
+    if (VERBOSE)
+      printf("finished waiting for child\n");
+  }
+  return 0;
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadabc31/proton-c/src/tests/fuzz/fuzz-sniff-header.c
----------------------------------------------------------------------
diff --git a/proton-c/src/tests/fuzz/fuzz-sniff-header.c b/proton-c/src/tests/fuzz/fuzz-sniff-header.c
new file mode 100644
index 0000000..dfc874e
--- /dev/null
+++ b/proton-c/src/tests/fuzz/fuzz-sniff-header.c
@@ -0,0 +1,33 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 <stdio.h>
+#include <stdlib.h>
+
+#include "core/autodetect.h"
+
+#include "libFuzzingEngine.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+  /* pni_protocol_type_t protocol = */ pni_sniff_header((const char *)Data,
+                                                        Size);
+  return 0;
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadabc31/proton-c/src/tests/fuzz/fuzz-url.c
----------------------------------------------------------------------
diff --git a/proton-c/src/tests/fuzz/fuzz-url.c b/proton-c/src/tests/fuzz/fuzz-url.c
new file mode 100644
index 0000000..71dafd5
--- /dev/null
+++ b/proton-c/src/tests/fuzz/fuzz-url.c
@@ -0,0 +1,43 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "proton/url.h"
+
+#include "libFuzzingEngine.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+  // null-terminate the string in Data in case it doesn't contain null already
+  char *str = (char *)malloc(Size + 1);
+  memcpy(str, Data, Size);
+  str[Size] = '\0';
+
+  pn_url_t *url = pn_url_parse(str);
+  if (url != NULL) {
+    pn_url_free(url);
+  }
+
+  free(str);
+  return 0;
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/aadabc31/proton-c/src/tests/fuzz/libFuzzingEngine.h
----------------------------------------------------------------------
diff --git a/proton-c/src/tests/fuzz/libFuzzingEngine.h b/proton-c/src/tests/fuzz/libFuzzingEngine.h
new file mode 100644
index 0000000..c2877d1
--- /dev/null
+++ b/proton-c/src/tests/fuzz/libFuzzingEngine.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ *
+ */
+
+#ifndef _LIB_FUZZING_ENGINE_H_
+#define _LIB_FUZZING_ENGINE_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+
+// This header defines `extern "C"` for the fuzzing interface functions
+
+#ifdef __cplusplus
+extern "C"
+#endif
+int LLVMFuzzerInitialize(int *argc, char ***argv);
+
+#ifdef __cplusplus
+extern "C"
+#endif
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
+
+#endif


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org