Configuration Information:
Machine: aarch64 (Apple silicon)
OS: darwin25.4.0 (built), running macOS 26.6.2 (25G83)
Bash Version: 5.3
Patch Level: 15
Release Status: release
Build: Homebrew bottle (bash 5.3.15), linked against Homebrew gettext 1.0
(libintl.8.dylib)
Locale environment: LANG unset, LC_CTYPE=UTF-8, no LC_ALL

Description:
Bash intermittently dies with SIGSEGV in the child process of a command
substitution, before exec, while restoring the locale after a command that
ran with a temporary `LC_ALL=C` assignment.
On restore, sv_locale calls libintl's setlocale, which on macOS asks
CoreFoundation for the preferred languages; CoreFoundation is not usable in
the child of a multi-threaded process after fork, and the process crashes.
macOS's crash report says so explicitly:

  "CoreFoundation": "*** multi-threaded process forked ***"
  "libsystem_c.dylib": "crashed on child side of fork pre-exec"
  EXC_BAD_ACCESS (SIGSEGV), KERN_INVALID_ADDRESS

Crashing thread (the process has one thread), innermost first:

  libsystem_trace.dylib  _os_log_preferences_refresh
  libsystem_trace.dylib  os_log_type_enabled
  CoreFoundation         -[CFPrefsSearchListSource
alreadylocked_copyValueForKey:]
  CoreFoundation         -[CFPrefsSource copyValueForKey:]
  CoreFoundation         ...
_CFPreferencesCopyAppValueWithContainerAndConfiguration
  Foundation             LocaleCache.preferredLanguages(forCurrentUser:)
  Foundation             +[NSLocale _preferredLanguagesForCurrentUser:]
  CoreFoundation         CFLocaleCopyPreferredLanguages
  libintl.8.dylib        libintl_setlocale
  bash                   reset_locale_vars
  bash                   set_locale_var
  bash                   sv_locale
  bash                   dispose_temporary_env
  bash                   dispose_used_env_vars
  bash                   execute_command_internal
  bash                   parse_and_execute
  bash                   command_substitute
  bash                   param_expand
  bash                   expand_word_internal
  bash                   call_expand_word_internal
  bash                   expand_string_assignment

This looks like the same class of problem as the May 2024 thread on
bug-bash ("+initialize may have been in progress" / gettext after fork on
macOS, https://lists.gnu.org/archive/html/bug-bash/2024-05/msg00011.html),
where the fix was an early gettext() call in the parent.
This path is different: the CoreFoundation call comes from setlocale() in
the child, while restoring locale variables after a temporary environment,
and it still happens in 5.3.15.

The script that hits it uses the pattern below in several places, inside
`$(...)` and `<(...)`, under `set -euo pipefail`, in a long-running script
that also sources a few libraries.
Measured on one workload: 5 of 65 runs exited with status 139 within an
hour; more crash reports with the same stack appeared in that hour than
there were failed runs, so some crashing children may not fail the script.

Repeat-By:
I don't have a minimal reproducer yet.
The pattern is a command substitution whose command carries a temporary
locale assignment, for example:

  x=$(some_function_or_pipeline | LC_ALL=C sort -u)
  mapfile -t a < <(printf '%s\n' "${items[@]}" | LC_ALL=C sort -u)

A loop of 3,000 fresh `bash -c 'x=$(LC_ALL=C sort </dev/null)'` runs did
not crash, so it seems to need a parent that has already become
multi-threaded (presumably through an earlier setlocale() that initialised
CoreFoundation).
I can run a candidate patch or a debug build against the failing workload,
and send full .ips crash reports.

Fix:
Workaround I am about to test: `env LC_ALL=C sort` instead of `LC_ALL=C
sort`, so bash has no temporary locale assignment to restore in the forked
child.
In bash, presumably: avoid calling setlocale() (or anything reaching
CoreFoundation) in a forked child before exec on macOS, or make sure the
parent has initialised the locale machinery in a way that keeps the child
safe, as the 2024 fix did for gettext().
Building bash against its bundled libintl reportedly avoided the 2024
crash; I have not tried that for this one.

Best,
Jakub Bocheński

Reply via email to