gkeyfile: Add a length argument to is_key_name()

This allows it to be called on a substring of a larger string, without
having to allocate a nul-terminated copy of the substring with
`g_strndup()` before knowing that the key name is actually valid.

This speeds up parsing of highly invalid key files, but doesn’t affect
performance in the normal case of a valid key file.

oss-fuzz#31796

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
This commit is contained in:
Philip Withnall 2021-03-14 13:47:26 +00:00
parent 46fe9639b9
commit 960030712d

View File

@ -575,7 +575,8 @@ static void g_key_file_add_key (GKeyFile
static void g_key_file_add_group (GKeyFile *key_file, static void g_key_file_add_group (GKeyFile *key_file,
const gchar *group_name); const gchar *group_name);
static gboolean g_key_file_is_group_name (const gchar *name); static gboolean g_key_file_is_group_name (const gchar *name);
static gboolean g_key_file_is_key_name (const gchar *name); static gboolean g_key_file_is_key_name (const gchar *name,
gsize len);
static void g_key_file_key_value_pair_free (GKeyFileKeyValuePair *pair); static void g_key_file_key_value_pair_free (GKeyFileKeyValuePair *pair);
static gboolean g_key_file_line_is_comment (const gchar *line); static gboolean g_key_file_line_is_comment (const gchar *line);
static gboolean g_key_file_line_is_group (const gchar *line); static gboolean g_key_file_line_is_group (const gchar *line);
@ -1380,17 +1381,16 @@ g_key_file_parse_key_value_pair (GKeyFile *key_file,
g_warn_if_fail (key_len <= length); g_warn_if_fail (key_len <= length);
key = g_strndup (line, key_len - 1); if (!g_key_file_is_key_name (line, key_len - 1))
if (!g_key_file_is_key_name (key))
{ {
g_set_error (error, G_KEY_FILE_ERROR, g_set_error (error, G_KEY_FILE_ERROR,
G_KEY_FILE_ERROR_PARSE, G_KEY_FILE_ERROR_PARSE,
_("Invalid key name: %s"), key); _("Invalid key name: %.*s"), (int) key_len - 1, line);
g_free (key);
return; return;
} }
key = g_strndup (line, key_len - 1);
/* Pull the value from the line (chugging leading whitespace) /* Pull the value from the line (chugging leading whitespace)
*/ */
while (g_ascii_isspace (*value_start)) while (g_ascii_isspace (*value_start))
@ -1877,7 +1877,7 @@ g_key_file_set_value (GKeyFile *key_file,
g_return_if_fail (key_file != NULL); g_return_if_fail (key_file != NULL);
g_return_if_fail (g_key_file_is_group_name (group_name)); g_return_if_fail (g_key_file_is_group_name (group_name));
g_return_if_fail (g_key_file_is_key_name (key)); g_return_if_fail (g_key_file_is_key_name (key, strlen (key)));
g_return_if_fail (value != NULL); g_return_if_fail (value != NULL);
group = g_key_file_lookup_group (key_file, group_name); group = g_key_file_lookup_group (key_file, group_name);
@ -4166,20 +4166,26 @@ g_key_file_is_group_name (const gchar *name)
} }
static gboolean static gboolean
g_key_file_is_key_name (const gchar *name) g_key_file_is_key_name (const gchar *name,
gsize len)
{ {
const gchar *p, *q; const gchar *p, *q, *end;
if (name == NULL) if (name == NULL)
return FALSE; return FALSE;
p = q = name; p = q = name;
end = name + len;
/* We accept a little more than the desktop entry spec says, /* We accept a little more than the desktop entry spec says,
* since gnome-vfs uses mime-types as keys in its cache. * since gnome-vfs uses mime-types as keys in its cache.
*/ */
while (*q && *q != '=' && *q != '[' && *q != ']') while (q < end && *q && *q != '=' && *q != '[' && *q != ']')
q = g_utf8_find_next_char (q, NULL); {
q = g_utf8_find_next_char (q, end);
if (q == NULL)
q = end;
}
/* No empty keys, please */ /* No empty keys, please */
if (q == p) if (q == p)
@ -4196,8 +4202,17 @@ g_key_file_is_key_name (const gchar *name)
if (*q == '[') if (*q == '[')
{ {
q++; q++;
while (*q && (g_unichar_isalnum (g_utf8_get_char_validated (q, -1)) || *q == '-' || *q == '_' || *q == '.' || *q == '@')) while (q < end &&
q = g_utf8_find_next_char (q, NULL); *q != '\0' &&
(g_unichar_isalnum (g_utf8_get_char_validated (q, end - q)) || *q == '-' || *q == '_' || *q == '.' || *q == '@'))
{
q = g_utf8_find_next_char (q, end);
if (q == NULL)
{
q = end;
break;
}
}
if (*q != ']') if (*q != ']')
return FALSE; return FALSE;
@ -4205,7 +4220,7 @@ g_key_file_is_key_name (const gchar *name)
q++; q++;
} }
if (*q != '\0') if (q < end)
return FALSE; return FALSE;
return TRUE; return TRUE;