From 706bfea476a013bb8cf7566666699eb331876d7f Mon Sep 17 00:00:00 2001
From: Ilya Verbin <iverbin@tarantool.org>
Date: Thu, 22 Dec 2022 17:10:33 +0300
Subject: [PATCH] core: unblock fatal (crashing) signals in all threads

Currently all non-main threads have all the signals blocked, however
according to `man pthread_sigmask':

> If SIGBUS, SIGFPE, SIGILL, or SIGSEGV are generated while they
> are blocked, the result is undefined, unless the signal was
> generated by kill(2), sigqueue(3), or raise(3).

On macOS they are actually blocked, causing the faulting instruction
to loop indefinitely. While on Linux they are not blocked, however the
signal handler registered by sigaction is not executed. Don't block them.

Closes #8023
Closes #8083

NO_DOC=bugfix
---
 changelogs/unreleased/gh-8083-fatal-signal-handler.md  |  5 +++++
 src/lib/core/errinj.h                                  |  1 +
 src/lib/core/fiber.c                                   |  1 +
 src/tt_pthread.h                                       | 10 ++++++++--
 test/app-luatest/gh_8083_fatal_signal_handler_test.lua |  3 ++-
 test/box/errinj.result                                 |  1 +
 6 files changed, 18 insertions(+), 3 deletions(-)
 create mode 100644 changelogs/unreleased/gh-8083-fatal-signal-handler.md

diff --git a/changelogs/unreleased/gh-8083-fatal-signal-handler.md b/changelogs/unreleased/gh-8083-fatal-signal-handler.md
new file mode 100644
index 0000000000..86fb63db63
--- /dev/null
+++ b/changelogs/unreleased/gh-8083-fatal-signal-handler.md
@@ -0,0 +1,5 @@
+## bugfix/core
+
+* Fixed Tarantool being stuck during a crash on macOS (gh-8023).
+
+* Fixed a bug that prevents crash report being collected (gh-8083).
diff --git a/src/lib/core/errinj.h b/src/lib/core/errinj.h
index 6861b9c5d6..6606f651d7 100644
--- a/src/lib/core/errinj.h
+++ b/src/lib/core/errinj.h
@@ -119,6 +119,7 @@ struct errinj {
 	_(ERRINJ_REPLICASET_VCLOCK, ERRINJ_BOOL, {.bparam = false}) \
 	_(ERRINJ_REPLICA_JOIN_DELAY, ERRINJ_BOOL, {.bparam = false}) \
 	_(ERRINJ_SIGILL_MAIN_THREAD, ERRINJ_BOOL, {.bparam = false}) \
+	_(ERRINJ_SIGILL_NONMAIN_THREAD, ERRINJ_BOOL, {.bparam = false}) \
 	_(ERRINJ_SIO_READ_MAX, ERRINJ_INT, {.iparam = -1}) \
 	_(ERRINJ_SNAP_COMMIT_DELAY, ERRINJ_BOOL, {.bparam = false}) \
 	_(ERRINJ_SNAP_COMMIT_FAIL, ERRINJ_BOOL, {.bparam = false}) \
diff --git a/src/lib/core/fiber.c b/src/lib/core/fiber.c
index 2a6dc541cb..2159d8048d 100644
--- a/src/lib/core/fiber.c
+++ b/src/lib/core/fiber.c
@@ -1732,6 +1732,7 @@ void *cord_thread_func(void *p)
 	ct_arg->is_started = true;
 	tt_pthread_cond_signal(&ct_arg->start_cond);
 	tt_pthread_mutex_unlock(&ct_arg->start_mutex);
+	ERROR_INJECT_SIGILL(ERRINJ_SIGILL_NONMAIN_THREAD);
 	void *res = f(arg);
 	/*
 	 * cord()->on_exit initially holds NULL. This field is
diff --git a/src/tt_pthread.h b/src/tt_pthread.h
index 70f6050912..39c863a985 100644
--- a/src/tt_pthread.h
+++ b/src/tt_pthread.h
@@ -264,12 +264,18 @@
 	tt_pthread_error(e__);			\
 })
 
-/** Make sure the created thread blocks all signals,
- * they are handled in the main thread.
+/**
+ * Make sure the created thread blocks all signals, they are handled in the main
+ * thread. Except SIGILL, SIGBUS, SIGFPE and SIGSEGV, that cannot be blocked and
+ * are handled by the thread that triggered them (see crash_signal_init).
  */
 #define tt_pthread_create(thread, attr, run, arg)	\
 ({	sigset_t set, oldset;				\
 	sigfillset(&set);				\
+	sigdelset(&set, SIGILL);			\
+	sigdelset(&set, SIGBUS);			\
+	sigdelset(&set, SIGFPE);			\
+	sigdelset(&set, SIGSEGV);			\
 	pthread_sigmask(SIG_BLOCK, &set, &oldset);	\
 	int e__ = pthread_create(thread, attr, run, arg);\
 	pthread_sigmask(SIG_SETMASK, &oldset, NULL);	\
diff --git a/test/app-luatest/gh_8083_fatal_signal_handler_test.lua b/test/app-luatest/gh_8083_fatal_signal_handler_test.lua
index b7b2f0db18..8e401ea71d 100644
--- a/test/app-luatest/gh_8083_fatal_signal_handler_test.lua
+++ b/test/app-luatest/gh_8083_fatal_signal_handler_test.lua
@@ -1,5 +1,6 @@
 local t = require('luatest')
-local g = t.group('gh-8083', {{errinj = 'ERRINJ_SIGILL_MAIN_THREAD'}})
+local g = t.group('gh-8083', {{errinj = 'ERRINJ_SIGILL_MAIN_THREAD'},
+                              {errinj = 'ERRINJ_SIGILL_NONMAIN_THREAD'}})
 
 g.before_each(function(cg)
     local server = require('luatest.server')
diff --git a/test/box/errinj.result b/test/box/errinj.result
index b4d7982f41..316462b751 100644
--- a/test/box/errinj.result
+++ b/test/box/errinj.result
@@ -94,6 +94,7 @@ evals
   - ERRINJ_REPLICASET_VCLOCK: false
   - ERRINJ_REPLICA_JOIN_DELAY: false
   - ERRINJ_SIGILL_MAIN_THREAD: false
+  - ERRINJ_SIGILL_NONMAIN_THREAD: false
   - ERRINJ_SIO_READ_MAX: -1
   - ERRINJ_SNAP_COMMIT_DELAY: false
   - ERRINJ_SNAP_COMMIT_FAIL: false
-- 
GitLab