From b1d22b0cd1fe363ccce462d1a147b65283883b75 Mon Sep 17 00:00:00 2001 From: renwang Date: Thu, 12 Dec 2024 11:21:48 +0800 Subject: [PATCH] change --- fuzzing/fuzz.h | 8 ++++++- fuzzing/fuzz_header_parsing.c | 19 +++++++++++++++ fuzzing/fuzz_header_parsing.dict | 8 +++++++ fuzzing/meson.build | 2 ++ libsoup/soup-headers.c | 40 ++++++++++++++++++++------------ meson.build | 3 +++ tests/header-parsing-test.c | 12 +++++++++- tests/hsts-db-test.c | 3 ++- tests/proxy-test.c | 2 +- 9 files changed, 78 insertions(+), 19 deletions(-) create mode 100644 fuzzing/fuzz_header_parsing.c create mode 100644 fuzzing/fuzz_header_parsing.dict diff --git a/fuzzing/fuzz.h b/fuzzing/fuzz.h index 0d38028..8ec48f3 100644 --- a/fuzzing/fuzz.h +++ b/fuzzing/fuzz.h @@ -1,6 +1,7 @@ #include "libsoup/soup.h" int LLVMFuzzerTestOneInput (const unsigned char *data, size_t size); +static int set_logger = 0; #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION static GLogWriterOutput @@ -16,6 +17,11 @@ static void fuzz_set_logging_func (void) { #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - g_log_set_writer_func (empty_logging_func, NULL, NULL); + if(!set_logger) + { + set_logger = 1; + g_log_set_writer_func (empty_logging_func, NULL, NULL); + + } #endif } diff --git a/fuzzing/fuzz_header_parsing.c b/fuzzing/fuzz_header_parsing.c new file mode 100644 index 0000000..a8e5c1f --- /dev/null +++ b/fuzzing/fuzz_header_parsing.c @@ -0,0 +1,19 @@ +#include "fuzz.h" + +int +LLVMFuzzerTestOneInput (const unsigned char *data, size_t size) +{ + GHashTable *elements; + + // We only accept NUL terminated strings + if (!size || data[size - 1] != '\0') + return 0; + + fuzz_set_logging_func (); + + elements = soup_header_parse_param_list((char*)data); + + g_hash_table_unref(elements); + + return 0; +} \ No newline at end of file diff --git a/fuzzing/fuzz_header_parsing.dict b/fuzzing/fuzz_header_parsing.dict new file mode 100644 index 0000000..1562ca3 --- /dev/null +++ b/fuzzing/fuzz_header_parsing.dict @@ -0,0 +1,8 @@ +"*=UTF-8''" +"*=iso-8859-1''" +"'" +"''" +"=" +"*=" +""" +";" \ No newline at end of file diff --git a/fuzzing/meson.build b/fuzzing/meson.build index b14cbb5..5dd0f41 100644 --- a/fuzzing/meson.build +++ b/fuzzing/meson.build @@ -5,6 +5,7 @@ fuzz_targets = [ 'fuzz_cookie_parse', 'fuzz_content_sniffer', 'fuzz_date_time', + 'fuzz_header_parsing', ] fuzzing_args = '-fsanitize=fuzzer,address,undefined' @@ -34,6 +35,7 @@ if have_fuzzing and (fuzzing_feature.enabled() or fuzzing_feature.auto()) '-runs=200000', '-artifact_prefix=meson-logs/' + target + '-', '-print_final_stats=1', + '-max_len=4096', ] + extra_args, env: [ 'ASAN_OPTIONS=fast_unwind_on_malloc=0', diff --git a/libsoup/soup-headers.c b/libsoup/soup-headers.c index f30ee46..79320e3 100644 --- a/libsoup/soup-headers.c +++ b/libsoup/soup-headers.c @@ -646,8 +646,9 @@ soup_header_contains (const char *header, const char *token) } static void -decode_quoted_string (char *quoted_string) +decode_quoted_string_inplace (GString *quoted_gstring) { + char *quoted_string = quoted_gstring->str; char *src, *dst; src = quoted_string + 1; @@ -661,10 +662,11 @@ decode_quoted_string (char *quoted_string) } static gboolean -decode_rfc5987 (char *encoded_string) +decode_rfc5987_inplace (GString *encoded_gstring) { char *q, *decoded; gboolean iso_8859_1 = FALSE; + const char *encoded_string = encoded_gstring->str; q = strchr (encoded_string, '\''); if (!q) @@ -703,7 +705,7 @@ decode_rfc5987 (char *encoded_string) * converted into at most 2 bytes in UTF-8, and so it's still * shorter. */ - strcpy (encoded_string, decoded); + g_string_assign (encoded_gstring, decoded); g_free (decoded); return TRUE; } @@ -713,15 +715,18 @@ parse_param_list (const char *header, char delim, gboolean strict) { GHashTable *params; GSList *list, *iter; - char *item, *eq, *name_end, *value; - gboolean override, duplicated; + params = g_hash_table_new_full (soup_str_case_hash, soup_str_case_equal, - g_free, NULL); + g_free, g_free); list = parse_list (header, delim); for (iter = list; iter; iter = iter->next) { + char *item, *eq, *name_end; + gboolean override, duplicated; + GString *parsed_value = NULL; + item = iter->data; override = FALSE; @@ -736,19 +741,19 @@ parse_param_list (const char *header, char delim, gboolean strict) *name_end = '\0'; - value = (char *)skip_lws (eq + 1); + parsed_value = g_string_new ((char *)skip_lws (eq + 1)); if (name_end[-1] == '*' && name_end > item + 1) { name_end[-1] = '\0'; - if (!decode_rfc5987 (value)) { + if (!decode_rfc5987_inplace (parsed_value)) { + g_string_free (parsed_value, TRUE); g_free (item); continue; } override = TRUE; - } else if (*value == '"') - decode_quoted_string (value); - } else - value = NULL; + } else if (parsed_value->str[0] == '"') + decode_quoted_string_inplace (parsed_value); + } duplicated = g_hash_table_lookup_extended (params, item, NULL, NULL); @@ -756,11 +761,16 @@ parse_param_list (const char *header, char delim, gboolean strict) soup_header_free_param_list (params); params = NULL; g_slist_foreach (iter, (GFunc)g_free, NULL); + if (parsed_value) + g_string_free (parsed_value, TRUE); break; - } else if (override || !duplicated) - g_hash_table_replace (params, item, value); - else + } else if (override || !duplicated) { + g_hash_table_replace (params, item, parsed_value ? g_string_free (parsed_value, FALSE) : NULL); + } else { + if (parsed_value) + g_string_free (parsed_value, TRUE); g_free (item); + } } g_slist_free (list); diff --git a/meson.build b/meson.build index 73a9fa0..3a5d354 100644 --- a/meson.build +++ b/meson.build @@ -112,6 +112,9 @@ glib_deps = [glib_dep, gmodule_dep, gobject_dep, gio_dep] cdata = configuration_data() +cdata.set('GLIB_VERSION_MAX_ALLOWED', 'GLIB_VERSION_2_70') +cdata.set('GLIB_VERSION_MIN_REQUIRED', 'GLIB_VERSION_2_70') + libnghttp2_dep = dependency('libnghttp2') if (libnghttp2_dep.version() == 'unknown' and (libnghttp2_dep.type_name() == 'internal' or cc.has_function('nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation', prefix : '#include ', dependencies : libnghttp2_dep))) or libnghttp2_dep.version().version_compare('>=1.50') cdata.set('HAVE_NGHTTP2_OPTION_SET_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION', '1') diff --git a/tests/header-parsing-test.c b/tests/header-parsing-test.c index 715c2c6..9d85705 100644 --- a/tests/header-parsing-test.c +++ b/tests/header-parsing-test.c @@ -818,13 +818,23 @@ static struct ParamListTest { { "filename", "filename.jpg" }, }, }, - { FALSE, "attachment; filename*=UTF-8''t%C3%A9st.txt; filename=\"test.txt\"", { { "attachment", NULL }, { "filename", "t\xC3\xA9st.txt" }, }, }, + + /* This tests invalid UTF-8 data which *should* never be passed here but it was designed to be robust against it. */ + { TRUE, + "invalid*=\x69\x27\x27\x93\x93\x93\x93\xff\x61\x61\x61\x61\x61\x61\x61\x62\x63\x64\x65\x0a; filename*=iso-8859-1''\x69\x27\x27\x93\x93\x93\x93\xff\x61\x61\x61\x61\x61\x61\x61\x62\x63\x64\x65\x0a; foo", + { + { "filename", "i''\302\223\302\223\302\223\302\223\303\277aaaaaaabcde" }, + { "invalid", "\302\223\302\223\302\223\302\223\303\277aaaaaaabcde" }, + { "foo", NULL }, + + }, + } }; static const int num_paramlisttests = G_N_ELEMENTS (paramlisttests); diff --git a/tests/hsts-db-test.c b/tests/hsts-db-test.c index 1149a04..04d7c4f 100644 --- a/tests/hsts-db-test.c +++ b/tests/hsts-db-test.c @@ -1,8 +1,9 @@ +#include "test-utils.h" + #include #include #include -#include "test-utils.h" #include "soup-uri-utils-private.h" #define DB_FILE "hsts-db.sqlite" diff --git a/tests/proxy-test.c b/tests/proxy-test.c index ec03936..1125dad 100644 --- a/tests/proxy-test.c +++ b/tests/proxy-test.c @@ -1,8 +1,8 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ -#include #include "test-utils.h" +#include typedef struct { const char *explanation; const char *url; -- 2.20.1