Skip to content

Commit bf9ad37

Browse files
oleg-nesterovKAGA-KOKO
authored andcommitted
signal, x86: Delay calling signals in atomic on RT enabled kernels
On x86_64 we must disable preemption before we enable interrupts for stack faults, int3 and debugging, because the current task is using a per CPU debug stack defined by the IST. If we schedule out, another task can come in and use the same stack and cause the stack to be corrupted and crash the kernel on return. When CONFIG_PREEMPT_RT is enabled, spinlock_t locks become sleeping, and one of these is the spin lock used in signal handling. Some of the debug code (int3) causes do_trap() to send a signal. This function calls a spinlock_t lock that has been converted to a sleeping lock. If this happens, the above issues with the corrupted stack is possible. Instead of calling the signal right away, for PREEMPT_RT and x86, the signal information is stored on the stacks task_struct and TIF_NOTIFY_RESUME is set. Then on exit of the trap, the signal resume code will send the signal when preemption is enabled. [ rostedt: Switched from #ifdef CONFIG_PREEMPT_RT to ARCH_RT_DELAYS_SIGNAL_SEND and added comments to the code. ] [bigeasy: Add on 32bit as per Yang Shi, minor rewording. ] [ tglx: Use a config option ] Signed-off-by: Oleg Nesterov <[email protected]> Signed-off-by: Steven Rostedt <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Signed-off-by: Sebastian Andrzej Siewior <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Link: https://lore.kernel.org/r/Ygq5aBB/[email protected]
1 parent 0ce055f commit bf9ad37

File tree

5 files changed

+69
-1
lines changed

5 files changed

+69
-1
lines changed

arch/x86/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ config X86
120120
select ARCH_WANTS_NO_INSTR
121121
select ARCH_WANT_HUGE_PMD_SHARE
122122
select ARCH_WANT_LD_ORPHAN_WARN
123+
select ARCH_WANTS_RT_DELAYED_SIGNALS
123124
select ARCH_WANTS_THP_SWAP if X86_64
124125
select ARCH_HAS_PARANOID_L1D_FLUSH
125126
select BUILDTIME_TABLE_SORT

include/linux/sched.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,9 @@ struct task_struct {
10871087
/* Restored if set_restore_sigmask() was used: */
10881088
sigset_t saved_sigmask;
10891089
struct sigpending pending;
1090+
#ifdef CONFIG_RT_DELAYED_SIGNALS
1091+
struct kernel_siginfo forced_info;
1092+
#endif
10901093
unsigned long sas_ss_sp;
10911094
size_t sas_ss_size;
10921095
unsigned int sas_ss_flags;

kernel/Kconfig.preempt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,14 @@ config SCHED_CORE
132132
which is the likely usage by Linux distributions, there should
133133
be no measurable impact on performance.
134134

135-
135+
config ARCH_WANTS_RT_DELAYED_SIGNALS
136+
bool
137+
help
138+
This option is selected by architectures where raising signals
139+
can happen in atomic contexts on PREEMPT_RT enabled kernels. This
140+
option delays raising the signal until the return to user space
141+
loop where it is also delivered. X86 requires this to deliver
142+
signals from trap handlers which run on IST stacks.
143+
144+
config RT_DELAYED_SIGNALS
145+
def_bool PREEMPT_RT && ARCH_WANTS_RT_DELAYED_SIGNALS

kernel/entry/common.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,18 @@ static void handle_signal_work(struct pt_regs *regs, unsigned long ti_work)
148148
arch_do_signal_or_restart(regs, ti_work & _TIF_SIGPENDING);
149149
}
150150

151+
#ifdef CONFIG_RT_DELAYED_SIGNALS
152+
static inline void raise_delayed_signal(void)
153+
{
154+
if (unlikely(current->forced_info.si_signo)) {
155+
force_sig_info(&current->forced_info);
156+
current->forced_info.si_signo = 0;
157+
}
158+
}
159+
#else
160+
static inline void raise_delayed_signal(void) { }
161+
#endif
162+
151163
static unsigned long exit_to_user_mode_loop(struct pt_regs *regs,
152164
unsigned long ti_work)
153165
{
@@ -162,6 +174,8 @@ static unsigned long exit_to_user_mode_loop(struct pt_regs *regs,
162174
if (ti_work & _TIF_NEED_RESCHED)
163175
schedule();
164176

177+
raise_delayed_signal();
178+
165179
if (ti_work & _TIF_UPROBE)
166180
uprobe_notify_resume(regs);
167181

kernel/signal.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,43 @@ enum sig_handler {
13071307
HANDLER_EXIT, /* Only visible as the process exit code */
13081308
};
13091309

1310+
/*
1311+
* On some archictectures, PREEMPT_RT has to delay sending a signal from a
1312+
* trap since it cannot enable preemption, and the signal code's
1313+
* spin_locks turn into mutexes. Instead, it must set TIF_NOTIFY_RESUME
1314+
* which will send the signal on exit of the trap.
1315+
*/
1316+
#ifdef CONFIG_RT_DELAYED_SIGNALS
1317+
static inline bool force_sig_delayed(struct kernel_siginfo *info,
1318+
struct task_struct *t)
1319+
{
1320+
if (!in_atomic())
1321+
return false;
1322+
1323+
if (WARN_ON_ONCE(t->forced_info.si_signo))
1324+
return true;
1325+
1326+
if (is_si_special(info)) {
1327+
WARN_ON_ONCE(info != SEND_SIG_PRIV);
1328+
t->forced_info.si_signo = info->si_signo;
1329+
t->forced_info.si_errno = 0;
1330+
t->forced_info.si_code = SI_KERNEL;
1331+
t->forced_info.si_pid = 0;
1332+
t->forced_info.si_uid = 0;
1333+
} else {
1334+
t->forced_info = *info;
1335+
}
1336+
set_tsk_thread_flag(t, TIF_NOTIFY_RESUME);
1337+
return true;
1338+
}
1339+
#else
1340+
static inline bool force_sig_delayed(struct kernel_siginfo *info,
1341+
struct task_struct *t)
1342+
{
1343+
return false;
1344+
}
1345+
#endif
1346+
13101347
/*
13111348
* Force a signal that the process can't ignore: if necessary
13121349
* we unblock the signal and change any SIG_IGN to SIG_DFL.
@@ -1327,6 +1364,9 @@ force_sig_info_to_task(struct kernel_siginfo *info, struct task_struct *t,
13271364
struct k_sigaction *action;
13281365
int sig = info->si_signo;
13291366

1367+
if (force_sig_delayed(info, t))
1368+
return 0;
1369+
13301370
spin_lock_irqsave(&t->sighand->siglock, flags);
13311371
action = &t->sighand->action[sig-1];
13321372
ignored = action->sa.sa_handler == SIG_IGN;

0 commit comments

Comments
 (0)