Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20260805145649.GR27423@brightrain.aerifal.cx>
Date: Wed, 5 Aug 2026 10:56:49 -0400
From: Rich Felker <dalias@...c.org>
To: Matthias Goergens <matthias.goergens@...il.com>
Cc: Szabolcs Nagy <nsz@...t70.net>, musl@...ts.openwall.com
Subject: Re: Re: [PATCH] stdio: reserve leading limb for
 floating-point rounding

On Wed, Aug 05, 2026 at 10:08:04PM +0800, Matthias Goergens wrote:
> You're right, and the patch should be withdrawn.  My analysis missed
> the mechanism you described.
> 
> The missing piece: in the e2<0 path the value is always in (0,1):
> frexpl gives y in [1,2) with v = y*2^e2, so e2<0 implies v<1, and
> after y *= 0x1p28 we have y < 2^29 with e2 <= -29.  The right-shift
> loop therefore divides the limb array by at least 2^29, which always
> leaves the most significant limb zero, and the loop's
> 
> 	if (!*a) a++;
> 
> then guarantees a >= big+1 before rounding.  The rounding carry's
> 
> 	if (d<a) *--a=0;
> 
> consequently writes big[0] — the dead units limb — never big[-1].
> Exactly the "a = d = big+1, big[0] = 0 before the while" trace you
> walked.
> 
> Because our claim said the opposite, I verified this empirically rather
> than just by re-reading the code.  I built unpatched master (b306b16a)
> with a canary in fmt_fp that aborts if `*--a` is ever reached with
> a<=big (in both the rounding loop and the positive-exponent scaling
> loop), plus offset tracing.  Results:
> 
> - Stated trigger printf("%.0Lf", nextafterl(1.0L, 0.0L)): the
>   carry-past-a path is reached, with a=d=big+1 and big[0]=0 — your
>   reading confirmed concretely.  The write lands on big[0].
> - A ~4.2M-call sweep (ulp neighbourhoods below/above 1.0L and powers of
>   two/ten, 1-2^-k, extrema, denormals, precisions 0..1000, %f/%e/%g,
>   double and long double) on x86_64: 9,090 carry-past-a events, every
>   one with a-big=1, canary never fired.
> - Same sweep under QEMU on aarch64 (binary128, LDBL_MANT_DIG==113):
>   20,684 carry-past-a events, minimum slack a-big=1, canary never
>   fired; and on arm (LDBL_MANT_DIG==53): likewise.
> - Our 62,304-case corpus and the extrema/rounding-mode matrices also
>   run clean under the canary, with output hashes unchanged.
> 
> Where the original claim went wrong: it keyed on the initial assignment
> `if (e2<0) a=r=z=big;` and assumed a==big persists into the rounding
> loop, overlooking that the right-shift loop vacates big[0] first.  The
> trigger does exercise the carry-past-a branch (that part was real), but
> the write is in bounds.
> 
> Net: the claimed big[-1] write does not occur; the patch reserves a
> slot that the vacated-units-limb invariant already provides, so it is a
> semantic no-op.  I'm withdrawing it.  Sorry for the noise, and thanks
> for the careful walk-through.

Thanks for reviewing this further so that we're all in agreement. I
worked through this (albeit maybe without keeping sufficient notes)
for commit 0ccaf0572e9cccda2cced0f7ee659af4c1c6679a which fixed a
previous bug here, and I was confident that the pointers remained
within bounds, so it was unsettling to find a new claim that they
don't.

Rich

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.