Skip to content

Commit ede8a02

Browse files
committed
py/vstr: Raise a RuntimeError if fixed vstr buffer overflows.
Current users of fixed vstr buffers (building file paths) assume that there is no overflow and do not check for overflow after building the vstr. This has the potential to lead to NULL pointer dereferences (when vstr_null_terminated_str returns NULL because it can't allocate RAM for the terminating byte) and stat'ing and loading invalid path names (due to the path being truncated). The safest and simplest thing to do in these cases is just raise an exception if a write goes beyond the end of a fixed vstr buffer, which is what this patch does. It also simplifies the vstr code.
1 parent 7885a42 commit ede8a02

3 files changed

Lines changed: 31 additions & 48 deletions

File tree

ports/unix/coverage.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,21 @@ STATIC mp_obj_t extra_coverage(void) {
181181
mp_printf(&mp_plat_print, "%.*s\n", (int)vstr->len, vstr->buf);
182182

183183
VSTR_FIXED(fix, 4);
184-
vstr_add_str(&fix, "large");
185-
mp_printf(&mp_plat_print, "%.*s\n", (int)fix.len, fix.buf);
184+
nlr_buf_t nlr;
185+
if (nlr_push(&nlr) == 0) {
186+
vstr_add_str(&fix, "large");
187+
nlr_pop();
188+
} else {
189+
mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
190+
}
191+
192+
fix.len = fix.alloc;
193+
if (nlr_push(&nlr) == 0) {
194+
vstr_null_terminated_str(&fix);
195+
nlr_pop();
196+
} else {
197+
mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
198+
}
186199
}
187200

188201
// repl autocomplete

py/vstr.c

Lines changed: 14 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
#include <assert.h>
3131

3232
#include "py/mpconfig.h"
33-
#include "py/misc.h"
33+
#include "py/runtime.h"
3434
#include "py/mpprint.h"
3535

3636
// returned value is always at least 1 greater than argument
@@ -92,7 +92,9 @@ void vstr_free(vstr_t *vstr) {
9292
// Extend vstr strictly by requested size, return pointer to newly added chunk.
9393
char *vstr_extend(vstr_t *vstr, size_t size) {
9494
if (vstr->fixed_buf) {
95-
return NULL;
95+
// We can't reallocate, and the caller is expecting the space to
96+
// be there, so the only safe option is to raise an exception.
97+
mp_raise_msg(&mp_type_RuntimeError, NULL);
9698
}
9799
char *new_buf = m_renew(char, vstr->buf, vstr->alloc, vstr->alloc + size);
98100
char *p = new_buf + vstr->alloc;
@@ -101,27 +103,26 @@ char *vstr_extend(vstr_t *vstr, size_t size) {
101103
return p;
102104
}
103105

104-
STATIC bool vstr_ensure_extra(vstr_t *vstr, size_t size) {
106+
STATIC void vstr_ensure_extra(vstr_t *vstr, size_t size) {
105107
if (vstr->len + size > vstr->alloc) {
106108
if (vstr->fixed_buf) {
107-
return false;
109+
// We can't reallocate, and the caller is expecting the space to
110+
// be there, so the only safe option is to raise an exception.
111+
mp_raise_msg(&mp_type_RuntimeError, NULL);
108112
}
109113
size_t new_alloc = ROUND_ALLOC((vstr->len + size) + 16);
110114
char *new_buf = m_renew(char, vstr->buf, vstr->alloc, new_alloc);
111115
vstr->alloc = new_alloc;
112116
vstr->buf = new_buf;
113117
}
114-
return true;
115118
}
116119

117120
void vstr_hint_size(vstr_t *vstr, size_t size) {
118121
vstr_ensure_extra(vstr, size);
119122
}
120123

121124
char *vstr_add_len(vstr_t *vstr, size_t len) {
122-
if (!vstr_ensure_extra(vstr, len)) {
123-
return NULL;
124-
}
125+
vstr_ensure_extra(vstr, len);
125126
char *buf = vstr->buf + vstr->len;
126127
vstr->len += len;
127128
return buf;
@@ -131,19 +132,14 @@ char *vstr_add_len(vstr_t *vstr, size_t len) {
131132
char *vstr_null_terminated_str(vstr_t *vstr) {
132133
// If there's no more room, add single byte
133134
if (vstr->alloc == vstr->len) {
134-
if (vstr_extend(vstr, 1) == NULL) {
135-
return NULL;
136-
}
135+
vstr_extend(vstr, 1);
137136
}
138137
vstr->buf[vstr->len] = '\0';
139138
return vstr->buf;
140139
}
141140

142141
void vstr_add_byte(vstr_t *vstr, byte b) {
143142
byte *buf = (byte*)vstr_add_len(vstr, 1);
144-
if (buf == NULL) {
145-
return;
146-
}
147143
buf[0] = b;
148144
}
149145

@@ -153,31 +149,19 @@ void vstr_add_char(vstr_t *vstr, unichar c) {
153149
// Is it worth just calling vstr_add_len(vstr, 4)?
154150
if (c < 0x80) {
155151
byte *buf = (byte*)vstr_add_len(vstr, 1);
156-
if (buf == NULL) {
157-
return;
158-
}
159152
*buf = (byte)c;
160153
} else if (c < 0x800) {
161154
byte *buf = (byte*)vstr_add_len(vstr, 2);
162-
if (buf == NULL) {
163-
return;
164-
}
165155
buf[0] = (c >> 6) | 0xC0;
166156
buf[1] = (c & 0x3F) | 0x80;
167157
} else if (c < 0x10000) {
168158
byte *buf = (byte*)vstr_add_len(vstr, 3);
169-
if (buf == NULL) {
170-
return;
171-
}
172159
buf[0] = (c >> 12) | 0xE0;
173160
buf[1] = ((c >> 6) & 0x3F) | 0x80;
174161
buf[2] = (c & 0x3F) | 0x80;
175162
} else {
176163
assert(c < 0x110000);
177164
byte *buf = (byte*)vstr_add_len(vstr, 4);
178-
if (buf == NULL) {
179-
return;
180-
}
181165
buf[0] = (c >> 18) | 0xF0;
182166
buf[1] = ((c >> 12) & 0x3F) | 0x80;
183167
buf[2] = ((c >> 6) & 0x3F) | 0x80;
@@ -193,16 +177,7 @@ void vstr_add_str(vstr_t *vstr, const char *str) {
193177
}
194178

195179
void vstr_add_strn(vstr_t *vstr, const char *str, size_t len) {
196-
if (!vstr_ensure_extra(vstr, len)) {
197-
// if buf is fixed, we got here because there isn't enough room left
198-
// so just try to copy as much as we can, with room for a possible null byte
199-
if (vstr->fixed_buf && vstr->len < vstr->alloc) {
200-
len = vstr->alloc - vstr->len;
201-
goto copy;
202-
}
203-
return;
204-
}
205-
copy:
180+
vstr_ensure_extra(vstr, len);
206181
memmove(vstr->buf + vstr->len, str, len);
207182
vstr->len += len;
208183
}
@@ -214,9 +189,7 @@ STATIC char *vstr_ins_blank_bytes(vstr_t *vstr, size_t byte_pos, size_t byte_len
214189
}
215190
if (byte_len > 0) {
216191
// ensure room for the new bytes
217-
if (!vstr_ensure_extra(vstr, byte_len)) {
218-
return NULL;
219-
}
192+
vstr_ensure_extra(vstr, byte_len);
220193
// copy up the string to make room for the new bytes
221194
memmove(vstr->buf + byte_pos + byte_len, vstr->buf + byte_pos, l - byte_pos);
222195
// increase the length
@@ -227,17 +200,13 @@ STATIC char *vstr_ins_blank_bytes(vstr_t *vstr, size_t byte_pos, size_t byte_len
227200

228201
void vstr_ins_byte(vstr_t *vstr, size_t byte_pos, byte b) {
229202
char *s = vstr_ins_blank_bytes(vstr, byte_pos, 1);
230-
if (s != NULL) {
231-
*s = b;
232-
}
203+
*s = b;
233204
}
234205

235206
void vstr_ins_char(vstr_t *vstr, size_t char_pos, unichar chr) {
236207
// TODO UNICODE
237208
char *s = vstr_ins_blank_bytes(vstr, char_pos, 1);
238-
if (s != NULL) {
239-
*s = chr;
240-
}
209+
*s = chr;
241210
}
242211

243212
void vstr_cut_head_bytes(vstr_t *vstr, size_t bytes_to_cut) {

tests/unix/extra_coverage.py.exp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ sts
1818

1919
test
2020
tes
21-
larg
21+
RuntimeError:
22+
RuntimeError:
2223
# repl
2324
ame__
2425

0 commit comments

Comments
 (0)