This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

[PATCH] Fix for libstdc++/4402


Hi all.

The segmentation fault of this PR is due to a too small __sbuf (in num_put::do_put for
doubles), which is currently dimensioned for non-std::fixed outputs; the very same problem
affects long doubles too (not yet reported to date).

Therefore, an obvious fix involves using a bigger __sbuf in some circumstances. But *how*
big, exactly? Among the implementations using sprintf I have access to, the best one
bypasses completely the problem by not respecting the division in 4 independent stages
prescribed by 22.2.2.2.2 (moreover, it involves many slow floating point divisions).
Another one uses a buffer of fixed length, 127 chars (vs ~36 chars for doubles in the
present libstdc++-v3 code)

Here I propose a patch which cleanly divides in two steps the work to be done in
num_put::do_put.
First an upper bound on the length in chars of the number is computed, then the various
cases (2 in this simple patch) are dealt with: when the length is below a predefined
threshold (I fixed it rather arbitrarily at 64 and 128 chars for doubles and long doubles,

respectively) an automatic array is used for __sbuf, otherwise dynamic memory is requested
by means of operator new. I did my best to avoid any slow dynamic memory allocation but
eventually I had to succumb. However, the automatic array should be used with
overwhelmingly higher frequency (particularly so for non-std::fixed outputs) and a strong
point of the patch is that of allowing for *any* number to be output without seg-faulting.

(Dynamic memory is also used in other parts of locale_facets.tcc, anyway).

Both the above steps could be fine tuned (in particular the "magic numbers" 32, 64 and
128) and customized in many ways, perhaps independently for doubles and long doubles.

As is, the patch was tested and regression checked on i686-pc-linux-gnu (PII, glibc2.2.4).
A minimal patch for the testsuite (ostream_inserter_arith.cc) is also included.

Cheers,
Paolo Carlini.

P.S. Contrary to my first guess (tricked by a rather misleading comment in the code ;-),
big values of std::width are not harmful, since the padding itself does not need any
buffering.



*** locale_facets.tcc.orig Sat Nov 10 19:29:38 2001
--- locale_facets.tcc Sun Nov 11 16:57:30 2001
*************** namespace std
*** 872,891 ****
      {
        const streamsize __max_prec = numeric_limits<double>::digits10 + 3;
        streamsize __prec = __io.precision();
-       // Protect against sprintf() buffer overflows.
        if (__prec > __max_prec)
          __prec = __max_prec;
-       // The *2 provides for signs, exp, 'E', and pad.
-       char __sbuf[__max_prec * 2];
        size_t __slen;
        // Long enough for the max format spec.
        char __fbuf[16];
!       if (__build_float_format(__io, __fbuf, 0, __prec))
!         __slen = sprintf(__sbuf, __fbuf, __prec, __v);
        else
!         __slen = sprintf(__sbuf, __fbuf, __v);
!       // [22.2.2.2.2] Stages 2-4.
!       return __output_float(__s, __io, __fill, __sbuf, __slen);
      }

    template<typename _CharT, typename _OutIter>
--- 872,917 ----
      {
        const streamsize __max_prec = numeric_limits<double>::digits10 + 3;
        streamsize __prec = __io.precision();
        if (__prec > __max_prec)
          __prec = __max_prec;
        size_t __slen;
        // Long enough for the max format spec.
        char __fbuf[16];
!       // Upper bounding __slen
!       size_t __max_slen;
!       if (__io.flags() & ios_base::fixed)
!         // Small and big std::fixed numbers
!         if (__v > -1e32 && __v < 1e32)
!           __max_slen = 32 + __max_prec + 3;
!         else
!           __max_slen = numeric_limits<double>::max_exponent10 + __max_prec + 3;
        else
!         // The *2 provides for signs, exp, 'E'.
!         __max_slen = __max_prec * 2;
!       bool __ff = __build_float_format(__io, __fbuf, 0, __prec);
!       // Deal with both "short" and "long" numbers
!       if (__max_slen < 64)
!         {
!           char __sbuf[64];
!           if (__ff)
!             __slen = sprintf(__sbuf, __fbuf, __prec, __v);
!           else
!             __slen = sprintf(__sbuf, __fbuf, __v);
!           // [22.2.2.2.2] Stages 2-4.
!           return __output_float(__s, __io, __fill, __sbuf, __slen);
!         }
!       else
!         {
!           char* __sbuf = new char[__max_slen];
!           if (__ff)
!             __slen = sprintf(__sbuf, __fbuf, __prec, __v);
!           else
!             __slen = sprintf(__sbuf, __fbuf, __v);
!           // [22.2.2.2.2] Stages 2-4.
!           _OutIter __ri = __output_float(__s, __io, __fill, __sbuf, __slen);
!           delete[] __sbuf;
!           return __ri;
!         }
      }

    template<typename _CharT, typename _OutIter>
*************** namespace std
*** 896,916 ****
      {
        const streamsize __max_prec = numeric_limits<long double>::digits10 + 3;
        streamsize __prec = __io.precision();
-       // Protect against sprintf() buffer overflows.
        if (__prec > __max_prec)
          __prec = __max_prec;
-       // The *2 provides for signs, exp, 'E', and pad.
-       char __sbuf[__max_prec * 2];
        size_t __slen;
        // Long enough for the max format spec.
        char __fbuf[16];
        // 'L' as per [22.2.2.2.2] Table 59
!       if (__build_float_format(__io, __fbuf, 'L', __prec))
!         __slen = sprintf(__sbuf, __fbuf, __prec, __v);
        else
!         __slen = sprintf(__sbuf, __fbuf, __v);
!       // [22.2.2.2.2] Stages 2-4
!       return __output_float(__s, __io, __fill, __sbuf, __slen);
      }

    template<typename _CharT, typename _OutIter>
--- 922,968 ----
      {
        const streamsize __max_prec = numeric_limits<long double>::digits10 + 3;
        streamsize __prec = __io.precision();
        if (__prec > __max_prec)
          __prec = __max_prec;
        size_t __slen;
        // Long enough for the max format spec.
        char __fbuf[16];
+       // Upper bounding number length
+       size_t __max_slen;
+       if (__io.flags() & ios_base::fixed)
+         // Small and big std::fixed numbers
+         if (__v > -1e64 && __v < 1e64)
+           __max_slen = 64 + __max_prec + 3;
+         else
+           __max_slen = numeric_limits<long double>::max_exponent10 + __max_prec + 3;
+       else
+         // The *2 provides for signs, exp, 'E'.
+         __max_slen = __max_prec * 2;
        // 'L' as per [22.2.2.2.2] Table 59
!       bool __ff = __build_float_format(__io, __fbuf, 'L', __prec);
!       // Deal with "short" and "long" numbers
!       if (__max_slen < 128)
!         {
!           char __sbuf[128];
!           if (__ff)
!             __slen = sprintf(__sbuf, __fbuf, __prec, __v);
!           else
!             __slen = sprintf(__sbuf, __fbuf, __v);
!           // [22.2.2.2.2] Stages 2-4.
!           return __output_float(__s, __io, __fill, __sbuf, __slen);
!         }
        else
!         {
!           char* __sbuf = new char[__max_slen];
!           if (__ff)
!             __slen = sprintf(__sbuf, __fbuf, __prec, __v);
!           else
!             __slen = sprintf(__sbuf, __fbuf, __v);
!           // [22.2.2.2.2] Stages 2-4.
!           _OutIter __ri = __output_float(__s, __io, __fill, __sbuf, __slen);
!           delete[] __sbuf;
!           return __ri;
!         }
      }

    template<typename _CharT, typename _OutIter>


*** ostream_inserter_arith.cc.orig Sun Nov 11 13:48:59 2001
--- ostream_inserter_arith.cc Sun Nov 11 15:13:47 2001
*************** test02()
*** 272,277 ****
--- 272,293 ----
  #endif
    VERIFY(os && os.str() == largebuf);

+   // make sure we can output a long float in std::fixed format
+   // without seg-faulting (libstdc++/4402)
+   double val2 = 3.5e230;
+
+   ostringstream os2;
+   os2.precision(3);
+   os2.setf(ios::fixed);
+   os2 << val2;
+
+   sprintf(largebuf, "%.*f", 3, val2);
+ #ifdef TEST_NUMPUT_VERBOSE
+   cout << "expect: " << largebuf << endl;
+   cout << "result: " << os2.str() << endl;
+ #endif
+   VERIFY(os2 && os2.str() == largebuf);
+
    return 0;
  }



Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]