Opened 12 months ago
Last modified 5 weeks ago
#68638 assigned defect
bash @5.2.15: When a "command not found" situation happens, Bash prints errors and ends with "Abort Trap: 6" instead of printing "Command not found"
Reported by: | some1so | Owned by: | raimue (Rainer Müller) |
---|---|---|---|
Priority: | Normal | Milestone: | |
Component: | ports | Version: | |
Keywords: | Cc: | mascguy (Christopher Nielsen), joel-coffman (Joel Coffman), kpreid (Kevin Reid), i0ntempest | |
Port: | bash |
Description (last modified by some1so)
For example, using 'derp' as a fake command (macports 5.2.15(1)-release):
~ $ derp objc[75690]: +[__SwiftNativeNSStringBase initialize] may have been in progress in another thread when fork() was called. objc[75690]: +[__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
expected behavior (Apple supplied 3.2.57):
~ $ derp bash: derp: command not found ~ $
Change History (31)
comment:1 Changed 12 months ago by some1so
Description: | modified (diff) |
---|
comment:2 Changed 12 months ago by ryandesign (Ryan Carsten Schmidt)
Owner: | set to raimue |
---|---|
Status: | new → assigned |
Summary: | When a "command not found" situation happens, Bash prints errors and ends with "Abort Trap: 6" instead of printing "Command not found" → bash @5.2.15: When a "command not found" situation happens, Bash prints errors and ends with "Abort Trap: 6" instead of printing "Command not found" |
comment:3 Changed 12 months ago by raimue (Rainer Müller)
comment:4 Changed 12 months ago by some1so
Hello
I tested this again. I'm using Apple Terminal.app, so I went into settings and changed the shell to "/opt/local/bin/bash --noprofile --norc". I then created a new window, and tested, and got a good result ("Command not found").
I then renamed my local bash_profile and bashrc files to get them out of the way, and set Terminal.app default shell to "/opt/local/bin/bash", opened a new window and tested again, and got a bad result (the same Swift error).
Next, I switch Terminal.app default shell to use the system default (/bin/bash). Open a new window and test. Good result. I then start a new session in the shell by executing "/opt/local/bin/bash", and test again. Good result.
So the only way to cause the error is to set Terminal.app to use "/opt/local/bin/bash".
Maybe of interest - when I am in a session and getting the error, if I use "Ctrl-X Ctrl-V" to print the shell version, and then test again, I get a good result. Somehow that clears whatever is causing the error.
comment:5 Changed 12 months ago by kickingvegas (Charles Choi)
FWIW, I'm seeing the same issue. Apparently setting Terminal.app to use /opt/local/bin/bash will emit the following message for unknown commands entered.
$ jsdf objc[53830]: +[__SwiftNativeNSStringBase initialize] may have been in progress in another thread when fork() was called. objc[53830]: +[__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
comment:6 Changed 12 months ago by raimue (Rainer Müller)
This must be coming from libc/libSystem and not from bash itself. You might be able to use export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
as a workaround (although I am not sure if this actually works when running as a login shell in Terminal.app).
Does this problem also occur when bash is started as login shell with /opt/local/bin/bash -l
? That might make debugging easier as we would be able to attach dtruss to see which system calls are responsible for this behavior.
comment:7 Changed 12 months ago by raimue (Rainer Müller)
It might actually still be the same problem as comment:ticket:41248:13. Can you check whether LANG
, LC_MESSAGES
and LC_ALL
are all unset in your environment?
comment:8 Changed 12 months ago by some1so
Hello, here are the results - I start off using /bin/bash and then start a login shell with the macports version.
~ $ echo $LANG, $LC_MESSAGES, $LC_ALL en_US.UTF-8, , ~ $ /opt/local/bin/bash -l Restored session: Tue Nov 14 17:41:17 PST 2023 ~ $ echo $LANG, $LC_MESSAGES, $LC_ALL en_US.UTF-8, , ~ $ derp objc[7696]: +[__SwiftNativeNSStringBase initialize] may have been in progress in another thread when fork() was called. objc[7696]: +[__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 ~ $ export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES ~ $ derp objc[7697]: +[__SwiftNativeNSStringBase initialize] may have been in progress in another thread when fork() was called. objc[7697]: +[__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 ~ $ echo $OBJC_DISABLE_INITIALIZE_FORK_SAFETY YES
comment:9 Changed 12 months ago by cculianu (Calin Culianu)
I have the same exact issue.
Any ideas? Should I rebuild bash from source?
comment:10 Changed 10 months ago by kickingvegas (Charles Choi)
Still seeing this issue with GNU bash, version 5.2.26(1)-release (aarch64-apple-darwin23.2.0)
comment:11 Changed 8 months ago by DarrenStone (Darren Stone)
Call stack:
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 0 libsystem_kernel.dylib 0x1864de72c __abort_with_payload + 8 1 libsystem_kernel.dylib 0x186503f88 abort_with_payload_wrapper_internal + 104 2 libsystem_kernel.dylib 0x186503f20 abort_with_reason + 32 3 libobjc.A.dylib 0x186179484 _objc_fatalv(unsigned long long, unsigned long long, char const*, char*) + 128 4 libobjc.A.dylib 0x186179404 _objc_fatal(char const*, ...) + 44 5 libobjc.A.dylib 0x18615fc1c performForkChildInitialize(objc_class*, objc_class*) + 400 6 libobjc.A.dylib 0x186146390 initializeNonMetaClass + 572 7 libobjc.A.dylib 0x1861461f0 initializeNonMetaClass + 156 8 libobjc.A.dylib 0x1861461f0 initializeNonMetaClass + 156 9 libobjc.A.dylib 0x186163304 initializeAndMaybeRelock(objc_class*, objc_object*, locker_mixin<lockdebug::lock_mixin<objc_lock_base_t>>&, bool) + 164 10 libobjc.A.dylib 0x186145dc4 lookUpImpOrForward + 892 11 libobjc.A.dylib 0x186145764 _objc_msgSend_uncached + 68 12 libswiftCore.dylib 0x1961145e0 type metadata accessor for __StringStorage + 24 13 libswiftCore.dylib 0x195eb63e8 String._bridgeToObjectiveCImpl() + 156 14 Foundation 0x187911930 specialized LocaleCache.preferredLanguages(forCurrentUser:) + 76 15 Foundation 0x187afd428 @objc static NSLocale._preferredLanguagesForCurrentUser(_:) + 44 16 CoreFoundation 0x1865ebbe4 CFLocaleCopyPreferredLanguages + 28 17 libintl.8.dylib 0x100ad93dc _libintl_language_preferences_default + 68 18 libintl.8.dylib 0x100ad7d40 libintl_dcigettext + 812 19 bash 0x10057123c execute_disk_command + 696 20 bash 0x10056c870 execute_simple_command + 3688 21 bash 0x10056a8e0 execute_command_internal + 3056 22 bash 0x100569c7c execute_command + 96 23 bash 0x10055a77c reader_loop + 728 24 bash 0x100559238 main + 4788 25 dyld 0x1861910e0 start + 2360
This has been a long-standing problem. See this comment: comment:ticket:41248:13
However, the problem occurs all of the time, not just when LANG is unset.
Bash uses 'gettext' to generate localized error messages. If a child process calls 'gettext' before the parent process has initialzied the localization library then the child process will crash.
You can work around the problem by forcing the parent process to generate a localized error message. Once that is done, child processes can produce localized text correctly. For example, you can type ";" on a single line to generate a localized error message from the parent bash process:
$ foo objc[10554]: +[__SwiftNativeNSStringBase initialize] may have been in progress in another thread when fork() was called. objc[10554]: +[__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 $ ; -bash: syntax error near unexpected token `;' $ foo -bash: foo: command not found
comment:12 Changed 8 months ago by mascguy (Christopher Nielsen)
Cc: | mascguy added |
---|
comment:13 Changed 7 months ago by joel-coffman (Joel Coffman)
Cc: | joel-coffman added |
---|
comment:14 Changed 7 months ago by msbit (Tom Sullivan)
This appears to be happening always with macOS 14, whereas it didn't happen on macOS 13.
Would it be worth adding the --with-included-gettext
configure argument? This seems to "fix" the issue for me when compiled locally.
comment:15 Changed 7 months ago by ryandesign (Ryan Carsten Schmidt)
Is it the included gettext that's the problem? The bash port declares a dependency on the gettext port, so I would expect it to be using that gettext, not the included one.
comment:16 follow-up: 21 Changed 7 months ago by msbit (Tom Sullivan)
Is it the included gettext that's the problem?
Using the in-tree intl
implementation(1) by passing --with-included-gettext
to ./configure
doesn't exhibit this problem. My guess is that it doesn't call into any Apple provided APIs (in this case CFLocaleCopyPreferredLanguages
as shown in @DarrenStone's back trace) which have the specific check around Objc initialize
during fork
.
so I would expect it to be using that gettext, not the included one
Yep, it appears to be, the distributed binary looks something like this:
$ otool -L $(which bash) /opt/local/bin/bash: /opt/local/lib/libncurses.6.dylib (compatibility version 6.0.0, current version 6.0.0) /opt/local/lib/libintl.8.dylib (compatibility version 12.0.0, current version 12.0.0) /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1858.112.0) /opt/local/lib/libiconv.2.dylib (compatibility version 9.0.0, current version 9.1.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1311.100.3)
(1) https://git.savannah.gnu.org/cgit/bash.git/tree/lib/intl?h=bash-5.2
comment:17 Changed 7 months ago by bradleyCPA (B. Holder)
That seems like it would work until they rev the in-tree gettext in the next release. https://git.savannah.gnu.org/cgit/bash.git/tree/lib/intl?h=bash-5.3-testing
comment:18 Changed 6 months ago by msbit (Tom Sullivan)
That seems like it would work until they rev the in-tree gettext in the next release. After which it would once again be broken.
Is there anything specifically in that branch that would cause that? I'm able to get a failing build by invoking:
LDFLAGS=-L/opt/local/lib ./configure
and a working build by invoking:
LDFLAGS=-L/opt/local/lib ./configure --with-included-gettext
on each of the following branches/tags
- devel
- bash-5.3-alpha
- bash-5.3-testing
Test script:
#!/usr/bin/env bash set -eu git status --porcelain --ignored=matching | grep '^!!' | awk '{print $2}' | xargs rm -rf git checkout -- . LDFLAGS=-L/opt/local/lib ./configure --silent ${@} make --jobs 12 --silent otool -L bash ./bash -l
comment:19 follow-up: 23 Changed 6 months ago by kencu (Ken)
the presumption is that the gettext included in bash is old, and once it is updated to be as new as the one currently in macports, it will exhibit the same error.
comment:20 Changed 6 months ago by ednl (Ewoud Dronkert)
Same for me on an M1 with everything up-to-date: Sonoma 14.4.1, port 2.9.3, bash 5.2.26_0, gettext 0.22.5_0. Hopefully a definitive fix can be decided upon.
comment:21 follow-up: 24 Changed 6 months ago by ryandesign (Ryan Carsten Schmidt)
Replying to msbit:
Is it the included gettext that's the problem?
Using the in-tree
intl
implementation(1) by passing--with-included-gettext
to./configure
doesn't exhibit this problem.
Sorry, I misread your suggestion. I thought you were suggesting --without-included-gettext
which didn't make sense to me.
Using --with-included-gettext
would be undesirable. We want to use MacPorts gettext which should be the most up-to-date and thus contain the most bug fixes. If there is a bug in this latest gettext that's causing this, let's fix it, or rather, report it to the developers of gettext so they can fix it and release a new version and we can then update to it.
comment:22 follow-up: 25 Changed 6 months ago by bradleyCPA (B. Holder)
I suppose the problem lies at the intersection of gettext() usage after fork() and before exec() in bash [execute_cmd.c in bash source] in combination with setlocale() function replacement [libintl.h, "define setlocale libintl_setlocale", gettext-runtime/intl/setlocale.c in gettext source]. It seems that there must be some scenario or scenarios where calling into Apple's APIs in the gettext-provided setlocale() replacement spawns a thread so that after the fork() [in bash] calling back into Apple's APIs using CFLocaleCopyPreferredLanguages (or similar) in the gettext() call results in the forked process terminating.
comment:23 Changed 6 months ago by msbit (Tom Sullivan)
Replying to kencu:
the presumption is that the gettext included in bash is old, and once it is updated to be as new as the one currently in macports, it will exhibit the same error.
👍
comment:24 Changed 6 months ago by msbit (Tom Sullivan)
Replying to ryandesign:
Replying to msbit:
Is it the included gettext that's the problem?
Using the in-tree
intl
implementation(1) by passing--with-included-gettext
to./configure
doesn't exhibit this problem.Sorry, I misread your suggestion. I thought you were suggesting
--without-included-gettext
which didn't make sense to me.Using
--with-included-gettext
would be undesirable. We want to use MacPorts gettext which should be the most up-to-date and thus contain the most bug fixes. If there is a bug in this latest gettext that's causing this, let's fix it, or rather, report it to the developers of gettext so they can fix it and release a new version and we can then update to it.
👍
comment:25 Changed 6 months ago by msbit (Tom Sullivan)
Replying to bradleyCPA:
I suppose the problem lies at the intersection of gettext() usage after fork() and before exec() in bash [execute_cmd.c in bash source] in combination with setlocale() function replacement [libintl.h, "define setlocale libintl_setlocale", gettext-runtime/intl/setlocale.c in gettext source]. It seems that there must be some scenario or scenarios where calling into Apple's APIs in the gettext-provided setlocale() replacement spawns a thread so that after the fork() [in bash] calling back into Apple's APIs using CFLocaleCopyPreferredLanguages (or similar) in the gettext() call results in the forked process terminating.
From what I can tell, this is what happens:
int main() { // via bash`reset_locale_vars > libintl.8.dylib`libintl_setlocale > .`_libintl_locale_name_default CFPreferencesCopyAppValue(CFSTR("AppleLocale"), kCFPreferencesCurrentApplication); if (fork() == 0) { // via bash`execute_disk_command > libintl.8.dylib`libintl_gettext > .`libintl_dcgettext > .`libintl_dcigettext > .`guess_category_value > .`_libintl_language_preferences_default CFLocaleCopyPreferredLanguages(); } }
CFPreferencesCopyAppValue
spins up a thread (one of the GCD worker threads) which triggers this forced crash whenever an objc +initialize
method is called (as occurs within CFLocaleCopyPreferredLanguages
).
comment:26 Changed 6 months ago by bradleyCPA (B. Holder)
That looks pretty spot on to me. The larger problem is bash is now forking [and doing unsafe things -- https://www.man7.org/linux/man-pages/man2/fork.2.html -- in the forked process before exec()] while multithreaded which is a barmy idea and probably a completely unintended side-effect of relying on gnu gettext.
To come back to this, I'd just add that I've never actually seen the bad behavior referenced here occur myself but maybe you could do something like:
#include <unistd.h> #include <CoreFoundation/CFPreferences.h> #include <objc/objc-runtime.h> int main() { Class __SwiftNativeNSStringBase = objc_getClass("__SwiftNativeNSStringBase"); id funAlloc = ((id (*)(Class, SEL))objc_msgSend) (__SwiftNativeNSStringBase, sel_registerName("alloc")); // via bash`reset_locale_vars > libintl.8.dylib`libintl_setlocale > .`_libintl_locale_name_default CFTypeRef preferences = CFPreferencesCopyAppValue(CFSTR("AppleLocale"), kCFPreferencesCurrentApplication); if (fork()== 0) { // via bash`execute_disk_command > libintl.8.dylib`libintl_gettext > .`libintl_dcgettext > .`libintl_dcigettext > .`guess_category_value > .`_libintl_language_preferences_default CFArrayRef prefArray = CFLocaleCopyPreferredLanguages(); } } // compile like clang -g -framework Foundation -o main main.c // run like OBJC_PRINT_INITIALIZE_METHODS=YES ./main 2>&1 | grep "__SwiftNativeNSStringBase"
to force the __SwiftNativeNSStringBase
class initializer to run before the fork(). I'd guess that the workaround in comment:11 does something like that but not sure and YMMV.
comment:27 Changed 4 months ago by kpreid (Kevin Reid)
Cc: | kpreid added |
---|
comment:28 follow-up: 29 Changed 2 months ago by jamesderlin (James D. Lin)
For anyone else looking for a simple, automatic workaround:
The workaround from comment:11 (running ;
first) worked for me, but it's not obvious how to suppress the error message so that it could be done automatically in from .bashrc
. The same thing applies to the workaround from https://trac.macports.org/ticket/41248#comment:11 (running trap [a-z]
first). However, understanding that you just need to get bash to print a localized message (not necessarily an error message), generating the help to most bash builtins also should work (e.g. cd --help
, set --help
, etc.), and those get printed to stdout which can be easily redirected to /dev/null
. In my .bashrc
, I use: help help > /dev/null
(along with a comment referencing this bug).
comment:29 Changed 8 weeks ago by i0ntempest
Replying to jamesderlin:
For anyone else looking for a simple, automatic workaround:
The workaround from comment:11 (running
;
first) worked for me, but it's not obvious how to suppress the error message so that it could be done automatically in from.bashrc
. The same thing applies to the workaround from https://trac.macports.org/ticket/41248#comment:11 (runningtrap [a-z]
first). However, understanding that you just need to get bash to print a localized message (not necessarily an error message), generating the help to most bash builtins also should work (e.g.cd --help
,set --help
, etc.), and those get printed to stdout which can be easily redirected to/dev/null
. In my.bashrc
, I use:help help > /dev/null
(along with a comment referencing this bug).
Does not seem to work for me. I put the command into .profile
but I still get a crash when I run a nonexistent command. Running the command in the shell after initialization does work though.
comment:30 Changed 8 weeks ago by i0ntempest
Cc: | i0ntempest added |
---|
comment:31 Changed 5 weeks ago by bradleyCPA (B. Holder)
You want https://git.savannah.gnu.org/cgit/bash.git/commit/?h=devel&id=b3d8c8a4e7c5417d986f93f646ea740cb13c08d7. Specifically, the init_notfound_str junk, probably, I haven't tested. I could never reproduce anyway.
Do you have anything configured as
PROMPT_COMMAND
in bash that could cause this? Can you reproduce the issue also without your custom bashrc, i.e.bash --norc --noprofile
?