Work around aarch64+ASan clang preserve_none miscompile (#78) #80

Open
weaselbot wants to merge 1 commits from weaselbot/weaseljson:weaselbot/issue-78 into main
pull from: weaselbot/weaseljson:weaselbot/issue-78
1 Commits
Author SHA1 Message Date
weaselbot 539cc264ff Work around aarch64+ASan clang preserve_none miscompile
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-arm64, ubuntu-latest-arm64, true) (pull_request) Successful in 1m1s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-arm64, ubuntu-latest-arm64, false) (pull_request) Successful in 54s
CI / pre-commit (pull_request) Successful in 1m1s
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-amd64, ubuntu-latest-amd64, true) (pull_request) Successful in 1m54s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-amd64, ubuntu-latest-amd64, false) (pull_request) Successful in 1m41s
clang miscompiles preserve_none on aarch64: a preserve_none callee uses
x19 as its frame base without saving it, so a caller that also uses x19
as its frame base resumes with a clobbered frame
(llvm/llvm-project#70577, llvm/llvm-project#177519). ASan's larger
frames make it reliable: n_string2() is left with nulled locals and the
parser segfaults on the valid document `""`.

preserve_none is only an optimization for the musttail chain, so fall
back to the default calling convention where it is not trustworthy:

- Define PRESERVE_NONE only when musttail is available (without it the
  parse() trampoline calls every continuation with an ordinary call, and
  those calls returned WeaselJson_REJECT for `""`) and we are not
  building for aarch64 with ASan.
- Stop annotating scan_string()/scan_string_impl() with it: n_string2()
  calls them with an ordinary call and keeps buf and bufEnd live across
  it, so they must preserve their caller's callee-saved registers.

Add a regression test for the minimal valid documents, parsing through
parser3::Parser3 directly so the continuations run under the flags of
the test binary; it segfaults in the aarch64+ASan build without this
change.

Closes #78.
2026-11-10 15:15:53 -05:00