Skip to content

Commit e1e7657

Browse files
committed
py/formatfloat: Fix further cases of buffer overflow in formatting.
Includes extensive test cases to catch hopefully all cases where buffer might overflow.
1 parent 03b8bb7 commit e1e7657

2 files changed

Lines changed: 46 additions & 3 deletions

File tree

py/formatfloat.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,20 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch
170170

171171
if (fp_iszero(f)) {
172172
e = 0;
173-
if (fmt == 'e') {
174-
e_sign = '+';
175-
} else if (fmt == 'f') {
173+
if (fmt == 'f') {
174+
// Truncate precision to prevent buffer overflow
175+
if (prec + 2 > buf_remaining) {
176+
prec = buf_remaining - 2;
177+
}
176178
num_digits = prec + 1;
179+
} else {
180+
// Truncate precision to prevent buffer overflow
181+
if (prec + 6 > buf_remaining) {
182+
prec = buf_remaining - 6;
183+
}
184+
if (fmt == 'e') {
185+
e_sign = '+';
186+
}
177187
}
178188
} else if (fp_isless1(f)) {
179189
// We need to figure out what an integer digit will be used
@@ -275,6 +285,12 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch
275285
if (fmt == 'e' && prec > (buf_remaining - FPMIN_BUF_SIZE)) {
276286
prec = buf_remaining - FPMIN_BUF_SIZE;
277287
}
288+
if (fmt == 'g'){
289+
// Truncate precision to prevent buffer overflow
290+
if (prec + (FPMIN_BUF_SIZE - 1) > buf_remaining) {
291+
prec = buf_remaining - (FPMIN_BUF_SIZE - 1);
292+
}
293+
}
278294
// If the user specified 'g' format, and e is < prec, then we'll switch
279295
// to the fixed format.
280296

@@ -378,6 +394,9 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch
378394
}
379395
}
380396

397+
// verify that we did not overrun the input buffer so far
398+
assert((size_t)(s + 1 - buf) <= buf_size);
399+
381400
if (org_fmt == 'g' && prec > 0) {
382401
// Remove trailing zeros and a trailing decimal point
383402
while (s[-1] == '0') {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# test formatting floats with large precision, that it doesn't overflow the buffer
2+
3+
def test(num, num_str):
4+
if num == float('inf') or num == 0.0 and num_str != '0.0':
5+
# skip numbers that overflow or underflow the FP precision
6+
return
7+
for kind in ('e', 'f', 'g'):
8+
# check precision either side of the size of the buffer (32 bytes)
9+
for prec in range(23, 36, 2):
10+
fmt = '%.' + '%d' % prec + kind
11+
s = fmt % num
12+
check = abs(float(s) - num)
13+
if num > 1:
14+
check /= num
15+
if check > 1e-6:
16+
print('FAIL', num_str, fmt, s, len(s), check)
17+
18+
# check pure zero
19+
test(0.0, '0.0')
20+
21+
# check most powers of 10, making sure to include exponents with 3 digits
22+
for e in range(-101, 102):
23+
num = pow(10, e)
24+
test(num, '1e%d' % e)

0 commit comments

Comments
 (0)