Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <aGQZTCr_HtSIofwP@voyager>
Date: Tue, 1 Jul 2025 19:22:20 +0200
From: Markus Wichmann <nullplan@....net>
To: musl@...ts.openwall.com
Cc: Michal Terepeta <michalt@...gle.com>,
	t5y-external <t5y-external@...gle.com>
Subject: Re: Wrong formatting of doubles?

Am Tue, Jul 01, 2025 at 12:37:58PM -0400 schrieb Rich Felker:
> On Tue, Jul 01, 2025 at 12:19:57PM -0400, Rich Felker wrote:
> > On Tue, Jul 01, 2025 at 10:22:25AM -0400, Rich Felker wrote:
> > >  static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t, int ps)
> > >  {
> > > -	int bufsize = (ps==BIGLPRE)
> > > -		? (LDBL_MANT_DIG+28)/29 + 1 +          // mantissa expansion
> > > -		  (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9  // exponent expansion
> > > -		: (DBL_MANT_DIG+28)/29 + 1 +
> > > -		  (DBL_MAX_EXP+DBL_MANT_DIG+28+8)/9;
> > > +	int max_mant_dig = (ps==BIGLPRE) ? LDBL_MANT_DIG : DBL_MANT_DIG;
> > > +	int max_exp = (ps==BIGLPRE) ? LDBL_MAX_EXP : DBL_MAX_EXP;
> > > +	int bufsize = (max_mant_dig+28)/29 + 1          // mantissa expansion
> > > +	            + (max_exp+max_mant_dig+28+8)/9;    // exponent expansion
> > >  	uint32_t big[bufsize];
> > >  	uint32_t *a, *d, *r, *z;
> > >  	int e2=0, e, i, j, l;
> > > @@ -266,7 +265,7 @@ static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t, int ps)
> > >  	if (y) y *= 0x1p28, e2-=28;
> > >  
> > >  	if (e2<0) a=r=z=big;
> > > -	else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1;
> > > +	else a=r=z=big+sizeof(big)/sizeof(*big) - max_mant_dig - 1;
> > >  
> > >  	do {
> > >  		*z = y;

I am wondering how this is causing wrong digits instead of a crash. The
only thing the above changes could prevent is an out-of-bounds access.

Well, OK, let's do some math:
DBL_MANT_DIG = 53
LDBL_MANT_DIG = 113
DBL_MAX_EXP = 1024
bufsize = 126

That only leaves 13 blocks for the exponent expansion, which is enough
for 117 decimal digits in front of the radix before a buffer overflow
occurs. Regrettably, DBL_MAX has 308 digits in front of the radix. But
that should mean that some return address gets overwritten and a crash
occurs on return, right?

With the corrected calculation, more than double the required digits can
be saved.

> > I think the correct code would be something like:
> > 
> > +	else a=r=z=big+sizeof(big)/sizeof(*big) - (max_mant_dig+28)/29 - 1;
> 
> An additional -1 (+1 to the # of slots for mantissa expansion, as in
> the commented expression) is needed here because the do/while loop
> emits a final zero slot for the mantissa. I'm not sure this is
> actually needed later, but as long as it's there it needs to be
> accounted for.
> 
> And indeed, with some debug instrumentation, empirically
> z==buf+bufsize after the initial mantissa expansion loop finishes.
> 

I am also wondering if there is any need for the calculation to be exact
in this case. The buffer size is as big as it is for negative exponents,
and for positive ones it really doesn't matter, since the decimal
expansion can never be big enough to fill the whole buffer. So the only
thing this changes is where exactly the data is inside the buffer, which
doesn't seem like a worthwhile change to me.

Ciao,
Markus

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.