Skip to content

Commit b3404d2

Browse files
BeaCoxmichalvasko
authored andcommitted
tests: extend fuzz parser coverage
1 parent 193c973 commit b3404d2

9 files changed

Lines changed: 254 additions & 23 deletions

File tree

CMakeLists.txt

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,16 @@ if ("${BUILD_TYPE_UPPER}" STREQUAL "DEBUG")
338338
source_format_enable(0.77)
339339
endif()
340340

341+
if(ENABLE_FUZZ_TARGETS)
342+
set(FUZZER "AFL" CACHE STRING "fuzzer type")
343+
if(FUZZER STREQUAL "LibFuzzer")
344+
if (NOT CMAKE_C_COMPILER_ID STREQUAL "Clang")
345+
message(FATAL_ERROR "LibFuzzer works only with clang")
346+
endif()
347+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address,undefined -fno-omit-frame-pointer")
348+
endif()
349+
endif()
350+
341351
# check regex compatibility
342352
check_include_file("regex.h" LY_HAVE_REGEX_H)
343353
if(LY_HAVE_REGEX_H)
@@ -498,16 +508,8 @@ endif()
498508
if(ENABLE_TESTS)
499509
enable_testing()
500510
add_subdirectory(tests)
501-
endif()
502-
503-
if(ENABLE_FUZZ_TARGETS)
504-
set(FUZZER "AFL" CACHE STRING "fuzzer type")
505-
if(FUZZER STREQUAL "LibFuzzer")
506-
if (NOT CMAKE_C_COMPILER_ID STREQUAL "Clang")
507-
message(FATAL_ERROR "LibFuzzer works only with clang")
508-
endif()
509-
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address,undefined -fno-omit-frame-pointer")
510-
endif()
511+
elseif(ENABLE_FUZZ_TARGETS AND NOT WIN32)
512+
add_subdirectory(tests/fuzz)
511513
endif()
512514

513515
# create coverage target for generating coverage reports

tests/fuzz/CMakeLists.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
if(ENABLE_FUZZ_TARGETS)
2-
set(fuzz_targets lys_parse_mem lyd_parse_mem_xml lyd_parse_mem_json yang_parse_module)
2+
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)
33

44
if(FUZZER STREQUAL "AFL")
55
foreach(target_name IN LISTS fuzz_targets)
@@ -9,15 +9,15 @@ if(ENABLE_FUZZ_TARGETS)
99
else()
1010
foreach(target_name IN LISTS fuzz_targets)
1111
add_executable(${target_name}_fuzz_harness ${target_name}.c)
12-
set_source_files_properties(${target_name}.c PROPERTIES COMPILE_FLAGS "-fsanitize=fuzzer")
13-
target_link_libraries(${target_name}_fuzz_harness yang "-fsanitize=fuzzer")
12+
set_source_files_properties(${target_name}.c PROPERTIES COMPILE_FLAGS "-fsanitize=fuzzer,address,undefined")
13+
target_link_libraries(${target_name}_fuzz_harness yang "-fsanitize=fuzzer,address,undefined")
1414
endforeach()
1515
endif()
1616
endif()
1717

1818
if(ENABLE_TESTS)
1919
add_executable(fuzz_regression_test fuzz_regression_test.c)
20-
set(fuzz_regression_tests lys_parse_mem lyd_parse_mem_xml lyd_parse_mem_json)
20+
set(fuzz_regression_tests lys_parse_mem lyd_parse_mem_xml lyd_parse_mem_json lyd_parse_schema_mount_xml)
2121
foreach(target_name IN LISTS fuzz_regression_tests)
2222
file(COPY ${CMAKE_SOURCE_DIR}/tests/fuzz/corpus/${target_name} DESTINATION ${CMAKE_BINARY_DIR}/tests/fuzz/)
2323
add_executable(regress_fuzz_${target_name} ${target_name}.c main.c)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<root xmlns="urn:tests:fuzz-sm"><lfl xmlns="urn:tests:fuzz-sm-leaf">1</lfl></root>

tests/fuzz/lyd_parse_lyb.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#define _GNU_SOURCE
2+
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
#include <stdbool.h>
6+
#include <unistd.h>
7+
8+
#include "libyang.h"
9+
10+
int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
11+
{
12+
struct ly_ctx *ctx = NULL;
13+
struct lyd_node *tree = NULL;
14+
FILE *input = NULL;
15+
static bool log = false;
16+
const char *schema =
17+
"module fuzz-lyb {namespace urn:tests:fuzz-lyb;prefix fl;"
18+
"container c {leaf s {type string;} leaf u16 {type uint16;}"
19+
"leaf-list bits {type bits {bit zero; bit one;}}}}";
20+
21+
if (!log) {
22+
ly_log_options(0);
23+
log = true;
24+
}
25+
26+
if (ly_ctx_new(LY_SRC_DIR "/modules", 0, &ctx) != LY_SUCCESS) {
27+
return 0;
28+
}
29+
30+
if (lys_parse_mem(ctx, schema, LYS_IN_YANG, NULL) != LY_SUCCESS) {
31+
goto cleanup;
32+
}
33+
34+
input = tmpfile();
35+
if (!input) {
36+
goto cleanup;
37+
}
38+
39+
if (len && (fwrite(buf, 1, len, input) != len)) {
40+
goto cleanup;
41+
}
42+
fflush(input);
43+
if (lseek(fileno(input), 0, SEEK_SET) == -1) {
44+
goto cleanup;
45+
}
46+
47+
lyd_parse_data_fd(ctx, fileno(input), LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree);
48+
49+
cleanup:
50+
lyd_free_all(tree);
51+
if (input) {
52+
fclose(input);
53+
}
54+
ly_ctx_destroy(ctx);
55+
return 0;
56+
}

tests/fuzz/lyd_parse_mem_json.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,21 +62,22 @@ int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
6262

6363
err = ly_ctx_new(LY_SRC_DIR "/modules", 0, &ctx);
6464
if (err != LY_SUCCESS) {
65-
fprintf(stderr, "Failed to create context\n");
66-
exit(EXIT_FAILURE);
65+
return 0;
6766
}
6867

6968
lys_parse_mem(ctx, schema_a, LYS_IN_YANG, NULL);
7069
lys_parse_mem(ctx, schema_b, LYS_IN_YANG, NULL);
7170

7271
data = malloc(len + 1);
7372
if (data == NULL) {
74-
return 0;
73+
goto cleanup;
7574
}
7675
memcpy(data, buf, len);
7776
data[len] = 0;
7877

7978
lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree);
79+
80+
cleanup:
8081
lyd_free_all(tree);
8182
ly_ctx_destroy(ctx);
8283

tests/fuzz/lyd_parse_mem_xml.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,21 +62,22 @@ int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
6262

6363
err = ly_ctx_new(LY_SRC_DIR "/modules", 0, &ctx);
6464
if (err != LY_SUCCESS) {
65-
fprintf(stderr, "Failed to create context\n");
66-
exit(EXIT_FAILURE);
65+
return 0;
6766
}
6867

6968
lys_parse_mem(ctx, schema_a, LYS_IN_YANG, NULL);
7069
lys_parse_mem(ctx, schema_b, LYS_IN_YANG, NULL);
7170

7271
data = malloc(len + 1);
7372
if (data == NULL) {
74-
return 0;
73+
goto cleanup;
7574
}
7675
memcpy(data, buf, len);
7776
data[len] = 0;
7877

7978
lyd_parse_data_mem(ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree);
79+
80+
cleanup:
8081
lyd_free_all(tree);
8182
ly_ctx_destroy(ctx);
8283

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#define _GNU_SOURCE
2+
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
#include <stdbool.h>
6+
#include <string.h>
7+
#include <unistd.h>
8+
9+
#include "libyang.h"
10+
11+
static const char mounted_module[] =
12+
"module fuzz-sm-leaf {"
13+
"namespace \"urn:tests:fuzz-sm-leaf\";"
14+
"prefix fsl;"
15+
"leaf lfl {type uint16;}"
16+
"leaf txt {type string;}"
17+
"}";
18+
19+
static const char mount_module[] =
20+
"module fuzz-sm {"
21+
"yang-version 1.1;"
22+
"namespace \"urn:tests:fuzz-sm\";"
23+
"prefix fsm;"
24+
"import ietf-yang-schema-mount {prefix sm;}"
25+
"container root {sm:mount-point \"root\";}"
26+
"}";
27+
28+
static const char ext_data_xml[] =
29+
"<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">"
30+
"<module-set><name>fuzz-set</name><module><name>fuzz-sm-leaf</name>"
31+
"<namespace>urn:tests:fuzz-sm-leaf</namespace></module></module-set>"
32+
"<content-id>1</content-id></yang-library>"
33+
"<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">"
34+
"<module-set-id>1</module-set-id></modules-state>"
35+
"<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">"
36+
"<mount-point><module>fuzz-sm</module><label>root</label><inline/></mount-point>"
37+
"</schema-mounts>";
38+
39+
static const char mount_module_dir_template[] = "/tmp/libyang-sm-fuzz.XXXXXX";
40+
static char mount_module_dir[sizeof mount_module_dir_template];
41+
static ly_bool mount_module_dir_ready;
42+
43+
static void
44+
remove_mount_module_dir(void)
45+
{
46+
char path[sizeof mount_module_dir + sizeof "/fuzz-sm-leaf.yang"];
47+
48+
if (!mount_module_dir_ready) {
49+
return;
50+
}
51+
52+
snprintf(path, sizeof path, "%s/fuzz-sm-leaf.yang", mount_module_dir);
53+
unlink(path);
54+
rmdir(mount_module_dir);
55+
mount_module_dir[0] = 0;
56+
mount_module_dir_ready = 0;
57+
}
58+
59+
static LY_ERR
60+
prepare_mount_module_dir(void)
61+
{
62+
char path[sizeof mount_module_dir + sizeof "/fuzz-sm-leaf.yang"];
63+
FILE *f;
64+
65+
if (mount_module_dir_ready) {
66+
return LY_SUCCESS;
67+
}
68+
69+
memcpy(mount_module_dir, mount_module_dir_template, sizeof mount_module_dir);
70+
if (!mkdtemp(mount_module_dir)) {
71+
mount_module_dir[0] = 0;
72+
return LY_ESYS;
73+
}
74+
mount_module_dir_ready = 1;
75+
atexit(remove_mount_module_dir);
76+
77+
snprintf(path, sizeof path, "%s/fuzz-sm-leaf.yang", mount_module_dir);
78+
f = fopen(path, "w");
79+
if (!f) {
80+
remove_mount_module_dir();
81+
return LY_ESYS;
82+
}
83+
84+
if (fputs(mounted_module, f) == EOF) {
85+
fclose(f);
86+
remove_mount_module_dir();
87+
return LY_ESYS;
88+
}
89+
if (fclose(f) == EOF) {
90+
remove_mount_module_dir();
91+
return LY_ESYS;
92+
}
93+
94+
return LY_SUCCESS;
95+
}
96+
97+
static LY_ERR
98+
fuzz_ext_data_clb(const struct lysc_ext_instance *ext, const struct lyd_node *parent, void *user_data,
99+
void **ext_data, ly_bool *ext_data_free)
100+
{
101+
struct lyd_node *data = NULL;
102+
static ly_bool recursive_call;
103+
LY_ERR ret = LY_SUCCESS;
104+
105+
(void)ext;
106+
(void)parent;
107+
108+
*ext_data = NULL;
109+
*ext_data_free = 0;
110+
111+
if (recursive_call) {
112+
return LY_SUCCESS;
113+
}
114+
115+
recursive_call = 1;
116+
ret = lyd_parse_data_mem(user_data, ext_data_xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &data);
117+
recursive_call = 0;
118+
if (ret) {
119+
lyd_free_all(data);
120+
return ret;
121+
}
122+
123+
*ext_data = data;
124+
*ext_data_free = 1;
125+
return LY_SUCCESS;
126+
}
127+
128+
int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
129+
{
130+
struct ly_ctx *ctx = NULL;
131+
struct lyd_node *tree = NULL;
132+
char *data = NULL;
133+
static bool log = false;
134+
135+
if (!log) {
136+
ly_log_options(0);
137+
log = true;
138+
}
139+
140+
if (prepare_mount_module_dir() != LY_SUCCESS) {
141+
return 0;
142+
}
143+
144+
if (ly_ctx_new(LY_SRC_DIR "/modules", 0, &ctx) != LY_SUCCESS) {
145+
return 0;
146+
}
147+
if (ly_ctx_set_searchdir(ctx, mount_module_dir) != LY_SUCCESS) {
148+
goto cleanup;
149+
}
150+
if (lys_parse_mem(ctx, mount_module, LYS_IN_YANG, NULL) != LY_SUCCESS) {
151+
goto cleanup;
152+
}
153+
154+
ly_ctx_set_ext_data_clb(ctx, fuzz_ext_data_clb, ctx);
155+
156+
data = malloc(len + 1);
157+
if (!data) {
158+
goto cleanup;
159+
}
160+
memcpy(data, buf, len);
161+
data[len] = 0;
162+
163+
lyd_parse_data_mem(ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT | LYD_VALIDATE_MULTI_ERROR, &tree);
164+
165+
cleanup:
166+
lyd_free_all(tree);
167+
free(data);
168+
ly_ctx_destroy(ctx);
169+
return 0;
170+
}

tests/fuzz/lys_parse_mem.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
1818

1919
err = ly_ctx_new(LY_SRC_DIR "/modules", 0, &ctx);
2020
if (err != LY_SUCCESS) {
21-
fprintf(stderr, "Failed to create context\n");
22-
exit(EXIT_FAILURE);
21+
return 0;
2322
}
2423

2524
data = malloc(len + 1);
2625
if (data == NULL) {
26+
ly_ctx_destroy(ctx);
2727
return 0;
2828
}
2929

tests/fuzz/yang_parse_module.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len)
2525

2626
data = malloc(len + 1);
2727
if (data == NULL) {
28-
fprintf(stderr, "Out of memory\n");
28+
ly_ctx_destroy(ctx);
2929
return 0;
3030
}
3131
memcpy(data, buf, len);

0 commit comments

Comments
 (0)