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 <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2024-02-16 23:12:23 +00:00
parent 04e7306cfa
commit c23dbe61f7
No known key found for this signature in database
GPG Key ID: D3A4405BE6CC17F4
3 changed files with 69 additions and 7 deletions

View File

@ -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;
}

20
src/fuzzer/c2.c Normal file
View File

@ -0,0 +1,20 @@
#include "c2.h"
#include <stddef.h>
#include <stdint.h>
#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.
}

View File

@ -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