From 4fca4aab077a46368cc4033dfbb36b5c5c4874fe Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 16 Feb 2024 23:12:23 +0000 Subject: [PATCH] c2: add a libfuzzer fuzzer for c2 Add fix some simple bugs discovered by the fuzzer. Two cases of missing input validation (we have assertions, but assertion failures are not user friendly). And one case of jump over variable initialization with goto. Signed-off-by: Yuxuan Shui --- src/c2.c | 38 ++++++++++++++++++++++++++++++-------- src/fuzzer/c2.c | 20 ++++++++++++++++++++ src/meson.build | 10 ++++++++++ 3 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 src/fuzzer/c2.c diff --git a/src/c2.c b/src/c2.c index 6f864c9b..4112d7e6 100644 --- a/src/c2.c +++ b/src/c2.c @@ -486,6 +486,18 @@ TEST_CASE(c2_parse) { cond = c2_parse(NULL, "_NET_WM_STATE = '_NET_WM_STATE_HIDDEN'", NULL); TEST_EQUAL(cond, NULL); + + cond = c2_parse(NULL, "1A:\n1111111111111ar1", NULL); + TEST_EQUAL(cond, NULL); + + cond = c2_parse(NULL, "N [4444444444444: \n", NULL); + TEST_EQUAL(cond, NULL); + + cond = c2_parse(NULL, " x:a=\"b\377\\xCCCCC", NULL); + TEST_EQUAL(cond, NULL); + + cond = c2_parse(NULL, "!!!!!!!((((((!(((((,", NULL); + TEST_EQUAL(cond, NULL); } #define c2_error(format, ...) \ @@ -507,11 +519,6 @@ TEST_CASE(c2_parse) { * @return offset of next character in string */ static int c2_parse_grp(const char *pattern, int offset, c2_ptr_t *presult, int level) { - // Check for recursion levels - if (level > C2_MAX_LEVELS) { - c2_error("Exceeded maximum recursion levels."); - } - if (!pattern) { return -1; } @@ -538,6 +545,11 @@ static int c2_parse_grp(const char *pattern, int offset, c2_ptr_t *presult, int // after encountering a logical operator bool next_expected = true; + // Check for recursion levels + if (level > C2_MAX_LEVELS) { + c2_error("Exceeded maximum recursion levels."); + } + // Parse the pattern character-by-character for (; pattern[offset]; ++offset) { assert(elei <= 2); @@ -761,8 +773,11 @@ static int c2_parse_target(const char *pattern, int offset, c2_ptr_t *presult) { if (!endptr || pattern + offset == endptr) { c2_error("No index number found after bracket."); } + if (index > INT_MAX) { + c2_error("Index %ld too large.", index); + } - pleaf->index = to_int_checked(index); + pleaf->index = (int)index; offset = to_int_checked(endptr - pattern); C2H_SKIP_SPACES(); @@ -848,7 +863,10 @@ static int c2_parse_target(const char *pattern, int offset, c2_ptr_t *presult) { "target.", pleaf->format); } - pleaf->format = to_int_checked(format); + if (format != 8 && format != 16 && format != 32) { + c2_error("Invalid format %ld.", format); + } + pleaf->format = (int)format; } } } @@ -1025,7 +1043,11 @@ static int c2_parse_pattern(const char *pattern, int offset, c2_ptr_t *presult) c2_error("Invalid octal/hex escape " "sequence."); } - *(ptptnstr++) = to_char_checked(val); + if (val > 127) { + c2_error("Octal/hex escape sequence out " + "of ASCII range."); + } + *(ptptnstr++) = (char)val; offset += 2; break; } diff --git a/src/fuzzer/c2.c b/src/fuzzer/c2.c new file mode 100644 index 00000000..078116d4 --- /dev/null +++ b/src/fuzzer/c2.c @@ -0,0 +1,20 @@ + +#include "c2.h" +#include +#include +#include "config.h" +#include "log.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + log_init_tls(); + if (size == 0) { + return 0; + } + if (data[size - 1] != 0) { + return 0; + } + c2_lptr_t *cond = c2_parse(NULL, (char *)data, NULL); + (void)cond; + (void)size; + return 0; // Values other than 0 and -1 are reserved for future use. +} \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index d7108fba..6057c7d2 100644 --- a/src/meson.build +++ b/src/meson.build @@ -95,3 +95,13 @@ picom = executable('picom', srcs, c_args: cflags, if get_option('unittest') test('picom unittest', picom, args: [ '--unittest' ]) endif + +if cc.has_argument('-fsanitize=fuzzer') + c2_fuzz = executable('c2_fuzz', srcs + ['fuzzer/c2.c'], + c_args: cflags + ['-fsanitize=fuzzer', '-Dmain=__main__'], + link_args: ['-fsanitize=fuzzer'], + dependencies: [ base_deps, deps, test_h_dep ], + build_by_default: false, + install: false, include_directories: picom_inc + ) +endif