Post

Replies

Boosts

Views

Activity

Using raise in GCD can cause timing issues with the signal mechanism.
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
4
0
215
Sep ’25
When implementing a custom Mach exception handler, all recovery operations for SIGBUS/SIGSEGV except the first attempt will fail.
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; }
2
0
103
Apr ’25