From c23dbe61f7a09228984b4fb78127721d286b8505 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 | 46 +++++++++++++++++++++++++++++++++++++++------- src/fuzzer/c2.c | 20 ++++++++++++++++++++ src/meson.build | 10 ++++++++++ 3 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 src/fuzzer/c2.c diff --git a/src/c2.c b/src/c2.c index 7ffe009c..0ca74148 100644 --- a/src/c2.c +++ b/src/c2.c @@ -479,6 +479,27 @@ 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); + + const char *rule = "(((role:s = \"\\\\tg^\\n\\n[\\t\" && role:s ~?= \"\") && " + "role:s ~?= \"\\n\\n\\n\\b\\n^\\n*0bon\") && role:s ~?= " + "\"\\n\\n\\x8a\\b\\n^\\n*0\\n[\\n[\\n\\n\\b\\n\")"; + cond = c2_parse(NULL, rule, NULL); + TEST_NOTEQUAL(cond, NULL); + len = c2_condition_to_str(cond->ptr, str, sizeof(str)); + TEST_STREQUAL3(str, rule, len); + c2_list_free(&cond, NULL); } #define c2_error(format, ...) \ @@ -500,11 +521,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; } @@ -531,6 +547,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); @@ -753,8 +774,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(); @@ -989,7 +1013,15 @@ 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 > 255) { + c2_error("Octal/hex escape sequence out " + "of ASCII range."); + } + if (val > 127) { + // Manual sign extension + val -= 256; + } + *(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 badb6a7e..9b06ffae 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