Skip to content

Fix five correctness bugs from the audit (B1–B5)#12

Merged
sigilante merged 1 commit into
fix/test-suite-honestyfrom
fix/correctness-bugs
May 30, 2026
Merged

Fix five correctness bugs from the audit (B1–B5)#12
sigilante merged 1 commit into
fix/test-suite-honestyfrom
fix/correctness-bugs

Conversation

@sigilante
Copy link
Copy Markdown
Collaborator

Five behavior bugs the audit found. Stacks on #11 (base is fix/test-suite-honesty); retarget to master once #11 merges. Each fix ships with a regression test (except B5, covered by existing stride tests, and B3, verified standalone since complex isn't in the munit build).

Sev Bug Fix Test
B1 🔴 *swap/csrot declared incX/incY uint64_t then tested if (incX<0) (always false) → negative stride wrapped to UINT64_MAX → OOB int64_t params + index vars (matches saxpy) test_sswap_negstride
B2 🟠 qaxpy canonicalized only QY[0]; non-canonical NaNs in QY[1..] escaped (SoftFloat propagates the quieted payload) → determinism break unify every written element test_qaxpy_nan_unify
B3 🟠 FNEG 1<<31 UB, 1<<63 wrong, c128_conj didn't compile (uint64_t[2] ^ int) uint64_t mask; separate c128_conj flipping v[1] sign verified under -fsanitize=undefined
B4 🟡 i*amax returned hardcoded 1 for N==1 but 0-based for N>1 return 0 test_isamax_one
B5 *nrm2 bound 1+(N-1)*incX could overflow → early-exit/OOB iterate by count existing nrm2 stride tests

143/143 pass. These are the bugs the Phase 1 honesty fixes (#11) make visible; B1/B4 specifically live in the swap-stride and d/h/q-amax routines that #11 wired back in.

🤖 Generated with Claude Code

B1 (critical): *swap/csrot declared incX/incY as uint64_t but tested
`if (incX < 0)` — always false, so a negative stride wrapped to a huge
positive stride and read/wrote out of bounds. Change incX/incY (and the
index accumulators) to int64_t, matching the already-correct saxpy
family. Test: test_sswap_negstride (X reversed, Y forward).

B2 (high): qaxpy canonicalized only QY[0]; non-canonical NaNs in QY[1..]
escaped (SoftFloat propagates the quieted payload), breaking determinism
for length>1. Canonicalize every written element. Test:
test_qaxpy_nan_unify (non-canonical NaN at index 1 -> QUADNAN).

B3 (high): FNEG used `1 << (bits-1)` — UB for 32-bit (1<<31), wrong for
64-bit (1<<63 exceeds int), and c128_conj didn't compile (xor on the
uint64_t[2] array). Use a uint64_t mask; define c128_conj separately to
flip only the high-word sign bit. Verified compiling + correct under
-fsanitize=undefined.

B4 (medium): i*amax returned a hardcoded 1 for N==1 while being 0-based
for N>1. Return 0. Test: test_isamax_one.

B5 (low): *nrm2 loop bound `1 + (N-1)*incX` could overflow -> early exit
or OOB. Iterate by count (k < N, ix += incX). Covered by the existing
nrm2 stride tests.

143/143 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sigilante sigilante merged commit 1d18d68 into fix/test-suite-honesty May 30, 2026
1 check passed
@sigilante sigilante deleted the fix/correctness-bugs branch May 30, 2026 16:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant