Add fuzz testing for control protocol based on LLVM libFuzzer.
authorDavin McCall <davmac@davmac.org>
Sun, 8 Jul 2018 21:12:15 +0000 (22:12 +0100)
committerDavin McCall <davmac@davmac.org>
Mon, 9 Jul 2018 21:00:49 +0000 (22:00 +0100)
.gitignore
src/tests/cptests/Makefile
src/tests/cptests/fuzz.cc [new file with mode: 0644]

index 725676e212a328f2562355cc912466c1777530f3..b092140c44dfc7fe3949a79a3282e5d4cc359e3c 100644 (file)
@@ -12,3 +12,5 @@ src/tests/loadtests
 src/tests/includes
 src/tests/cptests/includes
 src/tests/cptests/cptests
+src/tests/cptests/corpus
+src/tests/cptests/fuzz
index a4a1be1dffdda6338595c47689ff6d9540ac9f28..8e48a8ead759aeec0877680223eb857cea0254ca 100644 (file)
@@ -10,7 +10,7 @@ build-tests: prepare-incdir cptests
 
 run-tests: cptests
        ./cptests
-
+       
 # Create an "includes" directory populated with a combination of real and mock headers:
 prepare-incdir:
        mkdir -p includes
@@ -31,5 +31,22 @@ $(parent_objs): %.o: ../../%.cc
 clean:
        rm -f *.o *.d cptests
 
+
+# Experimental LLVM-libFuzzer based fuzzer. "make fuzz" to build; "fuzz corpus" to run (and store
+# interesting test data in "corpus" directory).
+
+fuzz_parent_test_objects = $(foreach obj,$(notdir $(parent_test_objects)),fuzz-$(obj))
+fuzz_objects = $(foreach obj,$(parent_objs),fuzz-$(obj))
+
+fuzz: fuzz.cc $(fuzz_parent_test_objects) $(fuzz_objects)
+       clang++ -std=c++11 -g -O1 -Iincludes -I../../dasynq -fsanitize=fuzzer,address,undefined,leak fuzz.cc $(fuzz_parent_test_objects) $(fuzz_objects) -o fuzz
+
+$(fuzz_parent_test_objects): fuzz-%.o: ../%.cc
+       clang -O1 -fsanitize=address,undefined,fuzzer-no-link,leak -MMD -MP -I../includes -I../../dasynq -c $< -o $@
+
+$(fuzz_objects): fuzz-%.o: ../../%.cc
+       clang -O1 -fsanitize=address,undefined,fuzzer-no-link,leak -MMD -MP -Iincludes -I../../dasynq -c $< -o $@
+
+
 -include $(objects:.o=.d)
 -include $(parent_objects:.o=.d)
diff --git a/src/tests/cptests/fuzz.cc b/src/tests/cptests/fuzz.cc
new file mode 100644 (file)
index 0000000..df197da
--- /dev/null
@@ -0,0 +1,68 @@
+#include <cassert>
+#include <iostream>
+#include <vector>
+#include <string>
+#include <set>
+
+#include "dinit.h"
+#include "service.h"
+#include "baseproc-sys.h"
+#include "control.h"
+
+// Control protocol fuzzing.
+
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
+{
+       if (Size == 0) return 0;
+
+       service_set sset;
+
+       service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
+       sset.add_service(s1);
+       service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {});
+       sset.add_service(s2);
+       service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {});
+       sset.add_service(s3);
+
+       int fd = bp_sys::allocfd();
+       auto *cc = new control_conn_t(event_loop, &sset, fd);
+
+       std::vector<char> input_data(Data, Data + Size);
+       bp_sys::supply_read_data(fd, std::move(input_data));
+
+       event_loop.regd_bidi_watchers[fd]->read_ready(event_loop, fd);
+
+       // Write will process immediately, so there's no need for this:
+       //event_loop.regd_bidi_watchers[fd]->write_ready(event_loop, fd);
+
+       // We expect, for each service:
+       // (1 byte)   DINIT_RP_SVCINFO
+       // (1 byte)   service name length
+       // (1 byte)   state
+       // (1 byte)   target state
+       // (1 byte)   flags: has console, waiting for console, start skipped
+       // (1 byte)   stop reason
+    // (2 bytes)  reserved
+       // (? bytes)  exit status (int) / process id (pid_t)
+       // (N bytes)  service name
+
+       delete cc;
+
+       return 0;
+}
+
+/*
+
+#define RUN_TEST(name, spacing) \
+    std::cout << #name "..." spacing; \
+    name(); \
+    std::cout << "PASSED" << std::endl;
+
+int main(int argc, char **argv)
+{
+    RUN_TEST(cptest_queryver, "    ");
+    RUN_TEST(cptest_listservices, "");
+    return 0;
+}
+*/