when we use raise in GCD, the signal handler is executed asynchronously, whereas in pthread, it is executed synchronously as expected.
example:
#include <Foundation/Foundation.h>
#include <pthread/pthread.h>
static void HandleSignal(int sigNum, siginfo_t* signalInfo, void* userContext) {
printf("handle signal %d\n", sigNum);
printf("begin sleep\n");
sleep(3);
printf("end sleep\n");
}
void InstallSignal(void) {
static const int g_fatalSignals[] =
{
SIGABRT,
SIGBUS,
SIGFPE,
SIGILL,
SIGPIPE,
SIGSEGV,
SIGSYS,
SIGTRAP,
};
int fatalSignalsCount = sizeof(g_fatalSignals) / sizeof(int);
struct sigaction action = {{0}};
action.sa_flags = SA_SIGINFO | SA_ONSTACK;
#if defined(__LP64__)
action.sa_flags |= SA_64REGSET;
#endif
sigemptyset(&action.sa_mask);
action.sa_sigaction = &HandleSignal;
struct sigaction pre_sa;
for(int i = 0; i < fatalSignalsCount; i++) {
int sigResult = sigaction(g_fatalSignals[i], &action, &pre_sa);
}
}
void* RaiseAbort(void *userdata) {
raise(SIGABRT);
printf("signal handler has finished\n");
return NULL;
}
int main(int argc, const char * argv[]) {
InstallSignal();
dispatch_async(dispatch_get_global_queue(0, 0), ^{
raise(SIGABRT);
// abort(); // abort() is ok
RaiseAbort(nullptr);
});
// pthread is ok
// pthread_t tid;
// int ret = pthread_create(&tid, NULL, RaiseAbort, NULL);
// if (ret != 0) {
// fprintf(stderr, "create thread failed\n");
// return EXIT_FAILURE;
// }
[[NSRunLoop mainRunLoop] run];
return 0;
}
console log:
signal handler has finished
handle signal 6
begin sleep
end sleep
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Recovery operations for signals SIGBUS/SIGSEGV fail when the process intercepts Mach exceptions. Only the first recovery attempt succeeds, and subsequent Signal notifications are no longer received within the process.
I think this is a bug in XNU.
The test code main.c is:
If we comment out AddMachExceptionServer;, everything will return to normal.
#include <fcntl.h>
#include <mach/arm/kern_return.h>
#include <mach/kern_return.h>
#include <mach/mach.h>
#include <mach/message.h>
#include <mach/port.h>
#include <pthread.h>
#include <setjmp.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/_types/_mach_port_t.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#pragma pack(4)
typedef struct {
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCount;
integer_t code[2];
/** Padding to avoid RCV_TOO_LARGE. */
char padding[512];
} MachExceptionMessage;
typedef struct {
mach_msg_header_t header;
NDR_record_t NDR;
kern_return_t returnCode;
} MachReplyMessage;
#pragma pack()
static jmp_buf jump_buffer;
static void sigbus_handler(int signo, siginfo_t *info, void *context) {
printf("Caught SIGBUS at address: %p\n", info->si_addr);
longjmp(jump_buffer, 1);
}
static void *RunExcServer(void *userdata) {
kern_return_t kr = KERN_FAILURE;
mach_port_t exception_port = MACH_PORT_NULL;
kr = mach_port_allocate(mach_task_self_, MACH_PORT_RIGHT_RECEIVE,
&exception_port);
if (kr != KERN_SUCCESS) {
printf("mach_port_allocate: %s", mach_error_string(kr));
return NULL;
}
kr = mach_port_insert_right(mach_task_self_, exception_port, exception_port,
MACH_MSG_TYPE_MAKE_SEND);
if (kr != KERN_SUCCESS) {
printf("mach_port_insert_right: %s", mach_error_string(kr));
return NULL;
}
kr = task_set_exception_ports(
mach_task_self_, EXC_MASK_ALL & ~(EXC_MASK_RPC_ALERT | EXC_MASK_GUARD),
exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,THREAD_STATE_NONE);
if (kr != KERN_SUCCESS) {
printf("task_set_exception_ports: %s", mach_error_string(kr));
return NULL;
}
MachExceptionMessage exceptionMessage = {{0}};
MachReplyMessage replyMessage = {{0}};
for (;;) {
printf("Wating for message\n");
// Wait for a message.
kern_return_t kr = mach_msg(&exceptionMessage.header, MACH_RCV_MSG, 0,
sizeof(exceptionMessage), exception_port,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (kr == KERN_SUCCESS) {
// Send a reply saying "I didn't handle this exception".
replyMessage.header = exceptionMessage.header;
replyMessage.NDR = exceptionMessage.NDR;
replyMessage.returnCode = KERN_FAILURE;
printf("Catch exception: %d codecnt:%d code[0]: %d, code[1]: %d\n",
exceptionMessage.exception, exceptionMessage.codeCount,
exceptionMessage.code[0], exceptionMessage.code[1]);
mach_msg(&replyMessage.header, MACH_SEND_MSG, sizeof(replyMessage), 0,
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
} else {
printf("Mach error: %s\n", mach_error_string(kr));
}
}
return NULL;
}
static bool AddMachExceptionServer(void) {
int error;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_t ptid = NULL;
error = pthread_create(&ptid, &attr, &RunExcServer, NULL);
if (error != 0) {
pthread_attr_destroy(&attr);
return false;
}
pthread_attr_destroy(&attr);
return true;
}
int main(int argc, char *argv[]) {
AddMachExceptionServer();
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = sigbus_handler;
sa.sa_flags = SA_SIGINFO;
// #if TARGET_OS_IPHONE
// sigaction(SIGSEGV, &sa, NULL);
// #else
sigaction(SIGBUS, &sa, NULL);
// #endif
int i = 0;
while (i++ < 3) {
printf("\nProgram start %d\n", i);
bzero(&jump_buffer, sizeof(jump_buffer));
if (setjmp(jump_buffer) == 0) {
int fd = open("tempfile", O_RDWR | O_CREAT | O_TRUNC, 0666);
ftruncate(fd, 0);
char *map =
(char *)mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
unlink("tempfile");
printf("About to write to mmap of size 0 — should trigger SIGBUS...\n");
map[0] = 'X'; // ❌ triger a SIGBUS
munmap(map, 4096);
} else {
printf("Recovered from SIGBUS via longjmp!\n");
}
}
printf("_exit(0)\n");
_exit(0);
return 0;
}