mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-27 06:26:15 +01:00
Merge branch 'ossfuzz-23815-23818-uri-overflows' into 'master'
Fix buffer read overflows in GUri See merge request GNOME/glib!1559
This commit is contained in:
commit
15bf2ddaf5
@ -1,19 +1,18 @@
|
||||
#include "fuzz.h"
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput (const unsigned char *data, size_t size)
|
||||
static void
|
||||
test_bytes (const guint8 *data,
|
||||
gsize size)
|
||||
{
|
||||
GBytes *unescaped_bytes = NULL;
|
||||
gchar *escaped_string = NULL;
|
||||
|
||||
fuzz_set_logging_func ();
|
||||
|
||||
if (size > G_MAXSSIZE)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
unescaped_bytes = g_uri_unescape_bytes ((const gchar *) data, (gssize) size);
|
||||
if (unescaped_bytes == NULL)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
escaped_string = g_uri_escape_bytes (g_bytes_get_data (unescaped_bytes, NULL),
|
||||
g_bytes_get_size (unescaped_bytes),
|
||||
@ -21,9 +20,41 @@ LLVMFuzzerTestOneInput (const unsigned char *data, size_t size)
|
||||
g_bytes_unref (unescaped_bytes);
|
||||
|
||||
if (escaped_string == NULL)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
g_free (escaped_string);
|
||||
}
|
||||
|
||||
static void
|
||||
test_string (const guint8 *data,
|
||||
gsize size)
|
||||
{
|
||||
gchar *unescaped_string = NULL;
|
||||
gchar *escaped_string = NULL;
|
||||
|
||||
unescaped_string = g_uri_unescape_segment ((const gchar *) data, (const gchar *) data + size, NULL);
|
||||
if (unescaped_string == NULL)
|
||||
return;
|
||||
|
||||
escaped_string = g_uri_escape_string (unescaped_string, NULL, TRUE);
|
||||
g_free (unescaped_string);
|
||||
|
||||
if (escaped_string == NULL)
|
||||
return;
|
||||
|
||||
g_free (escaped_string);
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput (const unsigned char *data, size_t size)
|
||||
{
|
||||
fuzz_set_logging_func ();
|
||||
|
||||
/* Bytes form */
|
||||
test_bytes (data, size);
|
||||
|
||||
/* String form (doesn’t do %-decoding) */
|
||||
test_string (data, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -253,10 +253,11 @@ uri_decoder (gchar **out,
|
||||
{
|
||||
if (*s == '%')
|
||||
{
|
||||
if (!g_ascii_isxdigit (s[1]) ||
|
||||
if (s + 2 >= end ||
|
||||
!g_ascii_isxdigit (s[1]) ||
|
||||
!g_ascii_isxdigit (s[2]))
|
||||
{
|
||||
/* % followed by non-hex; this is an error */
|
||||
/* % followed by non-hex or the end of the string; this is an error */
|
||||
if (flags & G_URI_FLAGS_PARSE_STRICT)
|
||||
{
|
||||
g_set_error_literal (error, G_URI_ERROR, parse_error,
|
||||
@ -1782,7 +1783,7 @@ g_uri_parse_params (const gchar *params,
|
||||
const gchar *end, *attr, *attr_end, *value, *value_end;
|
||||
gchar *decoded_attr, *decoded_value;
|
||||
|
||||
g_return_val_if_fail (params != NULL, NULL);
|
||||
g_return_val_if_fail (length == 0 || params != NULL, NULL);
|
||||
g_return_val_if_fail (length >= -1, NULL);
|
||||
|
||||
if (case_insensitive)
|
||||
|
219
glib/tests/uri.c
219
glib/tests/uri.c
@ -328,35 +328,111 @@ run_uri_list_tests (void)
|
||||
}
|
||||
|
||||
static void
|
||||
test_uri_unescape (void)
|
||||
test_uri_unescape_string (void)
|
||||
{
|
||||
GBytes *bytes;
|
||||
gchar *s;
|
||||
const gchar *data;
|
||||
const gchar *escaped_segment = "%2Babc %4F---";
|
||||
const struct
|
||||
{
|
||||
/* Inputs */
|
||||
const gchar *escaped; /* (nullable) */
|
||||
const gchar *illegal_characters; /* (nullable) */
|
||||
/* Outputs */
|
||||
const gchar *expected_unescaped; /* (nullable) */
|
||||
}
|
||||
tests[] =
|
||||
{
|
||||
{ "%2Babc %4F", NULL, "+abc O" },
|
||||
{ "%2Babc %4F", "+", NULL },
|
||||
{ "%00abc %4F", "+/", NULL },
|
||||
{ "%0", NULL, NULL },
|
||||
{ "%ra", NULL, NULL },
|
||||
{ "%2r", NULL, NULL },
|
||||
{ NULL, NULL, NULL }, /* actually a valid test, not a delimiter */
|
||||
};
|
||||
gsize i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (tests); i++)
|
||||
{
|
||||
gchar *s = NULL;
|
||||
|
||||
g_test_message ("Test %" G_GSIZE_FORMAT ": %s", i, tests[i].escaped);
|
||||
|
||||
s = g_uri_unescape_string (tests[i].escaped, tests[i].illegal_characters);
|
||||
g_assert_cmpstr (s, ==, tests[i].expected_unescaped);
|
||||
g_free (s);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_uri_unescape_bytes (gconstpointer test_data)
|
||||
{
|
||||
gboolean use_nul_terminated = GPOINTER_TO_INT (test_data);
|
||||
const struct
|
||||
{
|
||||
/* Inputs */
|
||||
const gchar *escaped; /* (nullable) */
|
||||
/* Outputs */
|
||||
gssize expected_unescaped_len; /* -1 => error expected */
|
||||
const guint8 *expected_unescaped; /* (nullable) */
|
||||
}
|
||||
tests[] =
|
||||
{
|
||||
{ "%00%00", 2, (const guint8 *) "\x00\x00" },
|
||||
{ "%%", -1, NULL },
|
||||
{ "%", -1, NULL },
|
||||
};
|
||||
gsize i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (tests); i++)
|
||||
{
|
||||
gssize escaped_len = 0;
|
||||
gchar *escaped = NULL;
|
||||
GBytes *bytes = NULL;
|
||||
|
||||
g_test_message ("Test %" G_GSIZE_FORMAT ": %s", i, tests[i].escaped);
|
||||
|
||||
/* The tests get run twice: once with the length unspecified, using a
|
||||
* nul-terminated string; and once with the length specified and a copy of
|
||||
* the string with the trailing nul explicitly removed (to help catch
|
||||
* buffer overflows). */
|
||||
if (use_nul_terminated)
|
||||
{
|
||||
escaped_len = -1;
|
||||
escaped = g_strdup (tests[i].escaped);
|
||||
}
|
||||
else
|
||||
{
|
||||
escaped_len = strlen (tests[i].escaped); /* no trailing nul */
|
||||
escaped = g_memdup (tests[i].escaped, escaped_len);
|
||||
}
|
||||
|
||||
bytes = g_uri_unescape_bytes (escaped, escaped_len);
|
||||
|
||||
if (tests[i].expected_unescaped_len < 0)
|
||||
{
|
||||
g_assert_null (bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_cmpmem (g_bytes_get_data (bytes, NULL),
|
||||
g_bytes_get_size (bytes),
|
||||
tests[i].expected_unescaped,
|
||||
tests[i].expected_unescaped_len);
|
||||
}
|
||||
|
||||
g_clear_pointer (&bytes, g_bytes_unref);
|
||||
g_free (escaped);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_uri_unescape_segment (void)
|
||||
{
|
||||
const gchar *escaped_segment = "%2Babc %4F---";
|
||||
gchar *s = NULL;
|
||||
|
||||
s = g_uri_unescape_string ("%2Babc %4F", NULL);
|
||||
g_assert_cmpstr (s, ==, "+abc O");
|
||||
g_free (s);
|
||||
s = g_uri_unescape_segment (escaped_segment, escaped_segment + 10, NULL);
|
||||
g_assert_cmpstr (s, ==, "+abc O");
|
||||
g_free (s);
|
||||
g_assert_cmpstr (g_uri_unescape_string ("%2Babc %4F", "+"), ==, NULL);
|
||||
g_assert_cmpstr (g_uri_unescape_string ("%00abc %4F", "+/"), ==, NULL);
|
||||
g_assert_cmpstr (g_uri_unescape_string ("%0", NULL), ==, NULL);
|
||||
g_assert_cmpstr (g_uri_unescape_string ("%ra", NULL), ==, NULL);
|
||||
g_assert_cmpstr (g_uri_unescape_string ("%2r", NULL), ==, NULL);
|
||||
g_assert_cmpstr (g_uri_unescape_string (NULL, NULL), ==, NULL);
|
||||
|
||||
bytes = g_uri_unescape_bytes ("%00%00", -1);
|
||||
g_assert_cmpint (g_bytes_get_size (bytes), ==, 2);
|
||||
data = g_bytes_get_data (bytes, NULL);
|
||||
g_assert_cmpint (data[0], ==, 0);
|
||||
g_assert_cmpint (data[1], ==, 0);
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
bytes = g_uri_unescape_bytes ("%%", -1);
|
||||
g_assert_null (bytes);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1187,31 +1263,78 @@ test_uri_is_valid (void)
|
||||
}
|
||||
|
||||
static void
|
||||
test_uri_parse_params (void)
|
||||
test_uri_parse_params (gconstpointer test_data)
|
||||
{
|
||||
GHashTable *params;
|
||||
gboolean use_nul_terminated = GPOINTER_TO_INT (test_data);
|
||||
const struct
|
||||
{
|
||||
/* Inputs */
|
||||
const gchar *uri;
|
||||
gchar separator;
|
||||
gboolean case_insensitive;
|
||||
/* Outputs */
|
||||
gssize expected_n_params; /* -1 => error expected */
|
||||
/* key, value, key, value, …, limited to length 2*expected_n_params */
|
||||
const gchar *expected_param_key_values[4];
|
||||
}
|
||||
tests[] =
|
||||
{
|
||||
{ "", '&', FALSE, 0, { NULL, }},
|
||||
{ "p1=foo&p2=bar", '&', FALSE, 2, { "p1", "foo", "p2", "bar" }},
|
||||
{ "p1=foo&&P1=bar", '&', FALSE, -1, { NULL, }},
|
||||
{ "%00=foo", '&', FALSE, -1, { NULL, }},
|
||||
{ "p1=%00", '&', FALSE, -1, { NULL, }},
|
||||
{ "p1=foo&P1=bar", '&', TRUE, 1, { "p1", "bar", NULL, }},
|
||||
{ "=%", '&', FALSE, 1, { "", "%", NULL, }},
|
||||
};
|
||||
gsize i;
|
||||
|
||||
params = g_uri_parse_params ("", G_URI_FLAGS_NONE, '&', FALSE);
|
||||
g_assert_cmpint (g_hash_table_size (params), ==, 0);
|
||||
g_hash_table_unref (params);
|
||||
for (i = 0; i < G_N_ELEMENTS (tests); i++)
|
||||
{
|
||||
GHashTable *params;
|
||||
gchar *uri = NULL;
|
||||
gssize uri_len;
|
||||
|
||||
params = g_uri_parse_params ("p1=foo&p2=bar", -1, '&', FALSE);
|
||||
g_assert_cmpint (g_hash_table_size (params), ==, 2);
|
||||
g_assert_cmpstr (g_hash_table_lookup (params, "p1"), ==, "foo");
|
||||
g_assert_cmpstr (g_hash_table_lookup (params, "p2"), ==, "bar");
|
||||
g_hash_table_unref (params);
|
||||
g_test_message ("URI %" G_GSIZE_FORMAT ": %s", i, tests[i].uri);
|
||||
|
||||
params = g_uri_parse_params ("p1=foo&&P1=bar", -1, '&', FALSE);
|
||||
g_assert_null (params);
|
||||
params = g_uri_parse_params ("%00=foo", -1, '&', FALSE);
|
||||
g_assert_null (params);
|
||||
params = g_uri_parse_params ("p1=%00", -1, '&', FALSE);
|
||||
g_assert_null (params);
|
||||
g_assert (tests[i].expected_n_params < 0 ||
|
||||
tests[i].expected_n_params <= G_N_ELEMENTS (tests[i].expected_param_key_values) / 2);
|
||||
|
||||
params = g_uri_parse_params ("p1=foo&P1=bar", -1, '&', TRUE);
|
||||
g_assert_cmpint (g_hash_table_size (params), ==, 1);
|
||||
g_assert_cmpstr (g_hash_table_lookup (params, "p1"), ==, "bar");
|
||||
g_hash_table_unref (params);
|
||||
/* The tests get run twice: once with the length unspecified, using a
|
||||
* nul-terminated string; and once with the length specified and a copy of
|
||||
* the string with the trailing nul explicitly removed (to help catch
|
||||
* buffer overflows). */
|
||||
if (use_nul_terminated)
|
||||
{
|
||||
uri_len = -1;
|
||||
uri = g_strdup (tests[i].uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
uri_len = strlen (tests[i].uri); /* no trailing nul */
|
||||
uri = g_memdup (tests[i].uri, uri_len);
|
||||
}
|
||||
|
||||
params = g_uri_parse_params (uri, uri_len, tests[i].separator, tests[i].case_insensitive);
|
||||
|
||||
if (tests[i].expected_n_params < 0)
|
||||
{
|
||||
g_assert_null (params);
|
||||
}
|
||||
else
|
||||
{
|
||||
gsize j;
|
||||
|
||||
g_assert_cmpint (g_hash_table_size (params), ==, tests[i].expected_n_params);
|
||||
|
||||
for (j = 0; j < tests[i].expected_n_params; j += 2)
|
||||
g_assert_cmpstr (g_hash_table_lookup (params, tests[i].expected_param_key_values[j]), ==,
|
||||
tests[i].expected_param_key_values[j + 1]);
|
||||
}
|
||||
|
||||
g_clear_pointer (¶ms, g_hash_table_unref);
|
||||
g_free (uri);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1247,7 +1370,10 @@ main (int argc,
|
||||
g_test_add_func ("/uri/file-from-uri", run_file_from_uri_tests);
|
||||
g_test_add_func ("/uri/file-roundtrip", run_file_roundtrip_tests);
|
||||
g_test_add_func ("/uri/list", run_uri_list_tests);
|
||||
g_test_add_func ("/uri/unescape", test_uri_unescape);
|
||||
g_test_add_func ("/uri/unescape-string", test_uri_unescape_string);
|
||||
g_test_add_data_func ("/uri/unescape-bytes/nul-terminated", GINT_TO_POINTER (TRUE), test_uri_unescape_bytes);
|
||||
g_test_add_data_func ("/uri/unescape-bytes/length", GINT_TO_POINTER (FALSE), test_uri_unescape_bytes);
|
||||
g_test_add_func ("/uri/unescape-segment", test_uri_unescape_segment);
|
||||
g_test_add_func ("/uri/escape", test_uri_escape);
|
||||
g_test_add_func ("/uri/scheme", test_uri_scheme);
|
||||
g_test_add_func ("/uri/parsing/absolute", test_uri_parsing_absolute);
|
||||
@ -1257,7 +1383,8 @@ main (int argc,
|
||||
g_test_add_func ("/uri/is_valid", test_uri_is_valid);
|
||||
g_test_add_func ("/uri/to-string", test_uri_to_string);
|
||||
g_test_add_func ("/uri/join", test_uri_join);
|
||||
g_test_add_func ("/uri/parse-params", test_uri_parse_params);
|
||||
g_test_add_data_func ("/uri/parse-params/nul-terminated", GINT_TO_POINTER (TRUE), test_uri_parse_params);
|
||||
g_test_add_data_func ("/uri/parse-params/length", GINT_TO_POINTER (FALSE), test_uri_parse_params);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user