Skip to content

Commit 6efb409

Browse files
committed
maybe_escape_markup: Make function memory-safe
This fixes #492 and an additional buffer overflow that can happen when pango markup is enabled. Using config ``` general { output_format = "none" markup = "pango" } order += "wireless _first_" wireless _first_ { format_up = "W: (%quality at %essid, %bitrate) %ip" } ``` and renaming my phone's hotspot to `Hello world &<<<<<<hello world>>` i3status will throw an AddressSanitizer error: ``` ==1373240==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7411d720923e at pc 0x7411daa7cee9 bp 0x7ffdae6ce070 sp 0x7ffdae6cd800 WRITE of size 5 at 0x7411d720923e thread T0 #0 0x7411daa7cee8 in __interceptor_vsprintf /usr/src/debug/gcc/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1765 #1 0x7411daa7d0ff in __interceptor_sprintf /usr/src/debug/gcc/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1808 #2 0x653b2764cdaf in maybe_escape_markup ../src/output.c:102 #3 0x653b27677df9 in print_wireless_info ../src/print_wireless_info.c:607 #4 0x653b27640bf1 in main ../i3status.c:709 #5 0x7411da641ccf (/usr/lib/libc.so.6+0x25ccf) (BuildId: 6542915cee3354fbcf2b3ac5542201faec43b5c9) #6 0x7411da641d89 in __libc_start_main (/usr/lib/libc.so.6+0x25d89) (BuildId: 6542915cee3354fbcf2b3ac5542201faec43b5c9) #7 0x653b27633f24 in _start (/tmp/xx/i3status/build/i3status+0x4ff24) (BuildId: c737ce6288265fa02a7617c66f51ddd16b5a8275) Address 0x7411d720923e is located in stack of thread T0 at offset 574 in frame #0 0x653b276750ed in print_wireless_info ../src/print_wireless_info.c:513 This frame has 10 object(s): [48, 56) 'tmp' (line 604) [80, 168) 'info' (line 516) [208, 320) 'placeholders' (line 623) [352, 382) 'string_quality' (line 569) [416, 446) 'string_signal' (line 570) [480, 510) 'string_noise' (line 571) [544, 574) 'string_essid' (line 572) <== Memory access at offset 574 overflows this variable [608, 638) 'string_frequency' (line 573) [672, 702) 'string_ip' (line 574) [736, 766) 'string_bitrate' (line 575) ``` With pango disabled, the error is thrown elsewhere (#492): ``` ==1366779==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7bab43a0923e at pc 0x7bab4727cee9 bp 0x7ffc289d2540 sp 0x7ffc289d1cd0 WRITE of size 33 at 0x7bab43a0923e thread T0 #0 0x7bab4727cee8 in __interceptor_vsprintf /usr/src/debug/gcc/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1765 #1 0x7bab4727d0ff in __interceptor_sprintf /usr/src/debug/gcc/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1808 #2 0x5dd180858aa4 in maybe_escape_markup ../src/output.c:93 #3 0x5dd180883df9 in print_wireless_info ../src/print_wireless_info.c:607 #4 0x5dd18084cbf1 in main ../i3status.c:709 #5 0x7bab46843ccf (/usr/lib/libc.so.6+0x25ccf) (BuildId: 6542915cee3354fbcf2b3ac5542201faec43b5c9) #6 0x7bab46843d89 in __libc_start_main (/usr/lib/libc.so.6+0x25d89) (BuildId: 6542915cee3354fbcf2b3ac5542201faec43b5c9) #7 0x5dd18083ff24 in _start (/tmp/xx/i3status/build/i3status+0x4ff24) (BuildId: c737ce6288265fa02a7617c66f51ddd16b5a8275) Address 0x7bab43a0923e is located in stack of thread T0 at offset 574 in frame #0 0x5dd1808810ed in print_wireless_info ../src/print_wireless_info.c:513 This frame has 10 object(s): [48, 56) 'tmp' (line 604) [80, 168) 'info' (line 516) [208, 320) 'placeholders' (line 623) [352, 382) 'string_quality' (line 569) [416, 446) 'string_signal' (line 570) [480, 510) 'string_noise' (line 571) [544, 574) 'string_essid' (line 572) <== Memory access at offset 574 overflows this variable [608, 638) 'string_frequency' (line 573) [672, 702) 'string_ip' (line 574) [736, 766) 'string_bitrate' (line 575) ``` With the patch output is correct: ``` W: ( 72% at Hello world &amp;&lt;&lt;&lt;&lt;&lt;&lt;hello world&gt;&gt;, 1,2009 Gb/s) 192.168.26.237 ``` and ``` W: ( 73% at Hello world &<<<<<<hello world>>, 1,1342 Gb/s) 192.168.26.237 ``` The patch changes the maybe_escape_markup function to use dynamic allocation instead of a static buffer. Confusing pointer arithmetic is replaced with index-based memory access. The `buffer` pointer does not move around except for `realloc`ations. Fixes #492 Closes #525 (alternative PR)
1 parent c07b9ca commit 6efb409

File tree

3 files changed

+32
-22
lines changed

3 files changed

+32
-22
lines changed

include/i3status.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ void print_separator(const char *separator);
204204
char *color(const char *colorstr);
205205
char *endcolor() __attribute__((pure));
206206
void reset_cursor(void);
207-
void maybe_escape_markup(char *text, char **buffer);
207+
char *maybe_escape_markup(char *text);
208208

209209
char *rtrim(const char *s);
210210
char *ltrim(const char *s);

src/output.c

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,39 +88,49 @@ void reset_cursor(void) {
8888
* https://git.gnome.org/browse/glib/tree/glib/gmarkup.c?id=03db1f455b4265654e237d2ad55464b4113cba8a#n2142
8989
*
9090
*/
91-
void maybe_escape_markup(char *text, char **buffer) {
91+
char *maybe_escape_markup(char *text) {
9292
if (markup_format == M_NONE) {
93-
*buffer += sprintf(*buffer, "%s", text);
94-
return;
93+
return strdup(text);
9594
}
95+
96+
size_t idx = 0;
97+
size_t size = 32;
98+
char *buffer = malloc(size);
9699
for (; *text != '\0'; text++) {
100+
if (idx + 10 > size) {
101+
size *= 2;
102+
buffer = realloc(buffer, size);
103+
}
97104
switch (*text) {
98105
case '&':
99-
*buffer += sprintf(*buffer, "%s", "&amp;");
106+
idx += sprintf(&buffer[idx], "%s", "&amp;");
100107
break;
101108
case '<':
102-
*buffer += sprintf(*buffer, "%s", "&lt;");
109+
idx += sprintf(&buffer[idx], "%s", "&lt;");
103110
break;
104111
case '>':
105-
*buffer += sprintf(*buffer, "%s", "&gt;");
112+
idx += sprintf(&buffer[idx], "%s", "&gt;");
106113
break;
107114
case '\'':
108-
*buffer += sprintf(*buffer, "%s", "&apos;");
115+
idx += sprintf(&buffer[idx], "%s", "&apos;");
109116
break;
110117
case '"':
111-
*buffer += sprintf(*buffer, "%s", "&quot;");
118+
idx += sprintf(&buffer[idx], "%s", "&quot;");
112119
break;
113120
default:
114121
if ((0x1 <= *text && *text <= 0x8) ||
115122
(0xb <= *text && *text <= 0xc) ||
116123
(0xe <= *text && *text <= 0x1f)) {
117-
*buffer += sprintf(*buffer, "&#x%x;", *text);
124+
idx += sprintf(&buffer[idx], "&#x%x;", *text);
118125
} else {
119-
*(*buffer)++ = *text;
126+
buffer[idx] = *text;
127+
idx++;
120128
}
121129
break;
122130
}
123131
}
132+
buffer[idx] = 0;
133+
return buffer;
124134
}
125135

126136
/*
@@ -154,4 +164,4 @@ char *trim(const char *s) {
154164
char *f = ltrim(r);
155165
free(r);
156166
return f;
157-
}
167+
}

src/print_wireless_info.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -402,9 +402,9 @@ static int get_wireless_info(const char *interface, wireless_info_t *info) {
402402
close(s);
403403
if (na.i_len >= sizeof(u.req)) {
404404
/*
405-
* Just use the first BSSID returned even if there are
406-
* multiple APs sharing the same BSSID.
407-
*/
405+
* Just use the first BSSID returned even if there are
406+
* multiple APs sharing the same BSSID.
407+
*/
408408
info->signal_level = u.req.info[0].isi_rssi / 2 +
409409
u.req.info[0].isi_noise;
410410
info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
@@ -523,7 +523,7 @@ void print_wireless_info(wireless_info_ctx_t *ctx) {
523523
/*
524524
* Removing '%' and following characters from IPv6 since the interface identifier is redundant,
525525
* as the output already includes the interface name.
526-
*/
526+
*/
527527
if (ipv6_address != NULL) {
528528
char *prct_ptr = strstr(ipv6_address, "%");
529529
if (prct_ptr != NULL) {
@@ -569,7 +569,6 @@ void print_wireless_info(wireless_info_ctx_t *ctx) {
569569
char string_quality[STRING_SIZE] = {'\0'};
570570
char string_signal[STRING_SIZE] = {'\0'};
571571
char string_noise[STRING_SIZE] = {'\0'};
572-
char string_essid[STRING_SIZE] = {'\0'};
573572
char string_frequency[STRING_SIZE] = {'\0'};
574573
char string_ip[STRING_SIZE] = {'\0'};
575574
char string_bitrate[STRING_SIZE] = {'\0'};
@@ -601,13 +600,13 @@ void print_wireless_info(wireless_info_ctx_t *ctx) {
601600
snprintf(string_noise, STRING_SIZE, "?");
602601
}
603602

604-
char *tmp = string_essid;
603+
char *string_essid_tmp = NULL; /* Dynamic allocation of ESSID */
604+
char *string_essid = "?";
605605
#ifdef IW_ESSID_MAX_SIZE
606-
if (info.flags & WIRELESS_INFO_FLAG_HAS_ESSID)
607-
maybe_escape_markup(info.essid, &tmp);
608-
else
606+
if (info.flags & WIRELESS_INFO_FLAG_HAS_ESSID) {
607+
string_essid = string_essid_tmp = maybe_escape_markup(info.essid);
608+
}
609609
#endif
610-
snprintf(string_essid, STRING_SIZE, "?");
611610

612611
if (info.flags & WIRELESS_INFO_FLAG_HAS_FREQUENCY)
613612
snprintf(string_frequency, STRING_SIZE, "%1.1f GHz", info.frequency / 1e9);
@@ -633,6 +632,7 @@ void print_wireless_info(wireless_info_ctx_t *ctx) {
633632
char *formatted = format_placeholders(walk, &placeholders[0], num);
634633
OUTPUT_FORMATTED;
635634
free(formatted);
635+
free(string_essid_tmp);
636636

637637
END_COLOR;
638638
free(ipv4_address);

0 commit comments

Comments
 (0)