mailing list of musl libc
 help / color / mirror / code / Atom feed
From: Rich Felker <dalias@libc.org>
To: Markus Wichmann <nullplan@gmx.net>
Cc: musl@lists.openwall.com, Michal Terepeta <michalt@google.com>,
	t5y-external <t5y-external@google.com>
Subject: Re: Wrong formatting of doubles?
Date: Tue, 1 Jul 2025 13:34:27 -0400	[thread overview]
Message-ID: <20250701173426.GE1827@brightrain.aerifal.cx> (raw)
In-Reply-To: <aGQZTCr_HtSIofwP@voyager>

On Tue, Jul 01, 2025 at 07:22:20PM +0200, Markus Wichmann wrote:
> 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.

Presumably it's overflowing into other locals on the stack and
clobbering them in a way that affects the subsequent flow of fmt_fp.

> 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?

Nope. The overflow is downward (lower addresses) but the return
address and most of the static contents of the stack frame should be
upward relative the the VLA.

I'm not actually sure how things end up being below the VLA in a way
that causes the erroneous output. FWIW on aarch64 it was all-zeros.
But in any case it's clearly UB, and the mechanism isn't that
important unless you're trying to write an exploit.

> 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.

It doesn't need to be exact, but if it's not, we're relying on roughly
28/29 of max_mant_dig being smaller than 2/27 of max_exp. In practice
this is true for the paramters of real-world floating point formats,
but it's not reasonable as an undocumented assumption to rely on for
safety. Having the values be correctly and exactly computed makes them
self-documenting.

Rich


  reply	other threads:[~2025-07-01 17:34 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-07-01  5:55 Wrong formatting of doubles? Michal Terepeta
2025-07-01 14:22 ` [musl] " Rich Felker
2025-07-01 16:19   ` Rich Felker
2025-07-01 16:37     ` Rich Felker
2025-07-01 17:02       ` Jeffrey Walton
2025-07-01 17:22       ` [musl] " Markus Wichmann
2025-07-01 17:34         ` Rich Felker [this message]
2025-07-02  1:46       ` Rich Felker
2025-07-02  7:56         ` [musl] " Michal Terepeta
2025-07-08  7:43           ` Michal Terepeta
2025-07-08 16:22             ` [musl] " Rich Felker
2025-07-02 14:58         ` Rich Felker
2025-07-03  4:31           ` Markus Wichmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250701173426.GE1827@brightrain.aerifal.cx \
    --to=dalias@libc.org \
    --cc=michalt@google.com \
    --cc=musl@lists.openwall.com \
    --cc=nullplan@gmx.net \
    --cc=t5y-external@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/musl/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).