bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

macOS 14 objc `+initialize` forced crash due to fork after thread


From: Tom
Subject: macOS 14 objc `+initialize` forced crash due to fork after thread
Date: Thu, 02 May 2024 06:23:00 +0000

Forgive me if this is a repost; there appeared to be no path for `bashbug` to 
respond to me, so I've sent this manually.

Configuration Information [Automatically generated, do not change]:
Machine: aarch64
OS: darwin23.4.0
Compiler: gcc
Compilation CFLAGS: -g -O2
uname output: Darwin toms-Mac-mini.local 23.4.0 Darwin Kernel Version 23.4.0: 
Fri Mar 15 00:12:49 PDT 2024; root:xnu-10063.101.17~1/RELEASE_ARM64_T6020 arm64
Machine Type: aarch64-apple-darwin23.4.0

Bash Version: 5.2
Patch Level: 26
Release Status: release

Description:

When linked against `libintl.8.dylib`, if an unknown comand is executed, the
following error is seen:

```
objc[54525]: +[__SwiftNativeNSStringBase initialize] may have been in progress 
in another thread when fork() was called.
objc[54525]: +[__SwiftNativeNSStringBase initialize] may have been in progress 
in another thread when fork() was called. We cannot safely call it or ignore it 
in the fork() child process. Crashing instead. Set a breakpoint on 
objc_initializeAfterForkError to debug.
Abort trap: 6
```

This is a synthetic crash caused by the objc runtime whenever a static
initializer (`+initialize`) is called in a fork child of a parent that has
multiple threads.

This appears to be due to calling `libintl_setlocale`, at `locale.c:369`, which
in turn calls `CFPreferencesCopyAppValue`, at `localename-unsafe.c:3406`, which
ultimately calling `dispatch_apply`, spawning long running threads.

After fork, `libintl_gettext` is called at `execute_cmd.c:5731`, which calls
`CFLocaleCopyPreferredLanguages` at `langprefs.c:253`, which (presumably)
attempts to initialize `__SwiftNativeNSStringBase`.

Back-trace of the first invocation (breaking on `dispatch_apply`):

```
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x0000000182c5602c libdispatch.dylib`dispatch_apply
    frame #1: 0x0000000183000af0 CoreFoundation`__103-[CFPrefsSearchListSource 
synchronouslySendSystemMessage:andUserMessage:andDirectMessage:replyHandler:]_block_invoke.52
 + 132
    frame #2: 0x0000000182e86cbc 
CoreFoundation`CFPREFERENCES_IS_WAITING_FOR_SYSTEM_AND_USER_CFPREFSDS + 100
    frame #3: 0x0000000182fffce0 CoreFoundation`-[CFPrefsSearchListSource 
synchronouslySendSystemMessage:andUserMessage:andDirectMessage:replyHandler:] + 
232
    frame #4: 0x0000000182e84f68 CoreFoundation`-[CFPrefsSearchListSource 
alreadylocked_generationCountFromListOfSources:count:] + 232
    frame #5: 0x0000000182e84c70 CoreFoundation`-[CFPrefsSearchListSource 
alreadylocked_getDictionary:] + 492
    frame #6: 0x0000000182e847dc CoreFoundation`-[CFPrefsSearchListSource 
alreadylocked_copyValueForKey:] + 172
    frame #7: 0x0000000182e84710 CoreFoundation`-[CFPrefsSource 
copyValueForKey:] + 52
    frame #8: 0x0000000182e846c4 CoreFoundation`__76-[_CFXPreferences 
copyAppValueForKey:identifier:container:configurationURL:]_block_invoke + 32
    frame #9: 0x0000000182e7dd00 
CoreFoundation`__108-[_CFXPreferences(SearchListAdditions) 
withSearchListForIdentifier:container:cloudConfigurationURL:perform:]_block_invoke
 + 376
    frame #10: 0x00000001830013a0 CoreFoundation`-[_CFXPreferences 
withSearchListForIdentifier:container:cloudConfigurationURL:perform:] + 384
    frame #11: 0x0000000182e7d5d8 CoreFoundation`-[_CFXPreferences 
copyAppValueForKey:identifier:container:configurationURL:] + 156
    frame #12: 0x0000000182e7d500 
CoreFoundation`_CFPreferencesCopyAppValueWithContainerAndConfiguration + 112
    frame #13: 0x00000001005c7704 libintl.8.dylib`_libintl_locale_name_default 
at localename-unsafe.c:3406:11 [opt]
    frame #14: 0x00000001005c6734 
libintl.8.dylib`libintl_setlocale(category=<unavailable>, locale=<unavailable>) 
at setlocale.c:1446:25 [opt]
    frame #15: 0x0000000100064df4 bash`reset_locale_vars at locale.c:369:7 [opt]
    frame #16: 0x00000001000650ec bash`set_lang(var=<unavailable>, 
value=<unavailable>) at locale.c:318:43 [opt] [artificial]
    frame #17: 0x0000000100028314 bash`sv_locale(name="LANG") at 
variables.c:6341:9 [opt]
    frame #18: 0x000000010006ee74 
bash`declare_internal(list=0x0000600000af7520, local_var=1) at 
declare.def:1048:7 [opt]
    frame #19: 0x000000010006f074 bash`local_builtin(list=<unavailable>) at 
declare.def:134:13 [opt] [artificial]
    frame #20: 0x000000010001f1b0 bash`execute_builtin_or_function [inlined] 
execute_builtin(builtin=(bash`local_builtin at declare.def:125), 
words=0x0000600000af7440, flags=<unavailable>, subshell=0) at 
execute_cmd.c:4974:13 [opt]
    frame #21: 0x000000010001eea4 
bash`execute_builtin_or_function(words=0x0000600000af7440, 
builtin=(bash`local_builtin at declare.def:125), var=0x0000000000000000, 
redirects=<unavailable>, fds_to_close=<unavailable>, flags=<unavailable>) at 
execute_cmd.c:5488:14 [opt]
    frame #22: 0x000000010001a27c 
bash`execute_simple_command(simple_command=<unavailable>, 
pipe_in=<unavailable>, pipe_out=-1, async=<unavailable>, 
fds_to_close=0x0000600000af7250) at execute_cmd.c:4740:13 [opt]
    frame #23: 0x00000001000181a4 
bash`execute_command_internal(command=0x00006000008daec0, asynchronous=0, 
pipe_in=-1, pipe_out=-1, fds_to_close=0x0000600000af7250) at 
execute_cmd.c:866:4 [opt]
    frame #24: 0x00000001000174d0 bash`execute_command(command=<unavailable>) 
at execute_cmd.c:413:12 [opt]
    frame #25: 0x000000010001ba70 
bash`execute_connection(command=0x00006000008dae80, asynchronous=0, pipe_in=-1, 
pipe_out=-1, fds_to_close=0x0000600000af7230) at execute_cmd.c:2757:7 [opt]
    frame #26: 0x0000000100017ff0 
bash`execute_command_internal(command=0x00006000008dae80, asynchronous=0, 
pipe_in=-1, pipe_out=-1, fds_to_close=0x0000600000af7230) at 
execute_cmd.c:1040:21 [opt]
    frame #27: 0x0000000100018244 
bash`execute_command_internal(command=0x00006000008dae60, asynchronous=0, 
pipe_in=-1, pipe_out=-1, fds_to_close=0x0000600000af7230) at execute_cmd.c:0 
[opt]
    frame #28: 0x000000010001bab0 
bash`execute_connection(command=0x00006000008dade0, asynchronous=0, pipe_in=-1, 
pipe_out=-1, fds_to_close=0x0000600000af7230) at execute_cmd.c:2766:21 [opt]
    frame #29: 0x0000000100017ff0 
bash`execute_command_internal(command=0x00006000008dade0, asynchronous=0, 
pipe_in=-1, pipe_out=-1, fds_to_close=0x0000600000af7230) at 
execute_cmd.c:1040:21 [opt]
    frame #30: 0x00000001000174d0 bash`execute_command(command=<unavailable>) 
at execute_cmd.c:413:12 [opt]
    frame #31: 0x000000010001ba70 
bash`execute_connection(command=0x00006000008dada0, asynchronous=0, pipe_in=-1, 
pipe_out=-1, fds_to_close=0x0000600000af4de0) at execute_cmd.c:2757:7 [opt]
    frame #32: 0x0000000100017ff0 
bash`execute_command_internal(command=0x00006000008dada0, asynchronous=0, 
pipe_in=-1, pipe_out=-1, fds_to_close=0x0000600000af4de0) at 
execute_cmd.c:1040:21 [opt]
    frame #33: 0x0000000100018244 
bash`execute_command_internal(command=0x00006000008dad80, asynchronous=0, 
pipe_in=-1, pipe_out=-1, fds_to_close=0x0000600000af4de0) at execute_cmd.c:0 
[opt]
    frame #34: 0x000000010001d744 bash`execute_function(var=0x00006000006ac570, 
words=0x0000600000af67a0, flags=<unavailable>, fds_to_close=0x0000600000af4de0, 
async=<unavailable>, subshell=0) at execute_cmd.c:5245:13 [opt]
    frame #35: 0x000000010001ee9c 
bash`execute_builtin_or_function(words=0x0000600000af67a0, 
builtin=0x0000000000000000, var=0x00006000006ac570, redirects=<unavailable>, 
fds_to_close=0x0000600000af4de0, flags=0) at execute_cmd.c:5490:14 [opt]
    frame #36: 0x000000010001a580 
bash`execute_simple_command(simple_command=<unavailable>, 
pipe_in=<unavailable>, pipe_out=-1, async=<unavailable>, 
fds_to_close=0x0000600000af4de0) at execute_cmd.c:4740:13 [opt]
    frame #37: 0x00000001000181a4 
bash`execute_command_internal(command=0x00006000008dabe0, asynchronous=0, 
pipe_in=-1, pipe_out=-1, fds_to_close=0x0000600000af4de0) at 
execute_cmd.c:866:4 [opt]
    frame #38: 0x0000000100070f60 bash`parse_and_execute(string=<unavailable>, 
from_file="PROMPT_COMMAND", flags=1029) at evalstring.c:539:17 [opt]
    frame #39: 0x00000001000094b8 
bash`execute_variable_command(command="update_terminal_cwd", 
vname="PROMPT_COMMAND") at parse.y:2845:3 [opt]
    frame #40: 0x00000001000066a0 bash`parse_command [inlined] 
execute_prompt_command at eval.c:315:5 [opt]
    frame #41: 0x0000000100006660 bash`parse_command at eval.c:341:9 [opt]
    frame #42: 0x000000010000643c bash`read_command at eval.c:392:12 [opt]
    frame #43: 0x00000001000061ec bash`reader_loop at eval.c:139:11 [opt]
    frame #44: 0x0000000100004c94 bash`main(argc=2, argv=<unavailable>, 
env=<unavailable>) at shell.c:833:3 [opt]
    frame #45: 0x0000000182a6a0e0 dyld`start + 2360
```

Back-trace of the second:

```
 thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
  * frame #0: 0x0000000182dbf0ac libsystem_kernel.dylib`__abort_with_payload + 8
    frame #1: 0x0000000182de4f08 
libsystem_kernel.dylib`abort_with_payload_wrapper_internal + 104
    frame #2: 0x0000000182de4ea0 libsystem_kernel.dylib`abort_with_reason + 32
    frame #3: 0x0000000182a520f8 libobjc.A.dylib`_objc_fatalv(unsigned long 
long, unsigned long long, char const*, char*) + 128
    frame #4: 0x0000000182a52078 libobjc.A.dylib`_objc_fatal(char const*, ...) 
+ 44
    frame #5: 0x0000000182a38460 
libobjc.A.dylib`performForkChildInitialize(objc_class*, objc_class*) + 400
    frame #6: 0x0000000182a1eba4 libobjc.A.dylib`initializeNonMetaClass + 572
    frame #7: 0x0000000182a1ea04 libobjc.A.dylib`initializeNonMetaClass + 156
    frame #8: 0x0000000182a1ea04 libobjc.A.dylib`initializeNonMetaClass + 156
    frame #9: 0x0000000182a3bbfc 
libobjc.A.dylib`initializeAndMaybeRelock(objc_class*, objc_object*, 
locker_mixin<lockdebug::lock_mixin<objc_lock_base_t>>&, bool) + 164
    frame #10: 0x0000000182a1e5c4 libobjc.A.dylib`lookUpImpOrForward + 892
    frame #11: 0x0000000182a1df64 libobjc.A.dylib`_objc_msgSend_uncached + 68
    frame #12: 0x0000000192dfbbc8 libswiftCore.dylib`type metadata accessor for 
Swift.__StringStorage + 24
    frame #13: 0x0000000192b85f24 
libswiftCore.dylib`Swift.String._bridgeToObjectiveCImpl() -> Swift.AnyObject + 
152
    frame #14: 0x0000000184409f80 Foundation`function signature specialization 
<Arg[1] = Dead> of Foundation.LocaleCache.preferredLanguages(forCurrentUser: 
Swift.Bool) -> Swift.Array<Swift.String> + 76
    frame #15: 0x00000001844ec9c4 Foundation`@objc static 
__C.NSLocale._preferredLanguagesForCurrentUser(Swift.Bool) -> 
Swift.Array<Swift.String> + 44
    frame #16: 0x0000000182ece0b4 CoreFoundation`CFLocaleCopyPreferredLanguages 
+ 28
    frame #17: 0x0000000101386174 
libintl.8.dylib`_libintl_language_preferences_default at langprefs.c:253:32 
[opt]
    frame #18: 0x0000000101384ac8 libintl.8.dylib`libintl_dcigettext [inlined] 
guess_category_value(category=6, categoryname="LC_MESSAGES") at 
dcigettext.c:1643:26 [opt]
    frame #19: 0x0000000101384a68 
libintl.8.dylib`libintl_dcigettext(domainname="bash", msgid1="%s: command not 
found", msgid2=0x0000000000000000, plural=0, n=0, category=<unavailable>) at 
dcigettext.c:710:19 [opt]
    frame #20: 0x0000000101382338 
libintl.8.dylib`libintl_dcgettext(domainname=<unavailable>, 
msgid=<unavailable>, category=<unavailable>) at dcgettext.c:47:10 [opt] 
[artificial]
    frame #21: 0x0000000101382350 
libintl.8.dylib`libintl_gettext(msgid=<unavailable>) at gettext.c:55:10 [opt] 
[artificial]
    frame #22: 0x0000000100ddf658 
bash`execute_disk_command(words=0x00006000029e8e70, 
redirects=0x0000000000000000, command_line="derp", pipe_in=<unavailable>, 
pipe_out=<unavailable>, async=<unavailable>, fds_to_close=0x000060000298c040, 
cmdflags=0) at execute_cmd.c:5731:24 [opt]
    frame #23: 0x0000000100dda378 
bash`execute_simple_command(simple_command=<unavailable>, pipe_in=-1, 
pipe_out=-1, async=<unavailable>, fds_to_close=0x000060000298c040) at 
execute_cmd.c:4814:12 [opt]
    frame #24: 0x0000000100dd8188 
bash`execute_command_internal(command=0x0000600002b800a0, asynchronous=0, 
pipe_in=-1, pipe_out=-1, fds_to_close=0x000060000298c040) at 
execute_cmd.c:866:4 [opt]
    frame #25: 0x0000000100dd74b4 bash`execute_command(command=<unavailable>) 
at execute_cmd.c:413:12 [opt]
    frame #26: 0x0000000100dc62c4 bash`reader_loop at eval.c:171:8 [opt]
    frame #27: 0x0000000100dc4c78 bash`main(argc=2, argv=<unavailable>, 
env=<unavailable>) at shell.c:833:3 [opt]
    frame #28: 0x0000000182a6a0e0 dyld`start + 2360
```


Repeat-By:

```
#!/usr/bin/env bash

set -eu

fetch_gettext () {
  wget --continue 
https://mirror.endianness.com/gnu/gettext/gettext-0.22.5.tar.gz
}

fetch_bash () {
  wget --continue https://mirror.endianness.com/gnu/bash/bash-5.2.tar.gz
  mkdir -p bash-5.2-patches
  pushd bash-5.2-patches
  for i in $(seq -w 01 26)
  do
    wget --continue 
https://mirror.endianness.com/gnu/bash/bash-5.2-patches/bash52-0${i}
  done
  popd
}

fetch () {
  mkdir -p assets
  pushd assets
  
  fetch_gettext
  
  fetch_bash
  
  popd
}

build_gettext () {
  prefix=${1}

  tar -xzf ../assets/gettext-0.22.5.tar.gz
  
  pushd gettext-0.22.5/gettext-runtime
  ./configure --prefix=${prefix} --silent
  make --jobs 12 --silent install
  popd
}

build_bash () {
  prefix=${1}

  tar -xzf ../assets/bash-5.2.tar.gz

  pushd bash-5.2
  for i in $(seq -w 01 26)
  do
    patch -p0 < ../../assets/bash-5.2-patches/bash52-0${i}
  done
  
  LDFLAGS=-L${prefix}/lib ./configure --silent
  make --jobs 12 --silent
  popd
}

build () {
  prefix=$(mktemp -d)
  
  rm -rf build
  mkdir build
  pushd build

  build_gettext ${prefix}

  build_bash ${prefix}

  popd
}

fetch
build

./build/bash-5.2/bash -l  # execute invalid command
```


Regards,

Tom Sullivan

Head Developer
Most Significant Bit Software

e:  tom@msbit.com.au
p:  +61 407 890 821



reply via email to

[Prev in Thread] Current Thread [Next in Thread]