Skip to content

Use the standard library's numeric facets in the global locale - #734

Open
CoffeeFlux wants to merge 1 commit into
TypesettingTools:masterfrom
CoffeeFlux:fix/macos-num-get-crash
Open

CoffeeFlux wants to merge 1 commit into
TypesettingTools:masterfrom
CoffeeFlux:fix/macos-num-get-crash

Conversation

@CoffeeFlux

Copy link
Copy Markdown
Member

Fixes #733: 3.5.0 aborts with "stack buffer overflow" at startup on macOS 13 while parsing the default config.

Cause

This is a libc++ bug that breaks apps built for older macOS:

  • InitLocale() makes a Boost.Locale locale global. Its number parser, num_parse, forwards to std::num_get::do_get, so the app gets its own copy of num_get::__do_get_floating_point, compiled from the SDK's libc++ headers.
  • That copy puts a buffer on its stack and has the system libc++'s __num_get<char>::__stage2_float_prep fill it.
  • Since LLVM 19 (llvm/llvm-project#77948), the headers make that buffer 28 characters, down from 32. The libc++ in older macOS releases still writes 32, which overwrites the stack canary.
  • The function's name and signature didn't change, so neither the compiler nor the linker can catch it.

This matches the crash log: __input_arithmetic<double> in the system libc++ calls __do_get_floating_point<double> in the app binary, which ends in __stack_chk_fail. In the static release build, libboost_locale.a is the only place __do_get_floating_point is defined.

Fix

Take the numeric category of the global locale from the classic locale instead, so number parsing and formatting run entirely inside the system libc++.

This shouldn't change behavior:

  • Nothing uses Boost.Locale's locale-aware number formatting (as:: manipulators or format).
  • Its ICU backend doesn't install its own numpunct, so the decimal point and grouping were already the classic locale's.
  • Collation, case conversion, word boundaries and the codecvt check are unaffected.

Testing

  • Added lagi_util.init_locale_uses_std_numeric_facets, which checks that the global num_get<char> and num_put<char> are the standard ones. Without the fix it fails with boost::locale::impl_icu::num_parse<char>.
  • The full test suite passes on macOS 26. The tests call InitLocale(), so the JSON, option and VFR tests now run with these facets.
  • Not yet tested on macOS 13 or 14. The fix follows from the crash log and the libc++ source. It would be good to have the reporter try a build from this branch.
  • My local build links Homebrew's Boost rather than the static fallback the release uses. The facet swap works the same either way, but CI is the first build of this change in the release configuration.

🤖 Generated with Claude Code

Boost.Locale's num_parse forwards to std::num_get::do_get, so the binary
gets its own copy of num_get::__do_get_floating_point from the SDK's
libc++ headers. Since LLVM 19 that copy allocates a 28-character buffer
and has the system libc++'s __num_get<char>::__stage2_float_prep fill it,
but the libc++ in older macOS releases writes 32 characters. Parsing any
floating-point number then aborts with a stack buffer overflow, which
crashes 3.5.0 on startup on macOS 13 while reading the default config.

Take the numeric category from the classic locale instead. Nothing uses
Boost.Locale's locale-aware number formatting, and its ICU backend
doesn't supply its own numpunct, so parsing and formatting behave the
same.

Fixes TypesettingTools#733.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

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

Crashes on startup in json::Reader::ParseNumber(json::Reader::TokenStream&) (3.5.0)

1 participant