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