summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvlefevre <vlefevre@280ebfd0-de03-0410-8827-d642c229c3f4>2018-07-10 11:51:56 +0000
committervlefevre <vlefevre@280ebfd0-de03-0410-8827-d642c229c3f4>2018-07-10 11:51:56 +0000
commitb359c87b50f1afb7c40baf0ff373f0dfa441bc74 (patch)
treedb9da24746006d9e9df1a159135c09dd58b7df00
parentd3be5e4df2b3c5306122b760c47aef0b04cba5e0 (diff)
downloadmpfr-b359c87b50f1afb7c40baf0ff373f0dfa441bc74.tar.gz
[src/vasprintf.c] Fixed several bugs in buffer_sandwich, which could
yield memory corruption with non-default memory allocators and other undefined behavior; check the return value when this function is called. Details: * check integer overflow on the size computations; * computation of q and r without tests; * fixed the case where r > len (e.g. len = 1 and tz = 1); * in the loop, increase the str pointer only when needed, otherwise str could be beyond of the end of the string of the digits, which is undefined behavior. The first consequence of the r > len issue was an incorrect generated string. Moreover, since the generated string was shorter than expected, mpfr_free_str would provide an incorrect buffer size to the "free" function of the current GMP memory allocator. By default, this size is ignored, but it may matter if the memory allocators have been changed with the mp_set_memory_functions GMP function, in which case a possible consequence could be memory corruption. Note: This corresponds to r12869 from the trunk. Other changes in the trunk (dead code removal...) have not been applied. git-svn-id: svn://scm.gforge.inria.fr/svn/mpfr/branches/4.0@12909 280ebfd0-de03-0410-8827-d642c229c3f4
-rw-r--r--src/vasprintf.c51
1 files changed, 39 insertions, 12 deletions
diff --git a/src/vasprintf.c b/src/vasprintf.c
index 378793f0a..b4fee3477 100644
--- a/src/vasprintf.c
+++ b/src/vasprintf.c
@@ -683,20 +683,31 @@ buffer_sandwich (struct string_buffer *b, char *str, size_t len,
else
{
const size_t step = 3;
- const size_t size = len + tz;
- const size_t r = size % step == 0 ? step : size % step;
- const size_t q = size % step == 0 ? size / step - 1 : size / step;
- const size_t fullsize = size + q;
- size_t i;
+ size_t size, q, r, fullsize;
+ /* check that len + tz does not overflow */
+ if (len > (size_t) -1 - tz)
+ return 1;
+
+ size = len + tz; /* number of digits */
MPFR_ASSERTD (size > 0);
+ q = (size - 1) / step; /* number of separators C */
+ r = ((size - 1) % step) + 1; /* number of digits in the leftmost block */
+
+ /* check that size + q does not overflow */
+ if (size > (size_t) -1 - q)
+ return 1;
+
+ fullsize = size + q; /* number of digits and separators */
+
if (buffer_incr_len (b, fullsize))
return 1;
if (b->size != 0)
{
char *oldcurr;
+ size_t i;
MPFR_ASSERTD (*b->curr == '\0');
MPFR_ASSERTN (b->size < ((size_t) -1) - fullsize);
@@ -705,11 +716,21 @@ buffer_sandwich (struct string_buffer *b, char *str, size_t len,
MPFR_DBGRES (oldcurr = b->curr);
- /* first R significant digits */
- memcpy (b->curr, str, r);
+ /* first r significant digits (leftmost block) */
+ if (r <= len)
+ {
+ memcpy (b->curr, str, r);
+ str += r;
+ len -= r;
+ }
+ else
+ {
+ MPFR_ASSERTD (r > len);
+ memcpy (b->curr, str, len);
+ memset (b->curr + len, '0', r - len);
+ len = 0;
+ }
b->curr += r;
- str += r;
- len -= r;
/* blocks of thousands. Warning: STR might end in the middle of a block */
for (i = 0; i < q; ++i)
@@ -722,6 +743,7 @@ buffer_sandwich (struct string_buffer *b, char *str, size_t len,
{
memcpy (b->curr, str, step);
len -= step;
+ str += step;
}
else
/* last digits in STR, fill up thousand block with zeros */
@@ -736,7 +758,6 @@ buffer_sandwich (struct string_buffer *b, char *str, size_t len,
memset (b->curr, '0', step);
b->curr += step;
- str += step;
}
MPFR_ASSERTD (b->curr - oldcurr == fullsize);
@@ -1920,8 +1941,14 @@ sprnt_fp (struct string_buffer *buf, mpfr_srcptr p,
/* integral part (may also be "nan" or "inf") */
MPFR_ASSERTN (np.ip_ptr != NULL); /* never empty */
if (MPFR_UNLIKELY (np.thousands_sep))
- buffer_sandwich (buf, np.ip_ptr, np.ip_size, np.ip_trailing_zeros,
- np.thousands_sep);
+ {
+ if (buffer_sandwich (buf, np.ip_ptr, np.ip_size, np.ip_trailing_zeros,
+ np.thousands_sep))
+ {
+ buf->len = -1;
+ goto clear_and_exit;
+ }
+ }
else
{
buffer_cat (buf, np.ip_ptr, np.ip_size);