Skip to content

Commit af40ff6

Browse files
Dave Martinwildea01
authored andcommitted
arm64: signal: Ensure si_code is valid for all fault signals
Currently, as reported by Eric, an invalid si_code value 0 is passed in many signals delivered to userspace in response to faults and other kernel errors. Typically 0 is passed when the fault is insufficiently diagnosable or when there does not appear to be any sensible alternative value to choose. This appears to violate POSIX, and is intuitively wrong for at least two reasons arising from the fact that 0 == SI_USER: 1) si_code is a union selector, and SI_USER (and si_code <= 0 in general) implies the existence of a different set of fields (siginfo._kill) from that which exists for a fault signal (siginfo._sigfault). However, the code raising the signal typically writes only the _sigfault fields, and the _kill fields make no sense in this case. Thus when userspace sees si_code == 0 (SI_USER) it may legitimately inspect fields in the inactive union member _kill and obtain garbage as a result. There appears to be software in the wild relying on this, albeit generally only for printing diagnostic messages. 2) Software that wants to be robust against spurious signals may discard signals where si_code == SI_USER (or <= 0), or may filter such signals based on the si_uid and si_pid fields of siginfo._sigkill. In the case of fault signals, this means that important (and usually fatal) error conditions may be silently ignored. In practice, many of the faults for which arm64 passes si_code == 0 are undiagnosable conditions such as exceptions with syndrome values in ESR_ELx to which the architecture does not yet assign any meaning, or conditions indicative of a bug or error in the kernel or system and thus that are unrecoverable and should never occur in normal operation. The approach taken in this patch is to translate all such undiagnosable or "impossible" synchronous fault conditions to SIGKILL, since these are at least probably localisable to a single process. Some of these conditions should really result in a kernel panic, but due to the lack of diagnostic information it is difficult to be certain: this patch does not add any calls to panic(), but this could change later if justified. Although si_code will not reach userspace in the case of SIGKILL, it is still desirable to pass a nonzero value so that the common siginfo handling code can detect incorrect use of si_code == 0 without false positives. In this case the si_code dependent siginfo fields will not be correctly initialised, but since they are not passed to userspace I deem this not to matter. A few faults can reasonably occur in realistic userspace scenarios, and _should_ raise a regular, handleable (but perhaps not ignorable/blockable) signal: for these, this patch attempts to choose a suitable standard si_code value for the raised signal in each case instead of 0. arm64 was the only arch to define a BUS_FIXME code, so after this patch nobody defines it. This patch therefore also removes the relevant code from siginfo_layout(). Cc: James Morse <[email protected]> Reported-by: Eric W. Biederman <[email protected]> Signed-off-by: Dave Martin <[email protected]> Signed-off-by: Will Deacon <[email protected]>
1 parent 6ae4b6e commit af40ff6

File tree

4 files changed

+59
-77
lines changed

4 files changed

+59
-77
lines changed

arch/arm64/include/uapi/asm/siginfo.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,4 @@
2828
#define FPE_FIXME 0 /* Broken dup of SI_USER */
2929
#endif /* __KERNEL__ */
3030

31-
/*
32-
* SIGBUS si_codes
33-
*/
34-
#ifdef __KERNEL__
35-
#define BUS_FIXME 0 /* Broken dup of SI_USER */
36-
#endif /* __KERNEL__ */
37-
38-
/*
39-
* SIGTRAP si_codes
40-
*/
41-
#ifdef __KERNEL__
42-
#define TRAP_FIXME 0 /* Broken dup of SI_USER */
43-
#endif /* __KERNEL__ */
44-
4531
#endif

arch/arm64/kernel/fpsimd.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ static void task_fpsimd_save(void)
285285
* re-enter user with corrupt state.
286286
* There's no way to recover, so kill it:
287287
*/
288-
force_signal_inject(SIGKILL, 0, 0);
288+
force_signal_inject(SIGKILL, SI_KERNEL, 0);
289289
return;
290290
}
291291

arch/arm64/mm/fault.c

Lines changed: 58 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -583,9 +583,9 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
583583
nmi_exit();
584584
}
585585

586-
info.si_signo = SIGBUS;
586+
info.si_signo = inf->sig;
587587
info.si_errno = 0;
588-
info.si_code = BUS_FIXME;
588+
info.si_code = inf->code;
589589
if (esr & ESR_ELx_FnV)
590590
info.si_addr = NULL;
591591
else
@@ -596,70 +596,70 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
596596
}
597597

598598
static const struct fault_info fault_info[] = {
599-
{ do_bad, SIGBUS, BUS_FIXME, "ttbr address size fault" },
600-
{ do_bad, SIGBUS, BUS_FIXME, "level 1 address size fault" },
601-
{ do_bad, SIGBUS, BUS_FIXME, "level 2 address size fault" },
602-
{ do_bad, SIGBUS, BUS_FIXME, "level 3 address size fault" },
599+
{ do_bad, SIGKILL, SI_KERNEL, "ttbr address size fault" },
600+
{ do_bad, SIGKILL, SI_KERNEL, "level 1 address size fault" },
601+
{ do_bad, SIGKILL, SI_KERNEL, "level 2 address size fault" },
602+
{ do_bad, SIGKILL, SI_KERNEL, "level 3 address size fault" },
603603
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 0 translation fault" },
604604
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 1 translation fault" },
605605
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 2 translation fault" },
606606
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 3 translation fault" },
607-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 8" },
607+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 8" },
608608
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 1 access flag fault" },
609609
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 access flag fault" },
610610
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 access flag fault" },
611-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 12" },
611+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 12" },
612612
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 1 permission fault" },
613613
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 permission fault" },
614614
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 permission fault" },
615-
{ do_sea, SIGBUS, BUS_FIXME, "synchronous external abort" },
616-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 17" },
617-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 18" },
618-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 19" },
619-
{ do_sea, SIGBUS, BUS_FIXME, "level 0 (translation table walk)" },
620-
{ do_sea, SIGBUS, BUS_FIXME, "level 1 (translation table walk)" },
621-
{ do_sea, SIGBUS, BUS_FIXME, "level 2 (translation table walk)" },
622-
{ do_sea, SIGBUS, BUS_FIXME, "level 3 (translation table walk)" },
623-
{ do_sea, SIGBUS, BUS_FIXME, "synchronous parity or ECC error" }, // Reserved when RAS is implemented
624-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 25" },
625-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 26" },
626-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 27" },
627-
{ do_sea, SIGBUS, BUS_FIXME, "level 0 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented
628-
{ do_sea, SIGBUS, BUS_FIXME, "level 1 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented
629-
{ do_sea, SIGBUS, BUS_FIXME, "level 2 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented
630-
{ do_sea, SIGBUS, BUS_FIXME, "level 3 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented
631-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 32" },
615+
{ do_sea, SIGBUS, BUS_OBJERR, "synchronous external abort" },
616+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 17" },
617+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 18" },
618+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 19" },
619+
{ do_sea, SIGKILL, SI_KERNEL, "level 0 (translation table walk)" },
620+
{ do_sea, SIGKILL, SI_KERNEL, "level 1 (translation table walk)" },
621+
{ do_sea, SIGKILL, SI_KERNEL, "level 2 (translation table walk)" },
622+
{ do_sea, SIGKILL, SI_KERNEL, "level 3 (translation table walk)" },
623+
{ do_sea, SIGBUS, BUS_OBJERR, "synchronous parity or ECC error" }, // Reserved when RAS is implemented
624+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 25" },
625+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 26" },
626+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 27" },
627+
{ do_sea, SIGKILL, SI_KERNEL, "level 0 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented
628+
{ do_sea, SIGKILL, SI_KERNEL, "level 1 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented
629+
{ do_sea, SIGKILL, SI_KERNEL, "level 2 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented
630+
{ do_sea, SIGKILL, SI_KERNEL, "level 3 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented
631+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 32" },
632632
{ do_alignment_fault, SIGBUS, BUS_ADRALN, "alignment fault" },
633-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 34" },
634-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 35" },
635-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 36" },
636-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 37" },
637-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 38" },
638-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 39" },
639-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 40" },
640-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 41" },
641-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 42" },
642-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 43" },
643-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 44" },
644-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 45" },
645-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 46" },
646-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 47" },
647-
{ do_bad, SIGBUS, BUS_FIXME, "TLB conflict abort" },
648-
{ do_bad, SIGBUS, BUS_FIXME, "Unsupported atomic hardware update fault" },
649-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 50" },
650-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 51" },
651-
{ do_bad, SIGBUS, BUS_FIXME, "implementation fault (lockdown abort)" },
652-
{ do_bad, SIGBUS, BUS_FIXME, "implementation fault (unsupported exclusive)" },
653-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 54" },
654-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 55" },
655-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 56" },
656-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 57" },
657-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 58" },
658-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 59" },
659-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 60" },
660-
{ do_bad, SIGBUS, BUS_FIXME, "section domain fault" },
661-
{ do_bad, SIGBUS, BUS_FIXME, "page domain fault" },
662-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 63" },
633+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 34" },
634+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 35" },
635+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 36" },
636+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 37" },
637+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 38" },
638+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 39" },
639+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 40" },
640+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 41" },
641+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 42" },
642+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 43" },
643+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 44" },
644+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 45" },
645+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 46" },
646+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 47" },
647+
{ do_bad, SIGKILL, SI_KERNEL, "TLB conflict abort" },
648+
{ do_bad, SIGKILL, SI_KERNEL, "Unsupported atomic hardware update fault" },
649+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 50" },
650+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 51" },
651+
{ do_bad, SIGKILL, SI_KERNEL, "implementation fault (lockdown abort)" },
652+
{ do_bad, SIGBUS, BUS_OBJERR, "implementation fault (unsupported exclusive)" },
653+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 54" },
654+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 55" },
655+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 56" },
656+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 57" },
657+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 58" },
658+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 59" },
659+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 60" },
660+
{ do_bad, SIGKILL, SI_KERNEL, "section domain fault" },
661+
{ do_bad, SIGKILL, SI_KERNEL, "page domain fault" },
662+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 63" },
663663
};
664664

665665
int handle_guest_sea(phys_addr_t addr, unsigned int esr)
@@ -748,11 +748,11 @@ static struct fault_info __refdata debug_fault_info[] = {
748748
{ do_bad, SIGTRAP, TRAP_HWBKPT, "hardware breakpoint" },
749749
{ do_bad, SIGTRAP, TRAP_HWBKPT, "hardware single-step" },
750750
{ do_bad, SIGTRAP, TRAP_HWBKPT, "hardware watchpoint" },
751-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 3" },
751+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 3" },
752752
{ do_bad, SIGTRAP, TRAP_BRKPT, "aarch32 BKPT" },
753-
{ do_bad, SIGTRAP, TRAP_FIXME, "aarch32 vector catch" },
753+
{ do_bad, SIGKILL, SI_KERNEL, "aarch32 vector catch" },
754754
{ early_brk64, SIGTRAP, TRAP_BRKPT, "aarch64 BRK" },
755-
{ do_bad, SIGBUS, BUS_FIXME, "unknown 7" },
755+
{ do_bad, SIGKILL, SI_KERNEL, "unknown 7" },
756756
};
757757

758758
void __init hook_debug_fault_code(int nr,

kernel/signal.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2843,10 +2843,6 @@ enum siginfo_layout siginfo_layout(int sig, int si_code)
28432843
#ifdef FPE_FIXME
28442844
if ((sig == SIGFPE) && (si_code == FPE_FIXME))
28452845
layout = SIL_FAULT;
2846-
#endif
2847-
#ifdef BUS_FIXME
2848-
if ((sig == SIGBUS) && (si_code == BUS_FIXME))
2849-
layout = SIL_FAULT;
28502846
#endif
28512847
}
28522848
return layout;

0 commit comments

Comments
 (0)