diff --git a/fuzzing/fuzz_filename_from_uri.c b/fuzzing/fuzz_filename_from_uri.c new file mode 100644 index 000000000..9b7a715f0 --- /dev/null +++ b/fuzzing/fuzz_filename_from_uri.c @@ -0,0 +1,40 @@ +/* + * Copyright 2025 GNOME Foundation, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "fuzz.h" + +int +LLVMFuzzerTestOneInput (const unsigned char *data, size_t size) +{ + unsigned char *nul_terminated_data = NULL; + char *filename = NULL; + GError *local_error = NULL; + + fuzz_set_logging_func (); + + /* ignore @size (g_filename_from_uri() doesn’t support it); ensure @data is nul-terminated */ + nul_terminated_data = (unsigned char *) g_strndup ((const char *) data, size); + filename = g_filename_from_uri ((const char *) nul_terminated_data, NULL, &local_error); + g_free (nul_terminated_data); + + g_free (filename); + g_clear_error (&local_error); + + return 0; +} diff --git a/fuzzing/fuzz_filename_to_uri.c b/fuzzing/fuzz_filename_to_uri.c new file mode 100644 index 000000000..acb319203 --- /dev/null +++ b/fuzzing/fuzz_filename_to_uri.c @@ -0,0 +1,40 @@ +/* + * Copyright 2025 GNOME Foundation, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "fuzz.h" + +int +LLVMFuzzerTestOneInput (const unsigned char *data, size_t size) +{ + unsigned char *nul_terminated_data = NULL; + char *uri = NULL; + GError *local_error = NULL; + + fuzz_set_logging_func (); + + /* ignore @size (g_filename_to_uri() doesn’t support it); ensure @data is nul-terminated */ + nul_terminated_data = (unsigned char *) g_strndup ((const char *) data, size); + uri = g_filename_to_uri ((const char *) nul_terminated_data, NULL, &local_error); + g_free (nul_terminated_data); + + g_free (uri); + g_clear_error (&local_error); + + return 0; +} diff --git a/fuzzing/meson.build b/fuzzing/meson.build index addbe9071..05f936eeb 100644 --- a/fuzzing/meson.build +++ b/fuzzing/meson.build @@ -25,6 +25,8 @@ fuzz_targets = [ 'fuzz_date_parse', 'fuzz_date_time_new_from_iso8601', 'fuzz_dbus_message', + 'fuzz_filename_from_uri', + 'fuzz_filename_to_uri', 'fuzz_get_locale_variants', 'fuzz_inet_address_mask_new_from_string', 'fuzz_inet_address_new_from_string', diff --git a/glib/gconvert.c b/glib/gconvert.c index 7ad8ca018..367e9b466 100644 --- a/glib/gconvert.c +++ b/glib/gconvert.c @@ -1336,8 +1336,9 @@ static const gchar hex[] = "0123456789ABCDEF"; /* Note: This escape function works on file: URIs, but if you want to * escape something else, please read RFC-2396 */ static gchar * -g_escape_uri_string (const gchar *string, - UnsafeCharacterSet mask) +g_escape_uri_string (const gchar *string, + UnsafeCharacterSet mask, + GError **error) { #define ACCEPTABLE(a) ((a)>=32 && (a)<128 && (acceptable[(a)-32] & use_mask)) @@ -1345,7 +1346,7 @@ g_escape_uri_string (const gchar *string, gchar *q; gchar *result; int c; - gint unacceptable; + size_t unacceptable; UnsafeCharacterSet use_mask; g_return_val_if_fail (mask == UNSAFE_ALL @@ -1362,7 +1363,14 @@ g_escape_uri_string (const gchar *string, if (!ACCEPTABLE (c)) unacceptable++; } - + + if (unacceptable >= (G_MAXSIZE - (p - string)) / 2) + { + g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI, + _("Invalid hostname")); + return NULL; + } + result = g_malloc (p - string + unacceptable * 2 + 1); use_mask = mask; @@ -1387,12 +1395,13 @@ g_escape_uri_string (const gchar *string, static gchar * -g_escape_file_uri (const gchar *hostname, - const gchar *pathname) +g_escape_file_uri (const gchar *hostname, + const gchar *pathname, + GError **error) { char *escaped_hostname = NULL; - char *escaped_path; - char *res; + char *escaped_path = NULL; + char *res = NULL; #ifdef G_OS_WIN32 char *p, *backslash; @@ -1413,10 +1422,14 @@ g_escape_file_uri (const gchar *hostname, if (hostname && *hostname != '\0') { - escaped_hostname = g_escape_uri_string (hostname, UNSAFE_HOST); + escaped_hostname = g_escape_uri_string (hostname, UNSAFE_HOST, error); + if (escaped_hostname == NULL) + goto out; } - escaped_path = g_escape_uri_string (pathname, UNSAFE_PATH); + escaped_path = g_escape_uri_string (pathname, UNSAFE_PATH, error); + if (escaped_path == NULL) + goto out; res = g_strconcat ("file://", (escaped_hostname) ? escaped_hostname : "", @@ -1424,6 +1437,7 @@ g_escape_file_uri (const gchar *hostname, escaped_path, NULL); +out: #ifdef G_OS_WIN32 g_free ((char *) pathname); #endif @@ -1757,7 +1771,7 @@ g_filename_to_uri (const gchar *filename, hostname = NULL; #endif - escaped_uri = g_escape_file_uri (hostname, filename); + escaped_uri = g_escape_file_uri (hostname, filename, error); return escaped_uri; }