diff options
author | dequis <dx@dxzone.com.ar> | 2015-10-23 04:25:57 -0300 |
---|---|---|
committer | dequis <dx@dxzone.com.ar> | 2015-10-23 05:12:04 -0300 |
commit | ed28483e7509f0a7a75716f2651f603824fd9817 (patch) | |
tree | 5653e73cb5e766227e5c46855690596f3b7bac55 /src/core | |
parent | 7ab38f29edb59941281723d7031d41d93fae06bd (diff) | |
download | irssi-ed28483e7509f0a7a75716f2651f603824fd9817.zip |
Fix invalid reads in strsplit_len when splitting on spaces
The symptom for this one is randomly getting lines split before the last
word, even if there's no need for splitting. Also, this function is only
reached if recode is on, and iconv failed (for example, due to an
incorrect source charset). Thanks to vague for finding this and
providing valgrind logs.
The loop that looks for spaces tried to read backwards from the end of
the current line, with the end being determined by len. Assuming
strsplit_len() with len=400, this meant accessing str[399] in the first
iteration. For strings that don't need splitting, this means an invalid
read always.
If that invalid read happens to hit garbage that has a space character,
(len - offset) points after the end of string, which isn't a problem for
g_strndup() since it stops at the first null, and no splitting happens.
If the garbage doesn't have any spaces, it splits by the last word.
This commit avoids that loop entirely if (remaining_len > len). It also
changes the way it iterates over the string to be much less confusing.
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/misc.c | 20 |
1 files changed, 12 insertions, 8 deletions
diff --git a/src/core/misc.c b/src/core/misc.c index 88c27255..e209efa1 100644 --- a/src/core/misc.c +++ b/src/core/misc.c @@ -995,25 +995,29 @@ char **strsplit_len(const char *str, int len, gboolean onspace) { char **ret = g_new(char *, 1); int n; - int offset; + int split_offset = 0; + size_t remaining_len = strlen(str); - for (n = 0; *str != '\0'; n++, str += MIN(len - offset, strlen(str))) { - offset = 0; - if (onspace) { + for (n = 0; *str != '\0'; n++) { + split_offset = MIN(len, remaining_len); + if (onspace && remaining_len > len) { /* * Try to find a space to split on and leave * the space on the previous line. */ int i; - for (i = 0; i < len; i++) { - if (str[len-1-i] == ' ') { - offset = i; + for (i = len - 1; i > 0; i--) { + if (str[i] == ' ') { + split_offset = i; break; } } } - ret[n] = g_strndup(str, len - offset); + ret[n] = g_strndup(str, split_offset); ret = g_renew(char *, ret, n + 2); + + str += split_offset; + remaining_len -= split_offset; } ret[n] = NULL; |