Skip to content

Conversation

@ryanbreen
Copy link
Owner

Summary

This PR adds complete POSIX signal handling support to Breenix, enabling userspace programs to register signal handlers, send signals between processes, and properly handle asynchronous events.

Key Features

  • Signal syscalls: kill, rt_sigaction, rt_sigprocmask, rt_sigreturn
  • Signal delivery: Signals delivered from context switch before returning to userspace
  • Signal trampoline: Machine code that calls sigreturn when handler returns
  • Security hardening: Userspace pointer validation, frame forgery protection, RFLAGS sanitization
  • Userspace API: libbreenix wrappers for all signal operations

Files Changed

Component Files Description
Signal infrastructure kernel/src/signal/ Types, constants, delivery, trampoline
Syscalls kernel/src/syscall/signal.rs, userptr.rs Syscall implementations
Integration context_switch.rs, handler.rs Signal delivery hook
Userspace libs/libbreenix/src/signal.rs Userspace wrappers
Tests userspace/tests/signal_*.rs End-to-end verification

Testing

Three end-to-end tests verify the implementation:

Test Marker Proves
signal_handler_test SIGNAL_HANDLER_EXECUTED Handlers actually execute
signal_return_test SIGNAL_RETURN_WORKS Trampoline → sigreturn works
signal_regs_test SIGNAL_REGS_PRESERVED Registers preserved across signals

All 63 boot stages pass.

Security

  • Userspace pointer validation: All pointers validated before dereferencing
  • Magic number: Signal frames include magic number to detect forgery
  • RIP/RSP validation: Kernel addresses rejected in sigreturn
  • RFLAGS sanitization: Cannot disable interrupts via crafted signal frame
  • SIGKILL/SIGSTOP: Cannot be caught or blocked

Test plan

  • Build passes with zero warnings
  • All 63 boot stages pass
  • Signal handler execution verified
  • Signal handler return (trampoline) verified
  • Register preservation verified
  • Security audit passed (Grade: A-)

🤖 Generated with Claude Code

ryanbreen and others added 3 commits December 7, 2025 13:26
This adds complete signal handling support to Breenix including:

Kernel signal infrastructure:
- SignalState per-process with pending/blocked masks and 64 handlers
- Signal constants (SIGHUP through SIGSYS, NSIG=64)
- Default actions (terminate, core dump, stop, continue, ignore)
- Signal delivery from context switch before return to userspace
- Signal trampoline that calls rt_sigreturn on handler return

Syscalls implemented:
- kill(pid, sig) - Send signal to process
- rt_sigaction(sig, act, oldact, sigsetsize) - Set signal handler
- rt_sigprocmask(how, set, oldset, sigsetsize) - Block/unblock signals
- rt_sigreturn() - Return from signal handler, restore context

Security measures:
- Userspace pointer validation (copy_from_user/copy_to_user)
- Signal frame magic number to detect forgery
- RIP/RSP validation to prevent kernel address injection
- RFLAGS sanitization to prevent disabling interrupts
- SIGKILL/SIGSTOP cannot be caught or blocked

Userspace support (libbreenix):
- kill(), sigaction(), sigprocmask() wrappers
- Sigaction struct with SA_* flags

End-to-end tests:
- signal_handler_test: Proves handlers execute
- signal_return_test: Proves trampoline/sigreturn works
- signal_regs_test: Proves registers preserved across signals

All 63 boot stages pass including new signal-specific stages.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The libs/libbreenix/src/signal.rs file was created but not committed
because libs/ is in .gitignore. Force-add it to match other libs files.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Two bugs fixed:

1. delivery.rs: Convert recursive signal delivery to iterative loop
   - The SIG_IGN case was recursively calling deliver_pending_signals
   - This caused unbounded stack growth with many ignored signals
   - Now uses a loop to process all deliverable signals with O(1) stack

2. signal.rs: Fix off-by-8 error in sigreturn frame pointer
   - When signal handler executes 'ret', it pops trampoline_addr from stack
   - This increments RSP by 8, so frame pointer was pointing past the frame
   - Now subtracts 8 from RSP to find the actual SignalFrame start

All signal tests now pass:
- Signal handler execution verified
- Signal handler return verified
- Signal register preservation verified

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@ryanbreen ryanbreen merged commit 6f828d0 into main Dec 7, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants