// POSIX signal policy and user-mode dispatch over generic thread APC delivery. #include #include #include #include #include #include #include #include "posix_internal.h" // Returns one signal bit after validating its public signal number. static uint64_t PosixSignalBit(int signalNumber) { return NtlPosixSignalValid(signalNumber) ? NTL_POSIX_SIGNAL_BIT(signalNumber) : 0; } // Converts the process-local action snapshot into the server's routing view. static uint32_t PosixSignalDisposition(const POSIX_SIGNAL_ACTION_STATE* action) { uintptr_t handler = action ? action->Handler : 0; if (handler == (uintptr_t)SIG_IGN) return NTL_POSIX_SIGNAL_DISPOSITION_IGNORE; if (handler == (uintptr_t)SIG_DFL) return NTL_POSIX_SIGNAL_DISPOSITION_DEFAULT; return NTL_POSIX_SIGNAL_DISPOSITION_CAUGHT; } // Sends one signal-control request through the authoritative POSIX server. static KSTATUS PosixSignalCallServer(NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* reply) { if (!request || !reply) return NTLSTATUS_INVALID_PARAMETER; request->Size = sizeof(*request); request->Version = NTL_POSIX_PROTOCOL_VERSION; request->Flags = NTL_POSIX_REQUEST_FLAG_NONE; KSTATUS status = PosixRuntimeCallServer(request, reply); if (!KSUCCESS(status)) return status; return reply->Status; } // Applies one caught/default/ignored signal on the current POSIX thread. static bool PosixSignalDeliver(POSIX_THREAD_STATE* thread, int signalNumber) { if (!thread || !thread->NativeProcess || !NtlPosixSignalValid(signalNumber)) return false; uint64_t bit = PosixSignalBit(signalNumber); uint64_t effectiveMask = thread->SignalBlocked | thread->SignalHandlerBlocked; if (signalNumber != SIGKILL && (effectiveMask & bit) != 0) { thread->SignalPending |= bit; return false; } thread->SignalPending &= ~bit; POSIX_SIGNAL_ACTION_STATE* state = &thread->LogicalProcess->SignalActions[signalNumber]; uintptr_t handlerValue = state->Handler; if (signalNumber == SIGKILL) handlerValue = (uintptr_t)SIG_DFL; if (handlerValue == (uintptr_t)SIG_IGN || (handlerValue == (uintptr_t)SIG_DFL && signalNumber == SIGCHLD)) return false; if (handlerValue == (uintptr_t)SIG_DFL) { _exit(128 + signalNumber); return true; } uint64_t previousHandlerMask = thread->SignalHandlerBlocked; thread->SignalHandlerBlocked = (previousHandlerMask | state->Mask | bit) & NTL_POSIX_SIGNAL_VALID_MASK & ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK; ++thread->SignalDeliveryCount; if (thread->SignalWaitEvent != SYS_HANDLE_INVALID) (void)SysSignalEvent(thread->SignalWaitEvent); ((sighandler_t)handlerValue)(signalNumber); thread->SignalHandlerBlocked = previousHandlerMask; PosixSignalDispatchLocalPending(thread); return true; } // Maps one personality-neutral native exception class to a POSIX signal. static int PosixSignalForNativeException(uint32_t code) { switch (code) { case SYS_EXCEPTION_CODE_ACCESS_VIOLATION: case SYS_EXCEPTION_CODE_DATATYPE_MISALIGNMENT: case SYS_EXCEPTION_CODE_STACK_OVERFLOW: case SYS_EXCEPTION_CODE_GUARD_PAGE_VIOLATION: return SIGSEGV; case SYS_EXCEPTION_CODE_ILLEGAL_INSTRUCTION: case SYS_EXCEPTION_CODE_PRIVILEGED_INSTRUCTION: return SIGILL; case SYS_EXCEPTION_CODE_INT_DIVIDE_BY_ZERO: case SYS_EXCEPTION_CODE_INT_OVERFLOW: case SYS_EXCEPTION_CODE_FLT_DENORMAL_OPERAND: case SYS_EXCEPTION_CODE_FLT_DIVIDE_BY_ZERO: case SYS_EXCEPTION_CODE_FLT_INEXACT_RESULT: case SYS_EXCEPTION_CODE_FLT_INVALID_OPERATION: case SYS_EXCEPTION_CODE_FLT_OVERFLOW: case SYS_EXCEPTION_CODE_FLT_STACK_CHECK: case SYS_EXCEPTION_CODE_FLT_UNDERFLOW: return SIGFPE; default: return 0; } } // Translates selected native faults at the POSIX personality boundary. static uint32_t PosixSignalNativeException(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) { (void)context; (void)userContext; if (!record) return NTL_EXCEPTION_CONTINUE_SEARCH; int signalNumber = PosixSignalForNativeException(record->Code); if (!signalNumber) return NTL_EXCEPTION_CONTINUE_SEARCH; POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); if (!thread) return NTL_EXCEPTION_CONTINUE_SEARCH; uint64_t bit = PosixSignalBit(signalNumber); if ((thread->SignalBlocked | thread->SignalHandlerBlocked) & bit) return NTL_EXCEPTION_CONTINUE_SEARCH; POSIX_SIGNAL_ACTION_STATE* state = &thread->LogicalProcess->SignalActions[signalNumber]; if (state->Handler == (uintptr_t)SIG_IGN) return NTL_EXCEPTION_CONTINUE_SEARCH; return PosixSignalDeliver(thread, signalNumber) ? NTL_EXCEPTION_CONTINUE_EXECUTION : NTL_EXCEPTION_CONTINUE_SEARCH; } // Installs the native-exception adapter exactly once for one POSIX image. KSTATUS PosixSignalInitializeExceptionTranslation(POSIX_PROCESS_STATE* process) { if (!process) return NTLSTATUS_INVALID_PARAMETER; if (process->ExceptionHandlerHandle) return NTLSTATUS_SUCCESS; void* handle = NtlAddExceptionHandler(1, PosixSignalNativeException, (void*)0); if (!handle) return NTLSTATUS_LIMIT_REACHED; process->ExceptionHandlerHandle = handle; return NTLSTATUS_SUCCESS; } // Delivers one POSIX signal from the generic user-APC substrate. void PosixSignalApc(void* signalValue, void* routeValue, void* reserved) { (void)routeValue; (void)reserved; int signalNumber = (int)(uintptr_t)signalValue; POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); if (!thread || !NtlPosixSignalValid(signalNumber)) return; PosixSignalDeliver(thread, signalNumber); } // Dispatches thread-pending signals made deliverable by a mask change. void PosixSignalDispatchLocalPending(POSIX_THREAD_STATE* thread) { if (!thread) return; for (;;) { uint64_t deliverable = thread->SignalPending & ~(thread->SignalBlocked | thread->SignalHandlerBlocked) & NTL_POSIX_SIGNAL_VALID_MASK; deliverable |= thread->SignalPending & NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK; if (deliverable == 0) return; int signalNumber = NTL_POSIX_SIGNAL_HUP; while (signalNumber <= NTL_POSIX_SIGNAL_MAX && (deliverable & PosixSignalBit(signalNumber)) == 0) ++signalNumber; if (signalNumber > NTL_POSIX_SIGNAL_MAX) return; PosixSignalDeliver(thread, signalNumber); } } // Initializes an empty signal set. __declspec(dllexport) int sigemptyset(sigset_t* set) { if (!set) { errno = EINVAL; return -1; } *set = 0; return 0; } // Initializes a signal set containing every supported signal. __declspec(dllexport) int sigfillset(sigset_t* set) { if (!set) { errno = EINVAL; return -1; } *set = NTL_POSIX_SIGNAL_VALID_MASK; return 0; } // Adds one signal to a signal set. __declspec(dllexport) int sigaddset(sigset_t* set, int signalNumber) { uint64_t bit = PosixSignalBit(signalNumber); if (!set || bit == 0) { errno = EINVAL; return -1; } *set |= bit; return 0; } // Removes one signal from a signal set. __declspec(dllexport) int sigdelset(sigset_t* set, int signalNumber) { uint64_t bit = PosixSignalBit(signalNumber); if (!set || bit == 0) { errno = EINVAL; return -1; } *set &= ~bit; return 0; } // Tests whether one signal belongs to a signal set. __declspec(dllexport) int sigismember(const sigset_t* set, int signalNumber) { uint64_t bit = PosixSignalBit(signalNumber); if (!set || bit == 0) { errno = EINVAL; return -1; } return (*set & bit) != 0 ? 1 : 0; } // Installs or queries one logical-process-wide POSIX signal action. __declspec(dllexport) int sigaction(int signalNumber, const struct sigaction* action, struct sigaction* oldAction) { if (!NtlPosixSignalValid(signalNumber)) { errno = EINVAL; return -1; } POSIX_LOGICAL_PROCESS_STATE* process = PosixRuntimeLogicalProcessState(); if (!process) { errno = EIO; return -1; } POSIX_SIGNAL_ACTION_STATE* state = &process->SignalActions[signalNumber]; if (oldAction) { oldAction->sa_handler = (sighandler_t)state->Handler; oldAction->sa_mask = state->Mask; oldAction->sa_flags = (int)state->Flags; } if (!action) return 0; if (action->sa_flags != 0 || (action->sa_mask & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0 || action->sa_handler == SIG_ERR || (signalNumber == SIGKILL && action->sa_handler != SIG_DFL)) { errno = EINVAL; return -1; } POSIX_SIGNAL_ACTION_STATE previous = *state; state->Handler = (uintptr_t)action->sa_handler; state->Mask = action->sa_mask & ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK; state->Flags = (uint32_t)action->sa_flags; NTL_POSIX_REQUEST request; memset(&request, 0, sizeof(request)); request.Operation = NTL_POSIX_OP_SET_SIGNAL_DISPOSITION; request.SignalNumber = signalNumber; request.SignalDisposition = PosixSignalDisposition(state); NTL_POSIX_REPLY reply; KSTATUS status = PosixSignalCallServer(&request, &reply); if (!KSUCCESS(status)) { *state = previous; errno = PosixStatusToErrno(status); return -1; } return 0; } // Changes the calling thread's persistent POSIX signal mask. __declspec(dllexport) int sigprocmask(int how, const sigset_t* set, sigset_t* oldSet) { POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); if (!thread) { errno = EIO; return -1; } uint64_t previous = thread->SignalBlocked; if (oldSet) *oldSet = previous; if (!set) return 0; if ((*set & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0 || (how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK)) { errno = EINVAL; return -1; } uint64_t next = previous; if (how == SIG_BLOCK) next |= *set; else if (how == SIG_UNBLOCK) next &= ~*set; else next = *set; next &= NTL_POSIX_SIGNAL_VALID_MASK & ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK; thread->SignalBlocked = next; NTL_POSIX_REQUEST request; memset(&request, 0, sizeof(request)); request.Operation = NTL_POSIX_OP_UPDATE_SIGNAL_MASK; request.SignalMask = next; NTL_POSIX_REPLY reply; KSTATUS status = PosixSignalCallServer(&request, &reply); if (!KSUCCESS(status)) { thread->SignalBlocked = previous; errno = PosixStatusToErrno(status); return -1; } PosixSignalDispatchLocalPending(thread); return 0; } // Returns the union of calling-thread and process-pending signals. __declspec(dllexport) int sigpending(sigset_t* set) { if (!set) { errno = EINVAL; return -1; } POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); if (!thread) { errno = EIO; return -1; } NTL_POSIX_REQUEST request; memset(&request, 0, sizeof(request)); request.Operation = NTL_POSIX_OP_GET_PENDING_SIGNALS; NTL_POSIX_REPLY reply; KSTATUS status = PosixSignalCallServer(&request, &reply); if (!KSUCCESS(status)) { errno = PosixStatusToErrno(status); return -1; } *set = (thread->SignalPending | reply.PendingSignals) & NTL_POSIX_SIGNAL_VALID_MASK; return 0; } // Atomically substitutes a temporary mask for the duration of one signal wait. __declspec(dllexport) int sigsuspend(const sigset_t* set) { if (!set || (*set & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0) { errno = EINVAL; return -1; } POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); if (!thread) { errno = EIO; return -1; } SYS_HANDLE event = SYS_HANDLE_INVALID; KSTATUS status = SysCreateEvent(0u, 0u, &event); if (!KSUCCESS(status)) { errno = PosixStatusToErrno(status); return -1; } SYS_HANDLE previousEvent = thread->SignalWaitEvent; uint64_t deliveryCount = thread->SignalDeliveryCount; thread->SignalWaitEvent = event; sigset_t previousMask = 0; if (sigprocmask(SIG_SETMASK, set, &previousMask) != 0) { thread->SignalWaitEvent = previousEvent; (void)SysCloseHandle(event); return -1; } for (;;) { if (thread->SignalDeliveryCount != deliveryCount) { status = NTLSTATUS_SUCCESS; break; } status = SysWait(event, SYS_WAIT_FLAG_ALERTABLE); if (status == NTLSTATUS_SUCCESS && thread->SignalDeliveryCount != deliveryCount) break; if (status == NTLSTATUS_USER_APC || status == NTLSTATUS_KERNEL_APC || status == NTLSTATUS_ALERTED) continue; if (!KSUCCESS(status)) break; } thread->SignalWaitEvent = previousEvent; int restore = sigprocmask(SIG_SETMASK, &previousMask, (sigset_t*)0); (void)SysCloseHandle(event); if (restore != 0) return -1; if (!KSUCCESS(status)) { errno = PosixStatusToErrno(status); return -1; } errno = EINTR; return -1; } // Sends one signal through stable POSIX process or process-group identity. __declspec(dllexport) int kill(pid_t pid, int signalNumber) { if (signalNumber < 0 || signalNumber > NTL_POSIX_SIGNAL_MAX) { errno = EINVAL; return -1; } NTL_POSIX_REQUEST request; memset(&request, 0, sizeof(request)); request.Operation = NTL_POSIX_OP_SEND_SIGNAL; request.SignalTargetPid = (int32_t)pid; request.SignalNumber = signalNumber; NTL_POSIX_REPLY reply; KSTATUS status = PosixSignalCallServer(&request, &reply); if (!KSUCCESS(status)) { if (status == NTLSTATUS_NOT_FOUND) errno = ESRCH; else if (status == NTLSTATUS_ACCESS_DENIED) errno = EPERM; else errno = PosixStatusToErrno(status); return -1; } return 0; } // Replaces the process alarm and returns the rounded-up previous remaining interval. __declspec(dllexport) unsigned int alarm(unsigned int seconds) { NTL_POSIX_REQUEST request; memset(&request, 0, sizeof(request)); request.Operation = NTL_POSIX_OP_SET_ALARM; request.AlarmSeconds = seconds; NTL_POSIX_REPLY reply; KSTATUS status = PosixSignalCallServer(&request, &reply); if (!KSUCCESS(status)) { errno = PosixStatusToErrno(status); return 0; } return reply.AlarmRemainingSeconds; } // Waits until one caught signal runs. __declspec(dllexport) int pause(void) { sigset_t mask = 0; if (sigprocmask(SIG_SETMASK, (const sigset_t*)0, &mask) != 0) return -1; return sigsuspend(&mask); } // Sleeps on a native waitable timer and reports whole seconds left after a caught signal. __declspec(dllexport) unsigned int sleep(unsigned int seconds) { if (seconds == 0) { (void)SysYield(); return 0; } POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); if (!thread) return seconds; SYS_HANDLE timer = SYS_HANDLE_INVALID; SYS_HANDLE signalEvent = SYS_HANDLE_INVALID; KSTATUS status = SysCreateTimer(&timer); if (!KSUCCESS(status)) return seconds; status = SysCreateEvent(0u, 0u, &signalEvent); if (!KSUCCESS(status)) { (void)SysCloseHandle(timer); return seconds; } uint64_t frequency = 0; status = SysGetTickFrequency(&frequency); if (!KSUCCESS(status) || frequency == 0) { (void)SysCloseHandle(signalEvent); (void)SysCloseHandle(timer); return seconds; } uint64_t start = SysGetTick(); SYS_SET_TIMER_REQUEST setTimer; memset(&setTimer, 0, sizeof(setTimer)); setTimer.Size = sizeof(setTimer); setTimer.TimerHandle = timer; setTimer.Flags = SYS_TIMER_FLAG_NONE; setTimer.DueTimeMilliseconds = (uint64_t)seconds * 1000u; status = SysSetTimer(&setTimer); if (!KSUCCESS(status)) { (void)SysCloseHandle(signalEvent); (void)SysCloseHandle(timer); return seconds; } SYS_HANDLE previousEvent = thread->SignalWaitEvent; uint64_t deliveryCount = thread->SignalDeliveryCount; thread->SignalWaitEvent = signalEvent; SYS_HANDLE waits[2] = { timer, signalEvent }; uint32_t selected = UINT32_MAX; SYS_WAIT_MANY_REQUEST wait; memset(&wait, 0, sizeof(wait)); wait.Size = sizeof(wait); wait.Flags = SYS_WAIT_FLAG_ALERTABLE; wait.Handles = waits; wait.Count = 2u; wait.SignaledIndexOut = &selected; wait.TimeoutMilliseconds = SYS_WAIT_TIMEOUT_INFINITE; for (;;) { selected = UINT32_MAX; status = SysWaitMany(&wait); if (status == NTLSTATUS_SUCCESS && selected == 0u) break; if (thread->SignalDeliveryCount != deliveryCount) break; if (status == NTLSTATUS_SUCCESS || status == NTLSTATUS_USER_APC || status == NTLSTATUS_KERNEL_APC || status == NTLSTATUS_ALERTED) continue; break; } thread->SignalWaitEvent = previousEvent; uint64_t elapsedTicks = SysGetTick() - start; uint64_t elapsedMs = SysTicksToMilliseconds(elapsedTicks, frequency); uint64_t totalMs = (uint64_t)seconds * 1000u; uint64_t remainingMs = elapsedMs < totalMs ? totalMs - elapsedMs : 0; unsigned int remaining = (unsigned int)((remainingMs + 999u) / 1000u); (void)SysCancelTimer(timer); (void)SysCloseHandle(signalEvent); (void)SysCloseHandle(timer); if (status == NTLSTATUS_SUCCESS && selected == 0u) return 0; if (thread->SignalDeliveryCount != deliveryCount) return remaining; return remaining ? remaining : seconds; } // Installs the historical one-argument signal disposition API. __declspec(dllexport) sighandler_t signal(int signalNumber, sighandler_t handler) { struct sigaction action; struct sigaction previous; action.sa_handler = handler; action.sa_mask = 0; action.sa_flags = 0; if (sigaction(signalNumber, &action, &previous) != 0) return SIG_ERR; return previous.sa_handler; } // Raises one signal on the calling POSIX thread. __declspec(dllexport) int raise(int signalNumber) { if (!NtlPosixSignalValid(signalNumber)) { errno = EINVAL; return -1; } POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); if (!thread) { errno = EIO; return -1; } PosixSignalDeliver(thread, signalNumber); return 0; }