Signal.lean
Declarations on this page (28)
- def SYS_TKILL
- def SYS_TGKILL
- def SYS_SIGALTSTACK
- def SYS_RT_SIGACTION
- def SYS_RT_SIGPROCMASK
- def LINUX_SIGABRT
- def LINUX_SIGBUS
- def LINUX_SIGKILL
- def LINUX_SIGSEGV
- def LINUX_SIGSTOP
- def SIG_DFL
- def SIG_IGN
- def SIG_UNBLOCKABLE_MASK
- def UAPI_SA_FLAGS
- def SS_ONSTACK
- def SS_DISABLE
- def SS_AUTODISARM
- def MINSIGSTKSZ
-
def SigAction.
toBytes -
def SigAltStack.
autodisarm -
def SigAltStack.
onSigStack -
def SigAltStack.
flags -
def SigAltStack.
toBytes - def execRtSigprocmask
- def execSigaltstack
- def execRtSigaction
- def execTkill
- def execTgkill
Signal and Abort Syscalls
Linux 7.3 AArch64 signal syscall numbers, ABI constants, SigAction/SigAltStack binary layouts, and execution semantics for rt_sigprocmask (kernel/), sigaltstack (kernel/, include/), rt_sigaction (kernel/), tgkill (kernel/), and tkill (kernel/).
@[expose] public sectionnamespace Arm64def SYS_TKILL
Linux AArch64 tkill syscall number (__NR_tkill = 130, include/, kernel/).
def SYS_TKILL : BitVec 64 := 130#64def SYS_TGKILL
Linux AArch64 tgkill syscall number (__NR_tgkill = 131, include/, kernel/).
def SYS_TGKILL : BitVec 64 := 131#64def SYS_SIGALTSTACK
Linux AArch64 sigaltstack syscall number (__NR_sigaltstack = 132, include/, kernel/).
def SYS_SIGALTSTACK : BitVec 64 := 132#64def SYS_RT_SIGACTION
Linux AArch64 rt_sigaction syscall number (__NR_rt_sigaction = 134, include/, kernel/).
def SYS_RT_SIGACTION : BitVec 64 := 134#64def SYS_RT_SIGPROCMASK
Linux AArch64 rt_sigprocmask syscall number (__NR_rt_sigprocmask = 135, include/, kernel/).
def SYS_RT_SIGPROCMASK : BitVec 64 := 135#64def LINUX_SIGABRT
POSIX / Linux SIGABRT (SIGIOT) signal number (6, include/).
def LINUX_SIGABRT : BitVec 32 := 6#32def LINUX_SIGBUS
POSIX / Linux SIGBUS signal number (7, include/).
def LINUX_SIGBUS : BitVec 32 := 7#32def LINUX_SIGKILL
POSIX / Linux SIGKILL signal number (9, include/).
def LINUX_SIGKILL : BitVec 32 := 9#32def LINUX_SIGSEGV
POSIX / Linux SIGSEGV signal number (11, include/).
def LINUX_SIGSEGV : BitVec 32 := 11#32def LINUX_SIGSTOP
POSIX / Linux SIGSTOP signal number (19, include/).
def LINUX_SIGSTOP : BitVec 32 := 19#32def SIG_DFL
Default signal disposition constant (SIG_DFL = 0, include/).
def SIG_DFL : BitVec 64 := 0#64def SIG_IGN
Ignore signal disposition constant (SIG_IGN = 1, include/).
def SIG_IGN : BitVec 64 := 1#64def SIG_UNBLOCKABLE_MASK
Bitmask of signals stripped from sa_mask by do_sigaction (sigmask(SIGKILL) | sigmask(SIGSTOP) = (1 << 8) | (1 << 18) = 0x40100, SIG_KERNEL_ONLY_MASK in include/).
def SIG_UNBLOCKABLE_MASK : BitVec 64 := 0x40100#64def UAPI_SA_FLAGS
AArch64 sa_flags bits kept by do_sigaction (act->sa.UAPI_SA_FLAGS in kernel/): UAPI_SA_FLAGS = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_SIGINFO | SA_ONSTACK | SA_RESTART | SA_NODEFER | SA_RESETHAND | SA_EXPOSE_TAGBITS | __ARCH_UAPI_SA_FLAGS (include/) with __ARCH_UAPI_SA_FLAGS = SA_RESTORER on arm64 (0x04000000, arch/), i.e. 0x1 | 0x2 | 0x4 | 0x08000000 | 0x10000000 | 0x40000000 | 0x80000000 | 0x800 | 0x04000000 (include/), zero-extended to the 64-bit sa_flags.
def UAPI_SA_FLAGS : BitVec 64 := 0xDC000807#64def SS_ONSTACK
Alternate signal stack active flag (SS_ONSTACK = 1, include/).
def SS_ONSTACK : BitVec 32 := 1#32def SS_DISABLE
Alternate signal stack disabled flag (SS_DISABLE = 2, include/).
def SS_DISABLE : BitVec 32 := 2#32def SS_AUTODISARM
Alternate signal stack auto-disarm flag (SS_AUTODISARM = 1U << 31, include/).
def SS_AUTODISARM : BitVec 32 := 0x80000000#32def MINSIGSTKSZ
Minimum AArch64 alternate signal stack byte size (MINSIGSTKSZ = 5120, arch/; note 2048 in asm-generic is 32-bit compat only).
def MINSIGSTKSZ : Nat := 5120
def SigAction.
Serialize a 32-byte AArch64 struct k_sigaction (handler, flags, restorer, mask) in little-endian byte order (include/).
def SigAltStack.
Whether the recorded raw sas_ss_flags word of sas has SS_AUTODISARM set (current->sas_ss_flags & SS_AUTODISARM in include/).
def SigAltStack.autodisarm (sas : SigAltStack) : Bool :=
(sas.ssFlags &&& SS_AUTODISARM) != 0#32
def SigAltStack.
True when stack pointer sp lies on the active alternate signal stack sas (on_sig_stack / __on_sig_stack for downward-growing AArch64 stacks in include/): returns false when sas. is set (SS_AUTODISARM) or sas., and otherwise checks sas. using unsigned 64-bit arithmetic ((sas.).
def SigAltStack.onSigStack (sas : SigAltStack) (sp : BitVec 64) : Bool :=
!sas.autodisarm && BitVec.ult sas.sp sp && BitVec.ule (sp - sas.sp) sas.size
def SigAltStack.
Reported 32-bit ss_flags word for sas at stack pointer sp (sas_ss_flags(sp) | (current->sas_ss_flags & SS_FLAG_BITS) in include/ and kernel/).
def SigAltStack.flags (sas : SigAltStack) (sp : BitVec 64) : BitVec 32 :=
let mode :=
if sas.size == 0#64 then SS_DISABLE
else if sas.onSigStack sp then SS_ONSTACK
else 0#32
if sas.autodisarm then mode ||| SS_AUTODISARM else mode
def SigAltStack.
Serialize a 24-byte AArch64 stack_t (ss_sp : u64 at +0, ss_flags : i32 at +8, 4 zero padding bytes at +12 from memset(oss, 0, sizeof(stack_t)), ss_size : u64 at +16; include/, kernel/).
def execRtSigprocmask
rt_sigprocmask(how, set, oldset, sigsetsize) (__NR_rt_sigprocmask = 135, kernel/ SYSCALL_DEFINE4(rt_sigprocmask)): validates sigsetsize == 8 (sizeof(sigset_t) on 64-bit Linux), user-memory readability and non-wrapping 8-byte range of set (when non-null), how ∈ {SIG_BLOCK(0), SIG_UNBLOCK(1), SIG_SETMASK(2)} when set ≠ 0, and writability and non-wrapping 8-byte range of oldset (8 bytes when non-null).
-
[INTENTIONAL_SCOPE]: Non-zero blocked signal masks are out of scope. Because a process starts with an empty blocked mask (0),set == 0,how == SIG_UNBLOCK(1), andSIG_BLOCK(0)/SIG_SETMASK(2)with (read64 set &&& ~~~SIG_UNBLOCKABLE_MASK) == 0 (SIGKILL/SIGSTOPstripped persigdelsetmaskinkernel/) all preserve an empty blocked mask (signal.c:3262 oldsetreceives 8 zero bytes when non-null), whereasSIG_BLOCKorSIG_SETMASKwith any blockable signal bit set fails closed with .Exception. -
[OVERPINNED_BEHAVIOR]: Validatessigsetsize->copy_from_user(set)->how/ empty-mask scope check ->copy_to_user(oldset), matchingkernel/.signal.c:3254-3281
def execRtSigprocmask (s : State) : State :=
let how := s.readReg32 Reg.x0
let set := s.readReg64 Reg.x1
let oldset := s.readReg64 Reg.x2
let sigsetsize := s.readReg64 Reg.x3
if sigsetsize != 8#64 then
(s.writeReg64 Reg.x0 (-EINVAL)).advancePC
else if set != 0#64 && !decide (set.toNat + 8 ≤ 2 ^ 64) then
(s.writeReg64 Reg.x0 EFAULT_RET).advancePC
else if set != 0#64 && !s.mem.allPerm (·.r) set 8 then
(s.writeReg64 Reg.x0 EFAULT_RET).advancePC
else if set != 0#64 && !s.mem.canRead set 8 then
s.setStatus (.Exception "rt_sigprocmask set in lazy-free memory")
else if set != 0#64 && how != 0#32 && how != 1#32 && how != 2#32 then
(s.writeReg64 Reg.x0 (-EINVAL)).advancePC
else if set != 0#64 && (how == 0#32 || how == 2#32) &&
(s.mem.read64 set &&& ~~~SIG_UNBLOCKABLE_MASK) != 0#64 then
s.setStatus (.Exception "unmodeled non-zero signal mask in rt_sigprocmask")
else if oldset == 0#64 then
(s.writeReg64 Reg.x0 0#64).advancePC
else if !decide (oldset.toNat + 8 ≤ 2 ^ 64) then
(s.writeReg64 Reg.x0 EFAULT_RET).advancePC
else if !s.mem.canWrite oldset 8 then
(s.writeReg64 Reg.x0 EFAULT_RET).advancePC
else
((s.setMem (s.mem.writeBytes oldset (List.replicate 8 0#8))).writeReg64 Reg.x0 0#64).advancePCdef execSigaltstack
sigaltstack(uss, uoss) (__NR_sigaltstack = 132, SYSCALL_DEFINE2(sigaltstack) and do_sigaltstack in kernel/): models queries and updates of the per-thread 24-byte stack_t alternate signal stack (ss_sp : u64 at +0, 32-bit ss_flags at +8 ignoring 4-byte caller padding at +12, ss_size : u64 at +16):
-
non-null
ussthat wraps modulo2 ^ 64or is not 24-byte readable returns-EFAULT(copy_from_user(&new, uss, sizeof(stack_t))before state mutation); -
when
uss ≠ 0ands.(sigAltStack. onSigStack s. sp sp ∈ (sas.withoutsp, sas. sp + sas. size] SS_AUTODISARM), returns-EPERM; -
when
uss ≠ 0and (ss_flags &&& ~~~SS_AUTODISARM) ∉ {0,SS_ONSTACK,SS_DISABLE}, returns-EINVAL; -
when
uss ≠ 0and (ss_flags &&& ~~~SS_AUTODISARM) ==SS_DISABLE, records { sp := 0, size := 0,ssFlags:= ss_flags } without checkingss_size(kernel/); otherwise records { sp := ss_sp, size := ss_size,signal.c:4496-4499 ssFlags:= ss_flags } after requiring ss_size ≥MINSIGSTKSZ(5120, returning-ENOMEMif smaller) unless the recorded(sas_ss_sp, sas_ss_size, sas_ss_flags)already equals(ss_sp, ss_size, ss_flags): that unchanged-settings early return (kernel/, after thesignal.c:4486-4493 -EPERM/-EINVALchecks and beforeSS_DISABLEnormalization and theMINSIGSTKSZcheck) leaves the state unchanged, which equals the store below (every reachable recordedSS_DISABLE-mode setting already hassp = size = 0); -
when
uoss ≠ 0, writes the previous 24-byte zero-paddedstack_tsnapshot (oldBytes, captured before updatings.) tosigAltStack uoss(returning-EFAULTifuosswraps modulo2 ^ 64or is not 24-byte writable, matchingcopy_to_user(uoss, &old, sizeof(stack_t))afterdo_sigaltstackinkernel/);signal.c:4521-4525 -
[OVERPINNED_BEHAVIOR]: checkscopy_from_user(uss)->do_sigaltstack->copy_to_user(uoss), matchingkernel/.signal.c:4519-4526
def execSigaltstack (s : State) : State :=
let uss := s.readReg64 Reg.x0
let uoss := s.readReg64 Reg.x1
let ssSp := s.mem.read64 uss
let ssFlags := s.mem.read32 (uss + 8#64)
let ssSize := s.mem.read64 (uss + 16#64)
let ssMode := ssFlags &&& ~~~SS_AUTODISARM
if uss != 0#64 && (!decide (uss.toNat + 24 ≤ 2 ^ 64) || !s.mem.allPerm (·.r) uss 24) then
(s.writeReg64 Reg.x0 EFAULT_RET).advancePC
else if uss != 0#64 && !s.mem.canRead uss 24 then
s.setStatus (.Exception "sigaltstack uss in lazy-free memory")
else if uss != 0#64 && s.sigAltStack.onSigStack s.sp then
(s.writeReg64 Reg.x0 (-EPERM)).advancePC
else if uss != 0#64 && ssMode != SS_DISABLE && ssMode != SS_ONSTACK && ssMode != 0#32 then
(s.writeReg64 Reg.x0 (-EINVAL)).advancePC
else if uss != 0#64 && ssMode != SS_DISABLE && ssSize.toNat < MINSIGSTKSZ &&
s.sigAltStack != { sp := ssSp, size := ssSize, ssFlags } then
(s.writeReg64 Reg.x0 (-ENOMEM)).advancePC
else
let oldBytes := s.sigAltStack.toBytes s.sp
let s1 :=
if uss == 0#64 then s
else if ssMode == SS_DISABLE then
s.setSigAltStack { sp := 0#64, size := 0#64, ssFlags }
else
s.setSigAltStack { sp := ssSp, size := ssSize, ssFlags }
if uoss == 0#64 then
(s1.writeReg64 Reg.x0 0#64).advancePC
else if !decide (uoss.toNat + 24 ≤ 2 ^ 64) || !s1.mem.canWrite uoss 24 then
(s1.writeReg64 Reg.x0 EFAULT_RET).advancePC
else
((s1.setMem (s1.mem.writeBytes uoss oldBytes)).writeReg64 Reg.x0 0#64).advancePCdef execRtSigaction
rt_sigaction(signum, act, oldact, sigsetsize) (__NR_rt_sigaction = 134, SYSCALL_DEFINE4(rt_sigaction) and do_sigaction in kernel/): models per-signal SIG_DFL (0) and SIG_IGN (1) disposition queries and updates across the 64 Linux signals (1 ≤ signum ≤ 64, _NSIG = 64), plus recording user-space handlers for the synchronous hardware fault signals SIGBUS (7) and SIGSEGV (11), in State. without asynchronous signal delivery:
-
sigsetsize != 8(sizeof(sigset_t)) returns-EINVAL; -
non-null
actthat wraps modulo2 ^ 64or is not 32-byte readable returns-EFAULT(copy_from_user); -
signum ≤ 0orsignum > 64returns-EINVAL(!valid_signal(sig)); -
non-null
actwithsignum == SIGKILL(9) orsignum == SIGSTOP(19) returns-EINVAL(sig_kernel_only(sig)); -
[INTENTIONAL_SCOPE]: non-nullactwhosesa_handleris neitherSIG_DFL(0) norSIG_IGN(1) is permitted only whensignum == SIGSEGV(11) orsignum == SIGBUS(7) (recording the disposition inState.); all other signals fail closed with .sigActions Exception"unmodeled rt_sigaction". Hardware faults inArm64/always raise .Trusted/ ISA/ Eval.lean Exceptiondirectly (never .Signaledor invoking user handlers), andexecTkill/execTgkillonSIGSEGV/SIGBUSor non-SIG_DFL/SIG_IGNSIGABRTlikewise raise .Exception; -
when
act ≠ 0, records { handler, flags &&&UAPI_SA_FLAGS, restorer, mask &&& ~~~SIG_UNBLOCKABLE_MASK} inState.signum (sa_flags &=sigActions UAPI_SA_FLAGSandsigdelsetmaskindo_sigaction); recorded flags are thus already masked, sooldactmatches Linux's oact->sa.sa_flags &=UAPI_SA_FLAGS; -
when
oldact ≠ 0, writes the previous 32-bytek_sigactiontooldact(returning-EFAULTifoldactwraps modulo2 ^ 64or is not 32-byte writable, matchingcopy_to_userafterdo_sigaction); -
[OVERPINNED_BEHAVIOR]: checkssigsetsize->copy_from_user(act)->do_sigaction(signum)->copy_to_user(oldact), matchingkernel/.signal.c:4559-4573
def execRtSigaction (s : State) : State :=
let signum := s.readReg32 Reg.x0
let act := s.readReg64 Reg.x1
let oldact := s.readReg64 Reg.x2
let sigsetsize := s.readReg64 Reg.x3
if sigsetsize != 8#64 then
(s.writeReg64 Reg.x0 (-EINVAL)).advancePC
else if act != 0#64 && (!decide (act.toNat + 32 ≤ 2 ^ 64) || !s.mem.allPerm (·.r) act 32) then
(s.writeReg64 Reg.x0 EFAULT_RET).advancePC
else if act != 0#64 && !s.mem.canRead act 32 then
s.setStatus (.Exception "rt_sigaction act in lazy-free memory")
else if signum.sle 0#32 || signum.toNat > 64 then
(s.writeReg64 Reg.x0 (-EINVAL)).advancePC
else if act != 0#64 && (signum == LINUX_SIGKILL || signum == LINUX_SIGSTOP) then
(s.writeReg64 Reg.x0 (-EINVAL)).advancePC
else if act != 0#64 && s.mem.read64 act != SIG_DFL && s.mem.read64 act != SIG_IGN &&
signum != LINUX_SIGSEGV && signum != LINUX_SIGBUS then
s.setStatus (.Exception "unmodeled rt_sigaction")
else
let old := s.getSigAction signum
let s1 :=
if act == 0#64 then s
else
s.setSigAction signum
{ handler := s.mem.read64 act
flags := s.mem.read64 (act + 8#64) &&& UAPI_SA_FLAGS
restorer := s.mem.read64 (act + 16#64)
mask := s.mem.read64 (act + 24#64) &&& ~~~SIG_UNBLOCKABLE_MASK }
if oldact == 0#64 then
(s1.writeReg64 Reg.x0 0#64).advancePC
else if !decide (oldact.toNat + 32 ≤ 2 ^ 64) || !s1.mem.canWrite oldact 32 then
(s1.writeReg64 Reg.x0 EFAULT_RET).advancePC
else
((s1.setMem (s1.mem.writeBytes oldact (SigAction.toBytes old))).writeReg64 Reg.x0 0#64).advancePCdef execTkill
tkill(tid, sig) (__NR_tkill = 130, do_tkill in kernel/):
-
if
tid ≤ 0,sig < 0, orsig > 64(_NSIG), returns-EINVAL; -
[INTENTIONAL_SCOPE]: iftid == 1(SIM_TID) andsig == SIGABRT(6):-
when
SIGABRT's disposition isSIG_DFL(0), terminates the process with fatal signal status .Signaled6#7 (shell /qemu-aarch64exit code128 + 6 = 134); -
when
SIGABRT's disposition isSIG_IGN(1), drops the ignored signal (sig_ignoredinkernel/) and returnssignal.c 0; -
otherwise fails closed with .
Exception"unmodeled tkill";
-
-
any other
(tid, sig)pair fails closed with .Exception"unmodeled tkill".
def execTkill (s : State) : State :=
let tid := s.readReg32 Reg.x0
let sig := s.readReg32 Reg.x1
if tid.sle 0#32 || sig.slt 0#32 || sig.toNat > 64 then
(s.writeReg64 Reg.x0 (-EINVAL)).advancePC
else if tid == 1#32 && sig == LINUX_SIGABRT then
let disp := (s.getSigAction LINUX_SIGABRT).handler
if disp == SIG_DFL then
s.setStatus (.Signaled 6#7)
else if disp == SIG_IGN then
(s.writeReg64 Reg.x0 0#64).advancePC
else
s.setStatus (.Exception "unmodeled tkill")
else
s.setStatus (.Exception "unmodeled tkill")def execTgkill
tgkill(tgid, tid, sig) (__NR_tgkill = 131, do_tkill in kernel/):
-
if
tid ≤ 0,tgid ≤ 0,sig < 0, orsig > 64(_NSIG), returns-EINVAL; -
[INTENTIONAL_SCOPE]: iftgid == 1,tid == 1(SIM_TID), andsig == SIGABRT(6):-
when
SIGABRT's disposition isSIG_DFL(0), terminates the process with fatal signal status .Signaled6#7 (shell /qemu-aarch64exit code128 + 6 = 134); -
when
SIGABRT's disposition isSIG_IGN(1), drops the ignored signal (sig_ignoredinkernel/) and returnssignal.c 0; -
otherwise fails closed with .
Exception"unmodeled tgkill";
-
-
any other
(tgid, tid, sig)triple fails closed with .Exception"unmodeled tgkill".
def execTgkill (s : State) : State :=
let tgid := s.readReg32 Reg.x0
let tid := s.readReg32 Reg.x1
let sig := s.readReg32 Reg.x2
if tid.sle 0#32 || tgid.sle 0#32 || sig.slt 0#32 || sig.toNat > 64 then
(s.writeReg64 Reg.x0 (-EINVAL)).advancePC
else if tgid == 1#32 && tid == 1#32 && sig == LINUX_SIGABRT then
let disp := (s.getSigAction LINUX_SIGABRT).handler
if disp == SIG_DFL then
s.setStatus (.Signaled 6#7)
else if disp == SIG_IGN then
(s.writeReg64 Reg.x0 0#64).advancePC
else
s.setStatus (.Exception "unmodeled tgkill")
else
s.setStatus (.Exception "unmodeled tgkill")end Arm64