fuzzing NEW refactor harness to enable LLVM LibFuzzer and more granular fuzzing

Refactor the harness to call LLVMFuzzerTestOneInput when using
AFL, and simultaneously enable standalone fuzzing with LibFuzzer,
which disables the main function of the harness, and uses
LLVMFuzzerTestOneInput directly.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1e87269..d80ef4e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -418,6 +418,13 @@
 endif(ENABLE_BUILD_TESTS)
 
 if(ENABLE_FUZZ_TARGETS)
+	set(FUZZER "AFL" CACHE STRING "fuzzer type")
+	if(FUZZER STREQUAL "LibFuzzer")
+		if (NOT CMAKE_C_COMPILER_ID STREQUAL "Clang")
+			message(FATAL_ERROR "LibFuzzer works only with clang")
+		endif()
+		set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address,undefined")
+	endif()
 	add_subdirectory(tests/fuzz)
 endif(ENABLE_FUZZ_TARGETS)
 
diff --git a/README.md b/README.md
index de19376..011aad9 100644
--- a/README.md
+++ b/README.md
@@ -258,6 +258,11 @@
  
 ## Fuzzing
 
-A YANG fuzzing target and fuzzing instructions are available in the `tests/fuzz` directory.
+Multiple YANG fuzzing targets and fuzzing instructions are available in the
+`tests/fuzz` directory.
 
-An asciinema example describing the process of fuzzing libyang2 with the yangfuzz fuzz harness is available at https://asciinema.org/a/260417.
+All of the targets can be fuzzed with LLVM's LibFuzzer and AFL, and new targets
+can easily be added.
+Asciinema examples which describe the fuzzing setup for both AFL (https://asciinema.org/a/311060) 
+and LibFuzzer (https://asciinema.org/a/311035) are available.
+
diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt
index 8ed9c90..1e8cf7b 100644
--- a/tests/fuzz/CMakeLists.txt
+++ b/tests/fuzz/CMakeLists.txt
@@ -1,8 +1,17 @@
 cmake_minimum_required(VERSION 2.8.12)
 
-set(fuzz_targets yangfuzz)
+set(fuzz_targets lys_parse_mem buf_add_char yang_parse_module)
 
-foreach(target_name IN LISTS fuzz_targets)
-    add_executable(${target_name} ${target_name}.c)
-    target_link_libraries(${target_name} yang)
-endforeach(target_name)
+if(FUZZER STREQUAL "AFL")
+	foreach(target_name IN LISTS fuzz_targets)
+	    add_executable(${target_name} ${target_name}.c main.c $<TARGET_OBJECTS:yangobj>)
+	    target_link_libraries(${target_name} yang)
+	    target_link_libraries(${target_name} ${CMAKE_THREADS_LIB_INIT})
+	endforeach(target_name)
+elseif(FUZZER STREQUAL "LibFuzzer")
+	foreach(target_name IN LISTS fuzz_targets)
+		add_executable(${target_name} ${target_name}.c $<TARGET_OBJECTS:yangobj>)
+		set_source_files_properties(${target_name}.c PROPERTIES COMPILE_FLAGS "-fsanitize=fuzzer")
+	    	target_link_libraries(${target_name} yang "-fsanitize=fuzzer")
+	endforeach(target_name)
+endif()
diff --git a/tests/fuzz/README.md b/tests/fuzz/README.md
index e07193f..e6641a1 100644
--- a/tests/fuzz/README.md
+++ b/tests/fuzz/README.md
@@ -1,13 +1,24 @@
 # FUZZING
-yangfuzz, a simple YANG fuzz harness is available in this directory and
-is designed to be used with the [AFL](http://lcamtuf.coredump.cx/afl/) fuzzer.
+This directory contains a collection of fuzz harnesses, which are designed to
+be used with [AFL](http://lcamtuf.coredump.cx/afl/) and [LibFuzzer](https://llvm.org/docs/LibFuzzer.html)
+fuzzers. The harnesses should also be easily reusable with other similar fuzzers.
 
-To build the fuzz target, the ENABLE_FUZZ_TARGETS option has to be enabled.
+Two asciinema examples are available, one for LibFuzzer:
+https://asciinema.org/a/311035
+and one for AFL:
+https://asciinema.org/a/311060
 
-To add AFL instrumentation when compiling libyang, the AFL clang-fast compiler
-should be used with the following cmake option:
+To build the fuzz targets, the ENABLE_FUZZ_TARGETS option has to be enabled.
+The FUZZER option specifies which fuzzer to use, currently only AFL and LibFuzzer
+are supported, with AFL being the default. LibFuzzer is based on the same 
+principles that AFL works with, but has a different implementation of the fuzzing engine
+and is integrated with UBSAN by default, while AFL lacks official integration with UBSAN.
 
-It is reccomended to set the build type to Release, since otherwise the fuzzer will detect failed asserts as crashes.
+To use the harnesses with AFL, one of AFL's compilers should be used.
+For example the AFL clang-fast compiler can be used with the cmake option shown below.
+It is recommended to set the build type to Release, since otherwise the fuzzer will
+detect failed asserts as crashes.
+If LibFuzzer is used, clang has to be used, as gcc doesn't support -fsanitize=fuzzer.
 
 ```
 $ cmake -DENABLE_FUZZ_TARGETS=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=path_to_afl/afl-clang-fast ..
@@ -18,13 +29,14 @@
 $ make
 ```
 
-The yangfuzz executable will then be available in the root of the build directory that was used.
+The target executables will then be available in tests/fuzz of the build directory that was used.
 
-The libyang yang test files available in the `tests/data/files` subdirectory can be used as initial
-test cases for fuzzing the programs with AFL.
+The libyang yang test files available in the `tests/modules/` subdirectory can be used as initial
+test cases for fuzzing the targets. However, a smaller corpus of YANG models should probably
+be used, as larger models decrease execution speed. A good place to start would be to collect
+small YANG files, each of which uses only a single YANG feature.
 
 The files that will be used as starting test cases should be copied into a single directory. Those files should then be minimized by using afl-cmin and afl-tmin.
-Also, AFL will issue a warning about a decrease of performance when working with large files, so only smaller test cases should be used.
 
 To increase the speed of fuzzing, the test cases and AFL output files should be stored on a temporary RAM disk.
 If a new fuzz target is used, AFL persistent mode should be used. More about persistent mode can be read in the official AFL documentation.
@@ -38,3 +50,10 @@
 afl-fuzz -i minimised_testcases/ -o syncdir/ -M fuzzer1 -- libyang/build/fuzz/yangfuzz @@
 afl-fuzz -i minimised_testcases/ -o syncdir/ -S fuzzer2 -- libyang/build/fuzz/yangfuzz @@
 ```
+
+To fuzz with LibFuzzer, at the most basic level, everything that is required is
+to run the compiled fuzz target.
+However, running the target like that invokes the harness with only one job
+on a single core, with no starting inputs. 
+Multiple jobs running on separate cores should be used, with a starting input corpus.
+The options are described in the official LibFuzzer documentation (https://llvm.org/docs/LibFuzzer.html).
diff --git a/tests/fuzz/buf_add_char.c b/tests/fuzz/buf_add_char.c
new file mode 100644
index 0000000..a8b2a77
--- /dev/null
+++ b/tests/fuzz/buf_add_char.c
@@ -0,0 +1,52 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "libyang.h"
+
+LY_ERR buf_add_char(struct ly_ctx *ctx, const char **input, size_t len, char **buf, size_t *buf_len, size_t *buf_used);
+
+int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
+{
+	struct ly_ctx *ctx = NULL;
+	LY_ERR err;
+	uint8_t *data = NULL;
+	uint8_t *old_data = NULL;
+	uint8_t *dest = NULL;
+	size_t dest_len = 0;
+	size_t used = 0;
+	static bool log = false;
+
+	if (!log) {
+		ly_log_options(0);
+		log = true;
+	}
+
+	err = ly_ctx_new(NULL, 0, &ctx);
+	if (err != LY_SUCCESS) {
+		fprintf(stderr, "Failed to create context\n");
+		return 0;
+	}
+
+	data = malloc(len);
+	if (data == NULL) {
+		return 0;
+	}
+
+	dest = malloc(len);
+	if (dest == NULL) {
+		return 0;
+	}
+	dest_len = len;
+
+	memcpy(data, buf, len);
+	old_data = data;
+	err = buf_add_char(ctx, &data, len, &dest, &dest_len, &used);
+
+	free(old_data);
+	free(dest);
+	ly_ctx_destroy(ctx, NULL);
+
+	return 0;
+}
diff --git a/tests/fuzz/lys_parse_mem.c b/tests/fuzz/lys_parse_mem.c
new file mode 100644
index 0000000..6ab5e0b
--- /dev/null
+++ b/tests/fuzz/lys_parse_mem.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "libyang.h"
+
+int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
+{
+	struct ly_ctx *ctx = NULL;
+	static bool log = false;
+	LY_ERR err;
+
+	if (!log) {
+		ly_log_options(0);
+		log = true;
+	}
+
+	err = ly_ctx_new(NULL, 0, &ctx);
+	if (err != LY_SUCCESS) {
+		fprintf(stderr, "Failed to create context\n");
+		exit(EXIT_FAILURE);
+	}
+
+	lys_parse_mem(ctx, buf, LYS_IN_YANG);
+	ly_ctx_destroy(ctx, NULL);
+	return 0;
+}
diff --git a/tests/fuzz/main.c b/tests/fuzz/main.c
new file mode 100644
index 0000000..0d6c20f
--- /dev/null
+++ b/tests/fuzz/main.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifdef  __AFL_COMPILER
+
+int main(void) {
+	int ret;
+	uint8_t buf[64 * 1024];
+
+#ifdef __AFL_LOOP
+	while (__AFL_LOOP(10000))
+#endif
+	{
+		ret = fread(buf, 1, sizeof(buf), stdin);
+		if (ret < 0) {
+			return 0;
+		}
+
+		LLVMFuzzerTestOneInput(buf, ret);
+
+	}
+
+	return 0;
+}
+
+#endif /* __AFL_COMPILER */
diff --git a/tests/fuzz/yang_parse_module.c b/tests/fuzz/yang_parse_module.c
new file mode 100644
index 0000000..2a42217
--- /dev/null
+++ b/tests/fuzz/yang_parse_module.c
@@ -0,0 +1,52 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "common.h"
+#include "tree_schema_internal.h"
+#include "libyang.h"
+
+LY_ERR yang_parse_module(struct lys_parser_ctx **context, const char *data, struct lys_module *mod);
+
+int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
+{
+	struct lys_module *mod = NULL;
+	struct lys_parser_ctx *context = NULL;
+	uint8_t *data = NULL;
+	struct ly_ctx *ctx = NULL;
+	static bool log = false; 
+	LY_ERR err;
+	
+	if (!log) {
+		ly_log_options(0);
+		log = true;
+	}
+
+	err = ly_ctx_new(NULL, 0, &ctx);
+	if (err != LY_SUCCESS) {
+		fprintf(stderr, "Failed to create new context\n");
+		return 0;
+	}
+
+	data = malloc(len + 1);
+	if (data == NULL) {
+		fprintf(stderr, "Out of memory\n");
+		return 0;
+	}
+	data[len] = 0;
+	memcpy(data, buf, len);
+
+	mod = calloc(1, sizeof *mod);
+	if (mod == NULL) {
+		fprintf(stderr, "Out of memory\n");
+		return 0;
+	}
+	mod->ctx = ctx;
+
+	yang_parse_module(&context, data, mod);
+
+	free(data);
+	free(mod);
+	ly_ctx_destroy(ctx, NULL);
+	return 0;
+}
diff --git a/tests/fuzz/yangfuzz.c b/tests/fuzz/yangfuzz.c
deleted file mode 100644
index 06d6076..0000000
--- a/tests/fuzz/yangfuzz.c
+++ /dev/null
@@ -1,27 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "libyang.h"
-
-int main(int argc, char **argv) {
-	if (argc != 2) {
-		fprintf(stderr, "invalid usage\n");
-		exit(EXIT_FAILURE);
-	}
-
-
-	struct ly_ctx *ctx = NULL;
-	LY_ERR err;
-	while (__AFL_LOOP(100)) {
-		err = ly_ctx_new(NULL, 0, &ctx);
-		if (err != LY_SUCCESS) {
-			fprintf(stderr, "Failed to create context\n");
-			exit(EXIT_FAILURE);
-		}
-
-		lys_parse_path(ctx, argv[1], LYS_IN_YANG);
-		ly_ctx_destroy(ctx, NULL);
-	}
-
-	return 0;
-}