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