From cea6cf8a5e87127eccc8214cbffa2d4c8aae8527 Mon Sep 17 00:00:00 2001
From: Damien George <damien.p.george@gmail.com>
Date: Tue, 15 Mar 2016 12:21:56 +0000
Subject: [PATCH] py/formatfloat: Fix buffer overflow when formatting tiny
 numbers.

---
 py/formatfloat.c                    | 14 ++++++++++----
 tests/float/string_format_modulo.py | 16 ++++++++++++++++
 2 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/py/formatfloat.c b/py/formatfloat.c
index b2107316f..f7762b07d 100644
--- a/py/formatfloat.c
+++ b/py/formatfloat.c
@@ -27,6 +27,7 @@
 #include "py/mpconfig.h"
 #if MICROPY_FLOAT_IMPL != MICROPY_FLOAT_IMPL_NONE
 
+#include <assert.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include "py/formatfloat.h"
@@ -210,13 +211,15 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch
             dec = -1;
             *s++ = first_dig;
 
-            if (prec + e + 1 > buf_remaining) {
-                prec = buf_remaining - e - 1;
-            }
-
             if (org_fmt == 'g') {
                 prec += (e - 1);
             }
+
+            // truncate precision to prevent buffer overflow
+            if (prec + 2 > buf_remaining) {
+                prec = buf_remaining - 2;
+            }
+
             num_digits = prec;
             if (num_digits) {
                 *s++ = '.'; 
@@ -390,6 +393,9 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch
     }
     *s = '\0';
 
+    // verify that we did not overrun the input buffer
+    assert((size_t)(s + 1 - buf) <= buf_size);
+
     return s - buf;
 }
 
diff --git a/tests/float/string_format_modulo.py b/tests/float/string_format_modulo.py
index 03b178703..681e9eb80 100644
--- a/tests/float/string_format_modulo.py
+++ b/tests/float/string_format_modulo.py
@@ -26,3 +26,19 @@ print("%02.3d" % 123) # prec > width
 print("%+f %+f" % (1.23, -1.23)) # float sign
 print("% f % f" % (1.23, -1.23)) # float space sign
 print("%0f" % -1.23) # negative number with 0 padding
+
+# numbers with large negative exponents
+print('%f' % 1e-10)
+print('%f' % 1e-20)
+print('%f' % 1e-50)
+print('%f' % 1e-100)
+print('%f' % 1e-300)
+
+# large decimal precision should be truncated and not overflow buffer
+# the output depends on the FP calculation so only first 2 digits are printed
+# (the 'g' with small e are printed using 'f' style, so need to be checked)
+print(('%.40f' % 1e-300)[:2])
+print(('%.40g' % 1e-1)[:2])
+print(('%.40g' % 1e-2)[:2])
+print(('%.40g' % 1e-3)[:2])
+print(('%.40g' % 1e-4)[:2])
-- 
GitLab