Browse Source

Add fuzz testing for control protocol based on LLVM libFuzzer.

Davin McCall 5 years ago
parent
commit
8b2f89ca02
3 changed files with 88 additions and 1 deletions
  1. 2 0
      .gitignore
  2. 18 1
      src/tests/cptests/Makefile
  3. 68 0
      src/tests/cptests/fuzz.cc

+ 2 - 0
.gitignore

@@ -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

+ 18 - 1
src/tests/cptests/Makefile

@@ -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)

+ 68 - 0
src/tests/cptests/fuzz.cc

@@ -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;
+}
+*/