diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1a2264137..28c6f9a62 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -338,6 +338,16 @@ if ("${BUILD_TYPE_UPPER}" STREQUAL "DEBUG")
source_format_enable(0.77)
endif()
+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 -fno-omit-frame-pointer")
+ endif()
+endif()
+
# check regex compatibility
check_include_file("regex.h" LY_HAVE_REGEX_H)
if(LY_HAVE_REGEX_H)
@@ -498,16 +508,8 @@ endif()
if(ENABLE_TESTS)
enable_testing()
add_subdirectory(tests)
-endif()
-
-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 -fno-omit-frame-pointer")
- endif()
+elseif(ENABLE_FUZZ_TARGETS AND NOT WIN32)
+ add_subdirectory(tests/fuzz)
endif()
# create coverage target for generating coverage reports
diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt
index 99906166b..d14e444e7 100644
--- a/tests/fuzz/CMakeLists.txt
+++ b/tests/fuzz/CMakeLists.txt
@@ -1,5 +1,5 @@
if(ENABLE_FUZZ_TARGETS)
- set(fuzz_targets lys_parse_mem lyd_parse_mem_xml lyd_parse_mem_json yang_parse_module)
+ set(fuzz_targets lys_parse_mem lyd_parse_mem_xml lyd_parse_mem_json lyd_parse_lyb lyd_parse_schema_mount_xml yang_parse_module)
if(FUZZER STREQUAL "AFL")
foreach(target_name IN LISTS fuzz_targets)
@@ -9,15 +9,15 @@ if(ENABLE_FUZZ_TARGETS)
else()
foreach(target_name IN LISTS fuzz_targets)
add_executable(${target_name}_fuzz_harness ${target_name}.c)
- set_source_files_properties(${target_name}.c PROPERTIES COMPILE_FLAGS "-fsanitize=fuzzer")
- target_link_libraries(${target_name}_fuzz_harness yang "-fsanitize=fuzzer")
+ set_source_files_properties(${target_name}.c PROPERTIES COMPILE_FLAGS "-fsanitize=fuzzer,address,undefined")
+ target_link_libraries(${target_name}_fuzz_harness yang "-fsanitize=fuzzer,address,undefined")
endforeach()
endif()
endif()
if(ENABLE_TESTS)
add_executable(fuzz_regression_test fuzz_regression_test.c)
- set(fuzz_regression_tests lys_parse_mem lyd_parse_mem_xml lyd_parse_mem_json)
+ set(fuzz_regression_tests lys_parse_mem lyd_parse_mem_xml lyd_parse_mem_json lyd_parse_schema_mount_xml)
foreach(target_name IN LISTS fuzz_regression_tests)
file(COPY ${CMAKE_SOURCE_DIR}/tests/fuzz/corpus/${target_name} DESTINATION ${CMAKE_BINARY_DIR}/tests/fuzz/)
add_executable(regress_fuzz_${target_name} ${target_name}.c main.c)
diff --git a/tests/fuzz/corpus/lyd_parse_schema_mount_xml/valid-mounted-leaf b/tests/fuzz/corpus/lyd_parse_schema_mount_xml/valid-mounted-leaf
new file mode 100644
index 000000000..009543605
--- /dev/null
+++ b/tests/fuzz/corpus/lyd_parse_schema_mount_xml/valid-mounted-leaf
@@ -0,0 +1 @@
+1
diff --git a/tests/fuzz/lyd_parse_lyb.c b/tests/fuzz/lyd_parse_lyb.c
new file mode 100644
index 000000000..2dd902f7f
--- /dev/null
+++ b/tests/fuzz/lyd_parse_lyb.c
@@ -0,0 +1,56 @@
+#define _GNU_SOURCE
+
+#include
+#include
+#include
+#include
+
+#include "libyang.h"
+
+int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
+{
+ struct ly_ctx *ctx = NULL;
+ struct lyd_node *tree = NULL;
+ FILE *input = NULL;
+ static bool log = false;
+ const char *schema =
+ "module fuzz-lyb {namespace urn:tests:fuzz-lyb;prefix fl;"
+ "container c {leaf s {type string;} leaf u16 {type uint16;}"
+ "leaf-list bits {type bits {bit zero; bit one;}}}}";
+
+ if (!log) {
+ ly_log_options(0);
+ log = true;
+ }
+
+ if (ly_ctx_new(LY_SRC_DIR "/modules", 0, &ctx) != LY_SUCCESS) {
+ return 0;
+ }
+
+ if (lys_parse_mem(ctx, schema, LYS_IN_YANG, NULL) != LY_SUCCESS) {
+ goto cleanup;
+ }
+
+ input = tmpfile();
+ if (!input) {
+ goto cleanup;
+ }
+
+ if (len && (fwrite(buf, 1, len, input) != len)) {
+ goto cleanup;
+ }
+ fflush(input);
+ if (lseek(fileno(input), 0, SEEK_SET) == -1) {
+ goto cleanup;
+ }
+
+ lyd_parse_data_fd(ctx, fileno(input), LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree);
+
+cleanup:
+ lyd_free_all(tree);
+ if (input) {
+ fclose(input);
+ }
+ ly_ctx_destroy(ctx);
+ return 0;
+}
diff --git a/tests/fuzz/lyd_parse_mem_json.c b/tests/fuzz/lyd_parse_mem_json.c
index c2f3e9813..739ea81a8 100644
--- a/tests/fuzz/lyd_parse_mem_json.c
+++ b/tests/fuzz/lyd_parse_mem_json.c
@@ -62,8 +62,7 @@ int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
err = ly_ctx_new(LY_SRC_DIR "/modules", 0, &ctx);
if (err != LY_SUCCESS) {
- fprintf(stderr, "Failed to create context\n");
- exit(EXIT_FAILURE);
+ return 0;
}
lys_parse_mem(ctx, schema_a, LYS_IN_YANG, NULL);
@@ -71,12 +70,14 @@ int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
data = malloc(len + 1);
if (data == NULL) {
- return 0;
+ goto cleanup;
}
memcpy(data, buf, len);
data[len] = 0;
lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree);
+
+cleanup:
lyd_free_all(tree);
ly_ctx_destroy(ctx);
diff --git a/tests/fuzz/lyd_parse_mem_xml.c b/tests/fuzz/lyd_parse_mem_xml.c
index e317fde47..0ef5481dc 100644
--- a/tests/fuzz/lyd_parse_mem_xml.c
+++ b/tests/fuzz/lyd_parse_mem_xml.c
@@ -62,8 +62,7 @@ int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
err = ly_ctx_new(LY_SRC_DIR "/modules", 0, &ctx);
if (err != LY_SUCCESS) {
- fprintf(stderr, "Failed to create context\n");
- exit(EXIT_FAILURE);
+ return 0;
}
lys_parse_mem(ctx, schema_a, LYS_IN_YANG, NULL);
@@ -71,12 +70,14 @@ int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
data = malloc(len + 1);
if (data == NULL) {
- return 0;
+ goto cleanup;
}
memcpy(data, buf, len);
data[len] = 0;
lyd_parse_data_mem(ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree);
+
+cleanup:
lyd_free_all(tree);
ly_ctx_destroy(ctx);
diff --git a/tests/fuzz/lyd_parse_schema_mount_xml.c b/tests/fuzz/lyd_parse_schema_mount_xml.c
new file mode 100644
index 000000000..d52c065b0
--- /dev/null
+++ b/tests/fuzz/lyd_parse_schema_mount_xml.c
@@ -0,0 +1,170 @@
+#define _GNU_SOURCE
+
+#include
+#include
+#include
+#include
+#include
+
+#include "libyang.h"
+
+static const char mounted_module[] =
+ "module fuzz-sm-leaf {"
+ "namespace \"urn:tests:fuzz-sm-leaf\";"
+ "prefix fsl;"
+ "leaf lfl {type uint16;}"
+ "leaf txt {type string;}"
+ "}";
+
+static const char mount_module[] =
+ "module fuzz-sm {"
+ "yang-version 1.1;"
+ "namespace \"urn:tests:fuzz-sm\";"
+ "prefix fsm;"
+ "import ietf-yang-schema-mount {prefix sm;}"
+ "container root {sm:mount-point \"root\";}"
+ "}";
+
+static const char ext_data_xml[] =
+ ""
+ "fuzz-setfuzz-sm-leaf"
+ "urn:tests:fuzz-sm-leaf"
+ "1"
+ ""
+ "1"
+ ""
+ "fuzz-sm"
+ "";
+
+static const char mount_module_dir_template[] = "/tmp/libyang-sm-fuzz.XXXXXX";
+static char mount_module_dir[sizeof mount_module_dir_template];
+static ly_bool mount_module_dir_ready;
+
+static void
+remove_mount_module_dir(void)
+{
+ char path[sizeof mount_module_dir + sizeof "/fuzz-sm-leaf.yang"];
+
+ if (!mount_module_dir_ready) {
+ return;
+ }
+
+ snprintf(path, sizeof path, "%s/fuzz-sm-leaf.yang", mount_module_dir);
+ unlink(path);
+ rmdir(mount_module_dir);
+ mount_module_dir[0] = 0;
+ mount_module_dir_ready = 0;
+}
+
+static LY_ERR
+prepare_mount_module_dir(void)
+{
+ char path[sizeof mount_module_dir + sizeof "/fuzz-sm-leaf.yang"];
+ FILE *f;
+
+ if (mount_module_dir_ready) {
+ return LY_SUCCESS;
+ }
+
+ memcpy(mount_module_dir, mount_module_dir_template, sizeof mount_module_dir);
+ if (!mkdtemp(mount_module_dir)) {
+ mount_module_dir[0] = 0;
+ return LY_ESYS;
+ }
+ mount_module_dir_ready = 1;
+ atexit(remove_mount_module_dir);
+
+ snprintf(path, sizeof path, "%s/fuzz-sm-leaf.yang", mount_module_dir);
+ f = fopen(path, "w");
+ if (!f) {
+ remove_mount_module_dir();
+ return LY_ESYS;
+ }
+
+ if (fputs(mounted_module, f) == EOF) {
+ fclose(f);
+ remove_mount_module_dir();
+ return LY_ESYS;
+ }
+ if (fclose(f) == EOF) {
+ remove_mount_module_dir();
+ return LY_ESYS;
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+fuzz_ext_data_clb(const struct lysc_ext_instance *ext, const struct lyd_node *parent, void *user_data,
+ void **ext_data, ly_bool *ext_data_free)
+{
+ struct lyd_node *data = NULL;
+ static ly_bool recursive_call;
+ LY_ERR ret = LY_SUCCESS;
+
+ (void)ext;
+ (void)parent;
+
+ *ext_data = NULL;
+ *ext_data_free = 0;
+
+ if (recursive_call) {
+ return LY_SUCCESS;
+ }
+
+ recursive_call = 1;
+ ret = lyd_parse_data_mem(user_data, ext_data_xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &data);
+ recursive_call = 0;
+ if (ret) {
+ lyd_free_all(data);
+ return ret;
+ }
+
+ *ext_data = data;
+ *ext_data_free = 1;
+ return LY_SUCCESS;
+}
+
+int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
+{
+ struct ly_ctx *ctx = NULL;
+ struct lyd_node *tree = NULL;
+ char *data = NULL;
+ static bool log = false;
+
+ if (!log) {
+ ly_log_options(0);
+ log = true;
+ }
+
+ if (prepare_mount_module_dir() != LY_SUCCESS) {
+ return 0;
+ }
+
+ if (ly_ctx_new(LY_SRC_DIR "/modules", 0, &ctx) != LY_SUCCESS) {
+ return 0;
+ }
+ if (ly_ctx_set_searchdir(ctx, mount_module_dir) != LY_SUCCESS) {
+ goto cleanup;
+ }
+ if (lys_parse_mem(ctx, mount_module, LYS_IN_YANG, NULL) != LY_SUCCESS) {
+ goto cleanup;
+ }
+
+ ly_ctx_set_ext_data_clb(ctx, fuzz_ext_data_clb, ctx);
+
+ data = malloc(len + 1);
+ if (!data) {
+ goto cleanup;
+ }
+ memcpy(data, buf, len);
+ data[len] = 0;
+
+ lyd_parse_data_mem(ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT | LYD_VALIDATE_MULTI_ERROR, &tree);
+
+cleanup:
+ lyd_free_all(tree);
+ free(data);
+ ly_ctx_destroy(ctx);
+ return 0;
+}
diff --git a/tests/fuzz/lys_parse_mem.c b/tests/fuzz/lys_parse_mem.c
index 6f8d87b40..739861380 100644
--- a/tests/fuzz/lys_parse_mem.c
+++ b/tests/fuzz/lys_parse_mem.c
@@ -18,12 +18,12 @@ int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
err = ly_ctx_new(LY_SRC_DIR "/modules", 0, &ctx);
if (err != LY_SUCCESS) {
- fprintf(stderr, "Failed to create context\n");
- exit(EXIT_FAILURE);
+ return 0;
}
data = malloc(len + 1);
if (data == NULL) {
+ ly_ctx_destroy(ctx);
return 0;
}
diff --git a/tests/fuzz/yang_parse_module.c b/tests/fuzz/yang_parse_module.c
index e9fbe8b42..7cd90387d 100644
--- a/tests/fuzz/yang_parse_module.c
+++ b/tests/fuzz/yang_parse_module.c
@@ -25,7 +25,7 @@ int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
data = malloc(len + 1);
if (data == NULL) {
- fprintf(stderr, "Out of memory\n");
+ ly_ctx_destroy(ctx);
return 0;
}
memcpy(data, buf, len);