1
0
mirror of https://github.com/weechat/weechat.git synced 2026-06-12 14:14:48 +02:00

core: fix buffer overflow in function utf8_next_char and return NULL for empty string

Now the function utf8_next_char with an empty string returns NULL instead of
the next char, which is most of the time after an allocated buffer.

And the function utf8_char_size with an empty string now returns 0 instead of
1.

This indirectly fixes a buffer overflow in function eval_string_range_chars
when the input string is empty (for example when doing `/eval -n ${chars:}`).
This commit is contained in:
Sébastien Helleu
2025-05-10 20:40:09 +02:00
parent 6ecd9e66bf
commit d475c16671
12 changed files with 126 additions and 48 deletions
+1
View File
@@ -10,6 +10,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
### Changed
- **breaking:** core: fix buffer overflow in function utf8_next_char and return NULL for empty string
- **breaking:** core: fix integer overflow and return "unsigned long" in function util_version_number
- core: write configuration files on disk only if there are changes ([#2250](https://github.com/weechat/weechat/issues/2250))
+11 -1
View File
@@ -254,6 +254,8 @@ string_reverse (const char *string)
while (ptr_string && ptr_string[0])
{
char_size = utf8_char_size (ptr_string);
if (char_size < 1)
break;
ptr_result -= char_size;
memcpy (ptr_result, ptr_string, char_size);
@@ -313,6 +315,8 @@ string_reverse_screen (const char *string)
if (ptr_string[0])
{
char_size = utf8_char_size (ptr_string);
if (char_size < 1)
break;
ptr_result -= char_size;
memcpy (ptr_result, ptr_string, char_size);
@@ -934,7 +938,7 @@ string_strcasestr (const char *string, const char *search)
if (!string || !search || (length_search == 0))
return NULL;
while (string[0])
while (string && string[0])
{
if (string_strncasecmp (string, search, length_search) == 0)
return (char *)string;
@@ -4139,6 +4143,8 @@ string_input_for_buffer (const char *string)
return string;
next_char = utf8_next_char (string);
if (!next_char)
return NULL;
/* next char is a space, then it's not a command */
if (next_char[0] == ' ')
@@ -4226,8 +4232,12 @@ string_levenshtein (const char *string1, const char *string2,
last_diag + ((char1 == char2) ? 0 : 1));
last_diag = old_diag;
ptr_str1 = utf8_next_char (ptr_str1);
if (!ptr_str1)
break;
}
ptr_str2 = utf8_next_char (ptr_str2);
if (!ptr_str2)
break;
}
return column[length1];
+10 -4
View File
@@ -242,7 +242,7 @@ utf8_prev_char (const char *string_start, const char *string)
const char *
utf8_next_char (const char *string)
{
if (!string)
if (!string || !string[0])
return NULL;
/* UTF-8, 2 bytes: 110vvvvv 10vvvvvv */
@@ -311,7 +311,7 @@ utf8_end_of_line (const char *string)
if (!string)
return NULL;
while (string[0] && (string[0] != '\n'))
while (string && string[0] && (string[0] != '\n'))
{
string = utf8_next_char (string);
}
@@ -451,10 +451,16 @@ utf8_int_string (unsigned int unicode_value, char *string)
int
utf8_char_size (const char *string)
{
if (!string)
const char *ptr_next;
if (!string || !string[0])
return 0;
return utf8_next_char (string) - string;
ptr_next = utf8_next_char (string);
if (!ptr_next)
return 0;
return ptr_next - string;
}
/*
+1 -1
View File
@@ -115,7 +115,7 @@ hook_command_run_exec (struct t_gui_buffer *buffer, const char *command)
if (command[0] != '/')
{
ptr_string = utf8_next_char (command);
if (string_asprintf (&command2, "/%s", ptr_string) < 0)
if (string_asprintf (&command2, "/%s", (ptr_string) ? ptr_string : "") < 0)
return WEECHAT_RC_ERROR;
}
else
+14 -7
View File
@@ -323,9 +323,11 @@ gui_completion_nick_has_ignored_chars (const char *string)
int char_size;
char utf_char[16];
while (string[0])
while (string && string[0])
{
char_size = utf8_char_size (string);
if (char_size < 1)
break;
memcpy (utf_char, string, char_size);
utf_char[char_size] = '\0';
@@ -352,9 +354,11 @@ gui_completion_nick_strdup_ignore_chars (const char *string)
result = malloc (strlen (string) + 1);
pos = result;
while (string[0])
while (string && string[0])
{
char_size = utf8_char_size (string);
if (char_size < 1)
break;
memcpy (utf_char, string, char_size);
utf_char[char_size] = '\0';
@@ -915,13 +919,16 @@ gui_completion_find_context (struct t_gui_completion *completion,
if (string_is_command_char (ptr_data))
{
ptr_data = utf8_next_char (ptr_data);
if (ptr_data < data + pos)
if (ptr_data)
{
if (string_is_command_char (ptr_data))
ptr_data = utf8_next_char (ptr_data);
if (ptr_data < data + pos)
{
if (string_is_command_char (ptr_data))
ptr_data = utf8_next_char (ptr_data);
}
if (!string_is_command_char (ptr_data))
ptr_command = ptr_data;
}
if (!string_is_command_char (ptr_data))
ptr_command = ptr_data;
}
/*
+64 -19
View File
@@ -871,10 +871,14 @@ gui_input_delete_next_char (struct t_gui_buffer *buffer)
return;
}
gui_buffer_undo_snap (buffer);
pos = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
pos_next = (char *)utf8_next_char (pos);
if (!pos_next)
return;
gui_buffer_undo_snap (buffer);
char_size = pos_next - pos;
size_to_move = strlen (pos_next);
memmove (pos, pos_next, size_to_move);
@@ -899,9 +903,13 @@ gui_input_delete_range (struct t_gui_buffer *buffer,
char *start,
char *end)
{
const char *ptr_next;
int size_deleted, length_deleted;
size_deleted = utf8_next_char (end) - start;
ptr_next = utf8_next_char (end);
if (!ptr_next)
return;
size_deleted = ptr_next - start;
length_deleted = utf8_strnlen (start, size_deleted);
gui_input_clipboard_copy (start, size_deleted);
@@ -956,7 +964,8 @@ gui_input_delete_previous_word (struct t_gui_buffer *buffer)
else
string = buffer->input_buffer;
gui_input_delete_range (buffer, string, start);
if (string)
gui_input_delete_range (buffer, string, start);
}
/*
@@ -994,7 +1003,8 @@ gui_input_delete_previous_word_whitespace (struct t_gui_buffer *buffer)
else
string = buffer->input_buffer;
gui_input_delete_range (buffer, string, start);
if (string)
gui_input_delete_range (buffer, string, start);
}
/*
@@ -1016,13 +1026,13 @@ gui_input_delete_next_word (struct t_gui_buffer *buffer)
string = start;
length_deleted = 0;
/* move to the right until we reach a word char */
while (string[0] && !string_is_word_char_input (string))
while (string && string[0] && !string_is_word_char_input (string))
{
string = (char *)utf8_next_char (string);
length_deleted++;
}
/* move to the right to skip the whole word */
while (string[0] && string_is_word_char_input (string))
while (string && string[0] && string_is_word_char_input (string))
{
string = (char *)utf8_next_char (string);
length_deleted++;
@@ -1060,12 +1070,14 @@ gui_input_delete_beginning_of_line (struct t_gui_buffer *buffer)
if (!buffer->input || (buffer->input_buffer_pos <= 0))
return;
gui_buffer_undo_snap (buffer);
start = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
beginning_of_line = (char *)utf8_beginning_of_line (buffer->input_buffer,
start);
if (!beginning_of_line)
return;
if (beginning_of_line == start)
{
beginning_of_line = (char *)utf8_prev_char (buffer->input_buffer,
@@ -1074,6 +1086,11 @@ gui_input_delete_beginning_of_line (struct t_gui_buffer *buffer)
beginning_of_line);
}
if (!beginning_of_line)
return;
gui_buffer_undo_snap (buffer);
size_deleted = start - beginning_of_line;
length_deleted = utf8_strnlen (beginning_of_line, size_deleted);
gui_input_clipboard_copy (beginning_of_line, size_deleted);
@@ -1109,7 +1126,6 @@ gui_input_delete_end_of_line (struct t_gui_buffer *buffer)
if (!buffer->input)
return;
gui_buffer_undo_snap (buffer);
start = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
@@ -1117,7 +1133,14 @@ gui_input_delete_end_of_line (struct t_gui_buffer *buffer)
end_of_line = (char *)utf8_next_char (start);
else
end_of_line = start;
if (!end_of_line)
return;
end_of_line = (char *)utf8_end_of_line (end_of_line);
if (!end_of_line)
return;
gui_buffer_undo_snap (buffer);
size_deleted = end_of_line - start;
length_deleted = utf8_strnlen (start, size_deleted);
@@ -1215,7 +1238,6 @@ gui_input_delete_line (struct t_gui_buffer *buffer)
if (!buffer->input)
return;
gui_buffer_undo_snap (buffer);
start = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
@@ -1223,6 +1245,11 @@ gui_input_delete_line (struct t_gui_buffer *buffer)
start);
end_of_line = (char *)utf8_end_of_line (start);
if (!beginning_of_line || !end_of_line)
return;
gui_buffer_undo_snap (buffer);
size_deleted = end_of_line - beginning_of_line;
length_deleted = utf8_strnlen (beginning_of_line, size_deleted);
@@ -1321,12 +1348,16 @@ gui_input_move_beginning_of_line (struct t_gui_buffer *buffer)
buffer->input_buffer_pos);
original_pos = pos;
pos = (char *)utf8_beginning_of_line (buffer->input_buffer, pos);
if (!pos)
return;
if (pos == original_pos)
{
pos = (char *)utf8_prev_char (buffer->input_buffer, pos);
pos = (char *)utf8_beginning_of_line (buffer->input_buffer, pos);
}
if (!pos)
return;
buffer->input_buffer_pos = utf8_pos (buffer->input_buffer,
pos - buffer->input_buffer);
@@ -1354,11 +1385,9 @@ gui_input_move_end_of_line (struct t_gui_buffer *buffer)
pos = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
if (pos[0] && pos[0] == '\n')
{
pos = (char *)utf8_next_char (pos);
}
pos = (char *)utf8_end_of_line (pos);
if (pos[0])
if (pos && pos[0])
{
buffer->input_buffer_pos = utf8_pos (buffer->input_buffer,
pos - buffer->input_buffer);
@@ -1462,6 +1491,8 @@ gui_input_move_previous_word (struct t_gui_buffer *buffer)
pos = (char *)utf8_next_char (pos);
else
pos = buffer->input_buffer;
if (!pos)
return;
buffer->input_buffer_pos = utf8_pos (buffer->input_buffer,
pos - buffer->input_buffer);
}
@@ -1489,17 +1520,17 @@ gui_input_move_next_word (struct t_gui_buffer *buffer)
pos = (char *)utf8_add_offset (buffer->input_buffer,
buffer->input_buffer_pos);
while (pos[0] && !string_is_word_char_input (pos))
while (pos && pos[0] && !string_is_word_char_input (pos))
{
pos = (char *)utf8_next_char (pos);
}
if (pos[0])
if (pos && pos[0])
{
while (pos[0] && string_is_word_char_input (pos))
{
pos = (char *)utf8_next_char (pos);
}
if (pos[0])
if (pos && pos[0])
{
buffer->input_buffer_pos =
utf8_pos (buffer->input_buffer,
@@ -1531,6 +1562,9 @@ gui_input_move_previous_line (struct t_gui_buffer *buffer)
buffer->input_buffer_pos);
pos = (char *)utf8_beginning_of_line (buffer->input_buffer, pos);
if (!pos)
return;
if (pos != buffer->input_buffer)
{
beginning_of_line_pos = utf8_pos (buffer->input_buffer,
@@ -1540,13 +1574,19 @@ gui_input_move_previous_line (struct t_gui_buffer *buffer)
pos = (char *)utf8_prev_char (buffer->input_buffer, pos);
pos = (char *)utf8_beginning_of_line (buffer->input_buffer, pos);
if (!pos)
return;
for (i = 0;
pos[0] && (pos[0] != '\n') && (i < length_from_beginning);
pos && pos[0] && (pos[0] != '\n') && (i < length_from_beginning);
i++)
{
pos = (char *)utf8_next_char (pos);
}
if (!pos)
return;
buffer->input_buffer_pos = utf8_pos (buffer->input_buffer,
pos - buffer->input_buffer);
@@ -1574,22 +1614,27 @@ gui_input_move_next_line (struct t_gui_buffer *buffer)
buffer->input_buffer_pos);
pos = (char *)utf8_beginning_of_line (buffer->input_buffer, pos);
if (!pos)
return;
beginning_of_line_pos = utf8_pos (buffer->input_buffer,
pos - buffer->input_buffer);
length_from_beginning = buffer->input_buffer_pos - beginning_of_line_pos;
pos = (char *)utf8_end_of_line (pos);
if (pos[0])
if (pos && pos[0])
{
pos = (char *)utf8_next_char (pos);
for (i = 0;
pos[0] && (pos[0] != '\n') && (i < length_from_beginning);
pos && pos[0] && (pos[0] != '\n') && (i < length_from_beginning);
i++)
{
pos = (char *)utf8_next_char (pos);
}
if (!pos)
return;
buffer->input_buffer_pos = utf8_pos (buffer->input_buffer,
pos - buffer->input_buffer);
+1 -1
View File
@@ -1328,7 +1328,7 @@ gui_key_chunk_seems_valid (const char *chunk)
if (!found)
chunk = utf8_next_char (chunk);
if (chunk[0])
if (chunk && chunk[0])
return 0;
return 1;
+15 -6
View File
@@ -420,6 +420,7 @@ void
alias_hook_command (struct t_alias *alias)
{
char *str_priority_name, *str_completion;
const char *ptr_command;
if (alias->hook)
{
@@ -442,11 +443,10 @@ alias_hook_command (struct t_alias *alias)
str_completion = NULL;
if (!alias->completion)
{
weechat_asprintf (
&str_completion,
"%%%%%s",
(weechat_string_is_command_char (alias->command)) ?
weechat_utf8_next_char (alias->command) : alias->command);
ptr_command = (weechat_string_is_command_char (alias->command)) ?
weechat_utf8_next_char (alias->command) : alias->command;
weechat_asprintf (&str_completion, "%%%%%s",
(ptr_command) ? ptr_command : "");
}
alias->hook = weechat_hook_command (
@@ -681,11 +681,20 @@ alias_new (const char *name, const char *command, const char *completion)
if (!command || !command[0])
return NULL;
while (weechat_string_is_command_char (name))
while (name && weechat_string_is_command_char (name))
{
name = weechat_utf8_next_char (name);
}
if (!name || !name[0])
{
weechat_printf (NULL,
_("%s%s: invalid alias name: \"%s\""),
weechat_prefix ("error"), ALIAS_PLUGIN_NAME,
"");
return NULL;
}
ptr_alias = alias_search (name);
alias_free (ptr_alias);
+2 -2
View File
@@ -1079,12 +1079,12 @@ irc_message_split_string (struct t_irc_message_split_context *context,
pos = arguments;
pos_max = pos + max_length;
pos_last_delim = NULL;
while (pos[0])
while (pos && pos[0])
{
if (pos[0] == delimiter)
pos_last_delim = pos;
pos_next = weechat_utf8_next_char (pos);
if (pos_next > pos_max)
if (!pos_next || (pos_next > pos_max))
break;
pos = pos_next;
}
+3 -3
View File
@@ -834,7 +834,7 @@ spell_modifier_cb (const void *pointer, void *data,
}
current_pos = 0;
while (ptr_string[0])
while (ptr_string && ptr_string[0])
{
ptr_string_orig = NULL;
@@ -885,7 +885,7 @@ spell_modifier_cb (const void *pointer, void *data,
word_end_pos_valid = word_end_pos;
}
ptr_end = (char *)weechat_utf8_next_char (ptr_end);
if (!ptr_end[0])
if (!ptr_end || !ptr_end[0])
break;
code_point = weechat_utf8_char_int (ptr_end);
}
@@ -906,7 +906,7 @@ spell_modifier_cb (const void *pointer, void *data,
while (!iswspace (code_point))
{
ptr_end = (char *)weechat_utf8_next_char (ptr_end);
if (!ptr_end[0])
if (!ptr_end || !ptr_end[0])
break;
code_point = weechat_utf8_char_int (ptr_end);
}
+2 -2
View File
@@ -700,11 +700,11 @@ trigger_regex_split (const char *str_regex,
/* search the delimiter (which can be more than one char) */
pos = weechat_utf8_next_char (ptr_regex);
while (pos[0] && (weechat_string_charcmp (ptr_regex, pos) == 0))
while (pos && pos[0] && (weechat_string_charcmp (ptr_regex, pos) == 0))
{
pos = weechat_utf8_next_char (pos);
}
if (!pos[0])
if (!pos || !pos[0])
goto format_error;
delimiter = weechat_strndup (ptr_regex, pos - ptr_regex);
if (!delimiter)
+2 -2
View File
@@ -351,7 +351,7 @@ TEST(CoreUtf8, Move)
STRCMP_EQUAL(NULL, utf8_prev_char (NULL, NULL));
STRCMP_EQUAL(NULL, utf8_next_char (NULL));
STRCMP_EQUAL(NULL, utf8_prev_char (empty_string, empty_string));
STRCMP_EQUAL(empty_string + 1, utf8_next_char (empty_string));
STRCMP_EQUAL(NULL, utf8_next_char (empty_string));
STRCMP_EQUAL(NULL, utf8_prev_char (noel_valid + 1, noel_valid));
ptr = utf8_next_char (noel_valid);
STRCMP_EQUAL("oël", ptr);
@@ -512,7 +512,7 @@ TEST(CoreUtf8, Size)
{
/* char size (in bytes) */
LONGS_EQUAL(0, utf8_char_size (NULL));
LONGS_EQUAL(1, utf8_char_size (""));
LONGS_EQUAL(0, utf8_char_size (""));
LONGS_EQUAL(1, utf8_char_size ("A"));
LONGS_EQUAL(2, utf8_char_size ("ë"));
LONGS_EQUAL(3, utf8_char_size (""));