diff options
author | vlefevre <vlefevre@280ebfd0-de03-0410-8827-d642c229c3f4> | 2017-05-24 07:10:21 +0000 |
---|---|---|
committer | vlefevre <vlefevre@280ebfd0-de03-0410-8827-d642c229c3f4> | 2017-05-24 07:10:21 +0000 |
commit | ee82b3c89f1068abf531d1d5168d231b7539bca9 (patch) | |
tree | 0c953b33c310d64d89c3495786e35f291d5e8126 | |
parent | def23de32451d0801b57d93da2ddc57d6ada69b4 (diff) | |
download | mpfr-ee82b3c89f1068abf531d1d5168d231b7539bca9.tar.gz |
[src/vasprintf.c] Fixed the handling of the 'n' format specifier
for size = 0, still allowing its support in case of overflow on
the return value (i.e. when the number of characters that would
have been output is > INT_MAX).
Note: full support in case of overflow is still limited by GMP
and the C library.
[tests/tprintf.c] Increased the memory needed in check_long_string().
git-svn-id: svn://scm.gforge.inria.fr/svn/mpfr/trunk@11523 280ebfd0-de03-0410-8827-d642c229c3f4
-rw-r--r-- | src/vasprintf.c | 86 | ||||
-rw-r--r-- | tests/tprintf.c | 4 |
2 files changed, 41 insertions, 49 deletions
diff --git a/src/vasprintf.c b/src/vasprintf.c index b1709fb19..2c97669e5 100644 --- a/src/vasprintf.c +++ b/src/vasprintf.c @@ -523,12 +523,14 @@ typedef wint_t mpfr_va_wint; buffer_cat ((buf_ptr), (start), n); \ } while (0) +/* Note: in case some form of %n is used in the format string, + we may need the maximum signed integer type for len. */ struct string_buffer { char *start; /* beginning of the buffer */ char *curr; /* null terminating character */ size_t size; /* buffer capacity */ - int len; /* string length or -1 if overflow */ + mpfr_intmax_t len; /* string length or -1 if overflow */ }; static void @@ -552,12 +554,15 @@ buffer_incr_len (struct string_buffer *b, size_t len) return 1; else { - size_t newlen = (size_t) b->len + len; + /* We need to take mpfr_uintmax_t as the type must be as large + as both size_t (which is unsigned) and mpfr_intmax_t (which + is used for the 'n' format specifier). */ + mpfr_uintmax_t newlen = (mpfr_uintmax_t) b->len + len; - /* size_t is unsigned, thus the above is valid, but one has - newlen < len in case of overflow. */ + /* mpfr_uintmax_t is unsigned, thus the above is valid, but one + has newlen < len in case of overflow. */ - if (MPFR_UNLIKELY (newlen < len || newlen > INT_MAX)) + if (MPFR_UNLIKELY (newlen < len || newlen > MPFR_INTMAX_MAX)) return 1; else { @@ -575,9 +580,10 @@ buffer_widen (struct string_buffer *b, size_t len) const size_t pos = b->curr - b->start; const size_t n = 0x1000 + (len & ~((size_t) 0xfff)); - /* An overflow is not possible since it would have been detected - in buffer_incr_len, called first (see buffer_* functions). */ - MPFR_ASSERTD (n >= 0x1000 && n >= len); + /* There are currently limitations here. We would need to switch to + the null-size behavior once there is an overflow in the buffer. */ + + MPFR_ASSERTN (n >= 0x1000 && n >= len); MPFR_ASSERTD (*b->curr == '\0'); MPFR_ASSERTD (pos < b->size); @@ -1595,7 +1601,7 @@ regular_fg (struct number_parts *np, mpfr_srcptr p, return the total number of characters to be written. return -1 if an error occurred, in that case np's fields are in an undefined state but all string buffers have been freed. */ -static int +static mpfr_intmax_t partition_number (struct number_parts *np, mpfr_srcptr p, struct printf_spec spec) { @@ -1818,14 +1824,14 @@ partition_number (struct number_parts *np, mpfr_srcptr p, /* compute the number of characters to be written verifying it is not too much */ -#define INCR_TOTAL(v) \ - do { \ - MPFR_ASSERTD ((v) >= 0); \ - if (MPFR_UNLIKELY ((v) > INT_MAX)) \ - goto error; \ - total += (v); \ - if (MPFR_UNLIKELY (total > INT_MAX)) \ - goto error; \ +#define INCR_TOTAL(v) \ + do { \ + MPFR_ASSERTD ((v) >= 0); \ + if (MPFR_UNLIKELY ((v) > MPFR_INTMAX_MAX)) \ + goto error; \ + total += (v); \ + if (MPFR_UNLIKELY (total > MPFR_INTMAX_MAX)) \ + goto error; \ } while (0) total = np->sign ? 1 : 0; @@ -1853,7 +1859,7 @@ partition_number (struct number_parts *np, mpfr_srcptr p, MPFR_ASSERTD (total == spec.width); } - MPFR_ASSERTD (total > 0 && total <= INT_MAX); + MPFR_ASSERTD (total > 0 && total <= MPFR_INTMAX_MAX); return total; error: @@ -1877,7 +1883,7 @@ static int sprnt_fp (struct string_buffer *buf, mpfr_srcptr p, const struct printf_spec spec) { - int length, start; + mpfr_intmax_t length, start; struct number_parts np; length = partition_number (&np, p, spec); @@ -2092,61 +2098,47 @@ mpfr_vasnprintf_aux (char **ptr, char *Buf, size_t size, const char *fmt, so as to be able to accept the same format strings. */ { void *p; - size_t nchar; p = va_arg (ap, void *); FLUSH (xgmp_fmt_flag, start, end, ap2, &buf); va_end (ap2); start = fmt; - /* FIXME: When size is 0, the buffer doesn't exist. We should take, - buf.len, but it is only an int. A solution could be to increase - it to mpfr_intmax_t, but all the overflow detection needs to be - redone. Alternatively, one may consider that in case of overflow, - the object associated with the 'n' format specifier does not - have to be filled, i.e. the consequences of the overflow error - are unspecified. For ISO C, an overflow on the return value - seems to be undefined behavior; in POSIX, this is not, but the - effects of an overflow seem to be unclear. Let's wait for - comments in the Austin Group mailing-list: - https://www.mail-archive.com/austin-group-l@opengroup.org/msg01038.html - */ - nchar = buf.curr - buf.start; switch (spec.arg_type) { case CHAR_ARG: - *(char *) p = (char) nchar; + *(char *) p = (char) buf.len; break; case SHORT_ARG: - *(short *) p = (short) nchar; + *(short *) p = (short) buf.len; break; case LONG_ARG: - *(long *) p = (long) nchar; + *(long *) p = (long) buf.len; break; #ifdef HAVE_LONG_LONG case LONG_LONG_ARG: - *(long long *) p = (long long) nchar; + *(long long *) p = (long long) buf.len; break; #endif #ifdef _MPFR_H_HAVE_INTMAX_T case INTMAX_ARG: - *(intmax_t *) p = (intmax_t) nchar; + *(intmax_t *) p = (intmax_t) buf.len; break; #endif case SIZE_ARG: - *(size_t *) p = nchar; + *(size_t *) p = buf.len; break; case PTRDIFF_ARG: - *(ptrdiff_t *) p = (ptrdiff_t) nchar; + *(ptrdiff_t *) p = (ptrdiff_t) buf.len; break; case MPF_ARG: - mpf_set_ui ((mpf_ptr) p, (unsigned long) nchar); + mpf_set_ui ((mpf_ptr) p, (unsigned long) buf.len); break; case MPQ_ARG: - mpq_set_ui ((mpq_ptr) p, (unsigned long) nchar, 1L); + mpq_set_ui ((mpq_ptr) p, (unsigned long) buf.len, 1L); break; case MP_LIMB_ARG: - *(mp_limb_t *) p = (mp_limb_t) nchar; + *(mp_limb_t *) p = (mp_limb_t) buf.len; break; case MP_LIMB_ARRAY_ARG: { @@ -2159,7 +2151,7 @@ mpfr_vasnprintf_aux (char **ptr, char *Buf, size_t size, const char *fmt, break; /* we assume here that mp_limb_t is wider than int */ - *q = (mp_limb_t) nchar; + *q = (mp_limb_t) buf.len; while (--n != 0) { q++; @@ -2168,16 +2160,16 @@ mpfr_vasnprintf_aux (char **ptr, char *Buf, size_t size, const char *fmt, } break; case MPZ_ARG: - mpz_set_ui ((mpz_ptr) p, (unsigned long) nchar); + mpz_set_ui ((mpz_ptr) p, (unsigned long) buf.len); break; case MPFR_ARG: - mpfr_set_ui ((mpfr_ptr) p, (unsigned long) nchar, + mpfr_set_ui ((mpfr_ptr) p, (unsigned long) buf.len, spec.rnd_mode); break; default: - *(int *) p = (int) nchar; + *(int *) p = (int) buf.len; } va_copy (ap2, ap); /* after the switch, due to MP_LIMB_ARRAY_ARG case */ diff --git a/tests/tprintf.c b/tests/tprintf.c index 7767a9052..8130266d0 100644 --- a/tests/tprintf.c +++ b/tests/tprintf.c @@ -142,10 +142,10 @@ check_long_string (void) increase is necessary, but is not guaranteed to be sufficient in all cases (e.g. with logging activated). */ min_memory_limit = large_prec / MPFR_BYTES_PER_MP_LIMB; - if (min_memory_limit > (size_t) -1 / 12) + if (min_memory_limit > (size_t) -1 / 32) min_memory_limit = (size_t) -1; else - min_memory_limit *= 12; + min_memory_limit *= 32; if (tests_memory_limit > 0 && tests_memory_limit < min_memory_limit) tests_memory_limit = min_memory_limit; |