Skip to content

kernel: SIGINT user-handler delivery + sigreturn (PR 3/3)#334

Merged
bboe merged 1 commit intomainfrom
sigint-3-handlers
May 8, 2026
Merged

kernel: SIGINT user-handler delivery + sigreturn (PR 3/3)#334
bboe merged 1 commit intomainfrom
sigint-3-handlers

Conversation

@bboe
Copy link
Copy Markdown
Owner

@bboe bboe commented May 7, 2026

Summary

Final PR in the SIGINT-handling stack: completes signal(SIGINT, handler) so user programs can register a ring-3 handler that's invoked on Ctrl+C, with a clean return path back to the interrupted instruction.

Stacks on:

What user-handler delivery means

When a user-virt address is registered via SYS_SYS_SIGNAL and Ctrl+C arrives at the IRET-to-user epilogue:

  1. signal_dispatch_user builds a 48-byte sigcontext on the user stack capturing the interrupted EIP / EFLAGS / ESP / pushad slots (minus the pushad-ESP slot, which is the kernel ESP and meaningless).
  2. The kernel rewrites the IRET frame so it enters the user handler at ring 3, with ESP pointing at the sigcontext.
  3. in_sigint_handler is set + pending_sigint cleared so a Ctrl+C burst doesn't re-enter the handler.
  4. The handler runs as a normal C function and ends with ret, popping the sigcontext's first dword (a vDSO trampoline address at user-virt 0x10450) into EIP.
  5. The 7-byte vDSO trampoline executes mov ah, SYS_SYS_SIGRETURN; int 0x30.
  6. signal_resume_after_handler validates the sigcontext, restores registers, sanitizes EFLAGS, and iretds back to the original interrupted EIP.

Security note: EFLAGS sanitization

The saved EFLAGS in the sigcontext lives on the user stack and is fully writable from ring 3. Without sanitization, a malicious handler could forge a sigcontext with IOPL=3 and gain ring-3 in/out, or set VM=1 to enter Virtual-8086 mode, etc.

SYS_SYS_SIGRETURN therefore masks the saved EFLAGS with USER_EFLAGS_MASK = 0xDD5 (whitelisting only CF/PF/AF/ZF/SF/TF/DF/OF) and OR's EFLAGS_IF_BIT = 0x200 so IF is always re-enabled. IOPL (bits 12-13), NT (14), RF (16), VM (17), AC (18), and VIF/VIP/ID (19-21) are all dropped. Mirrors Linux's restore_sigcontext FIX_EFLAGS rationale.

Macro restructure

SIGINT_TAIL_CHECK now runs before popad, so the pushad slots on the kernel stack are still readable when signal_dispatch_user captures them into the sigcontext. The CS check moves from [esp + 4] to [esp + 36] (4 + pushad's 32 bytes), and the macro itself owns the popad in the no-dispatch path.

Every IRQ + syscall handler that uses the macro loses its explicit popad — the macro now owns it. The IRQ 6 (FDC) handler is also upgraded from push eax / pop eax to pushad / popad so its dispatch frame matches the new macro contract.

Test plan

  • ./make_os.sh clean
  • ./tests/test_programs.py 30/30 (now includes sigint_test)
  • ./tests/test_asm.py 20/20
  • ./tests/test_bboefs.py 6/6
  • tests/programs/sigint_test.c exercises register, handler runs, sigreturn restores state

@bboe bboe force-pushed the sigint-2-sig-ign branch from 57e497b to 5dab05d Compare May 8, 2026 00:41
@bboe bboe force-pushed the sigint-3-handlers branch 2 times, most recently from a02e1f2 to 3a3661a Compare May 8, 2026 00:47
@bboe bboe force-pushed the sigint-2-sig-ign branch from 55a0edb to ae284d2 Compare May 8, 2026 01:10
@bboe bboe force-pushed the sigint-3-handlers branch from 3a3661a to f6ed911 Compare May 8, 2026 01:13
@bboe bboe force-pushed the sigint-2-sig-ign branch from ae284d2 to 2d6442f Compare May 8, 2026 01:52
@bboe bboe force-pushed the sigint-3-handlers branch from f6ed911 to 819a288 Compare May 8, 2026 01:53
@bboe bboe force-pushed the sigint-2-sig-ign branch from 2d6442f to 7a6c300 Compare May 8, 2026 03:20
@bboe bboe force-pushed the sigint-3-handlers branch from 819a288 to 227b302 Compare May 8, 2026 03:22
@bboe bboe force-pushed the sigint-2-sig-ign branch from 7a6c300 to 1bcf42e Compare May 8, 2026 03:27
@bboe bboe force-pushed the sigint-3-handlers branch from 227b302 to 6ac8e17 Compare May 8, 2026 03:28
@bboe bboe force-pushed the sigint-2-sig-ign branch 2 times, most recently from 66316cd to 904c56d Compare May 8, 2026 03:32
@bboe bboe force-pushed the sigint-3-handlers branch from 6ac8e17 to 238cc03 Compare May 8, 2026 03:33
@bboe bboe force-pushed the sigint-2-sig-ign branch from 904c56d to e7b7593 Compare May 8, 2026 03:35
@bboe bboe force-pushed the sigint-3-handlers branch from 238cc03 to 56fcbd3 Compare May 8, 2026 03:39
@bboe bboe force-pushed the sigint-2-sig-ign branch from e7b7593 to e13de04 Compare May 8, 2026 03:49
@bboe bboe force-pushed the sigint-3-handlers branch from 56fcbd3 to 9e22945 Compare May 8, 2026 03:50
Base automatically changed from sigint-2-sig-ign to main May 8, 2026 04:04
@bboe bboe force-pushed the sigint-3-handlers branch 2 times, most recently from 9c1c549 to 3ddc088 Compare May 8, 2026 05:37
Adds full signal() handler delivery: when SIG_DFL/SIG_IGN aren't set
and a user-virt handler is registered, the kernel builds a 48-byte
sigcontext on the user stack (saved EIP / EFLAGS / ESP / pushad
minus its ESP slot), rewrites the IRET frame to enter the handler
at ring 3, and clears in_sigint_handler / pending_sigint.  Handler
ends with 'ret' to a 7-byte vDSO trampoline at user-virt 0x10450
which calls SYS_SYS_SIGRETURN.

SYS_SYS_SIGRETURN validates the saved EIP/ESP are in user range,
sanitizes EFLAGS (mask 0xDD5 keeps CF/PF/AF/ZF/SF/TF/DF/OF; IF
forced on; IOPL/VM/NT/RF/AC dropped), and restores the user
register state.  EFLAGS sanitization prevents a malicious handler
from escalating IOPL via a forged sigcontext on stack.

The IRQ-handler dispatch macro is restructured to keep pushad on
stack at dispatch time so signal_dispatch_user can capture the
interrupted register state into the sigcontext.  Every IRQ +
syscall handler loses its explicit popad — the macro now owns it.
The IRQ 6 (FDC) handler is converted from push/pop eax to
pushad/popad to match the new macro contract.

Closes the three-PR series: PR 1 was default-kill, PR 2 added
SIG_IGN, this PR adds full user-handler delivery.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bboe bboe force-pushed the sigint-3-handlers branch from 3ddc088 to 37c4a3e Compare May 8, 2026 05:42
@bboe bboe merged commit ca38cae into main May 8, 2026
17 checks passed
@bboe bboe deleted the sigint-3-handlers branch May 8, 2026 05:45
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.

1 participant