diff options
-rw-r--r-- | patches/Add_localversion_for_-RT_release.patch | 2 | ||||
-rw-r--r-- | patches/ptrace-fix-ptrace-vs-tasklist_lock-race-on-PREEMPT_R.patch | 68 |
2 files changed, 49 insertions, 21 deletions
diff --git a/patches/Add_localversion_for_-RT_release.patch b/patches/Add_localversion_for_-RT_release.patch index 22146ab020cb..efeddd431fc4 100644 --- a/patches/Add_localversion_for_-RT_release.patch +++ b/patches/Add_localversion_for_-RT_release.patch @@ -15,4 +15,4 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> --- /dev/null +++ b/localversion-rt @@ -0,0 +1 @@ -+-rt16 ++-rt17 diff --git a/patches/ptrace-fix-ptrace-vs-tasklist_lock-race-on-PREEMPT_R.patch b/patches/ptrace-fix-ptrace-vs-tasklist_lock-race-on-PREEMPT_R.patch index f36406146dc6..6dc5ace22489 100644 --- a/patches/ptrace-fix-ptrace-vs-tasklist_lock-race-on-PREEMPT_R.patch +++ b/patches/ptrace-fix-ptrace-vs-tasklist_lock-race-on-PREEMPT_R.patch @@ -6,7 +6,7 @@ As explained by Alexander Fyodorov <halcy@yandex.ru>: |read_lock(&tasklist_lock) in ptrace_stop() is converted to sleeping |lock on a PREEMPT_RT kernel, and it can remove __TASK_TRACED from -|task->state (by moving it to task->saved_state). If parent does +|task->__state (by moving it to task->saved_state). If parent does |wait() on child followed by a sys_ptrace call, the following race can |happen: | @@ -20,18 +20,25 @@ As explained by Alexander Fyodorov <halcy@yandex.ru>: The patch is based on his initial patch where an additional check is added in case the __TASK_TRACED moved to ->saved_state. The pi_lock is -taken in case the caller is interrupted between looking into ->state and -->saved_state. +acquired to have stable view on ->__state and ->saved_state. + +wait_task_inactive() needs to check both task states while waiting for the +expected task state. Should the expected task state be in ->saved_state then +the task is blocked on a sleeping lock. In this case wait_task_inactive() needs +to wait until the lock situtation has been resolved (the expected state is in +->__state). This ensures that the task is idle and does not wakeup as part of +lock resolving and races for instance with __switch_to_xtra() while the +debugger clears TIF_BLOCKSTEP() (noted by Oleg Nesterov). [ Fix for ptrace_unfreeze_traced() by Oleg Nesterov ] Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> -Link: https://lore.kernel.org/r/Yh/b19JikC+Vnm8i@linutronix.de +Link: https://lore.kernel.org/r/YkW55u6u2fo5QmV7@linutronix.de --- - include/linux/sched.h | 127 ++++++++++++++++++++++++++++++++++++++++++++++++-- + include/linux/sched.h | 128 ++++++++++++++++++++++++++++++++++++++++++++++++-- kernel/ptrace.c | 25 +++++---- - kernel/sched/core.c | 5 + - 3 files changed, 140 insertions(+), 17 deletions(-) + kernel/sched/core.c | 11 +++- + 3 files changed, 146 insertions(+), 18 deletions(-) --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -48,7 +55,7 @@ Link: https://lore.kernel.org/r/Yh/b19JikC+Vnm8i@linutronix.de /* * Special states are those that do not use the normal wait-loop pattern. See * the comment with set_special_state(). -@@ -2009,6 +2005,129 @@ static inline int test_tsk_need_resched( +@@ -2009,6 +2005,130 @@ static inline int test_tsk_need_resched( return unlikely(test_tsk_thread_flag(tsk,TIF_NEED_RESCHED)); } @@ -68,21 +75,22 @@ Link: https://lore.kernel.org/r/Yh/b19JikC+Vnm8i@linutronix.de + return match; +} + -+static inline bool __task_state_match_eq(struct task_struct *tsk, long state) ++static inline int __task_state_match_eq(struct task_struct *tsk, long state) +{ -+ bool match = false; ++ int match = 0; + + if (READ_ONCE(tsk->__state) == state) -+ match = true; ++ match = 1; + else if (tsk->saved_state == state) -+ match = true; ++ match = -1; ++ + return match; +} + -+static inline bool task_state_match_eq(struct task_struct *tsk, long state) ++static inline int task_state_match_eq(struct task_struct *tsk, long state) +{ + unsigned long flags; -+ bool match; ++ int match; + + raw_spin_lock_irqsave(&tsk->pi_lock, flags); + match = __task_state_match_eq(tsk, state); @@ -101,7 +109,7 @@ Link: https://lore.kernel.org/r/Yh/b19JikC+Vnm8i@linutronix.de + WRITE_ONCE(tsk->__state, new_state); + match = true; + } else if (tsk->saved_state & state) { -+ tsk->__state = new_state; ++ tsk->saved_state = new_state; + match = true; + } + raw_spin_unlock_irqrestore(&tsk->pi_lock, flags); @@ -133,12 +141,12 @@ Link: https://lore.kernel.org/r/Yh/b19JikC+Vnm8i@linutronix.de + return READ_ONCE(tsk->__state) & state; +} + -+static inline bool __task_state_match_eq(struct task_struct *tsk, long state) ++static inline int __task_state_match_eq(struct task_struct *tsk, long state) +{ + return READ_ONCE(tsk->__state) == state; +} + -+static inline bool task_state_match_eq(struct task_struct *tsk, long state) ++static inline int task_state_match_eq(struct task_struct *tsk, long state) +{ + return __task_state_match_eq(tsk, state); +} @@ -228,7 +236,16 @@ Link: https://lore.kernel.org/r/Yh/b19JikC+Vnm8i@linutronix.de --- a/kernel/sched/core.c +++ b/kernel/sched/core.c -@@ -3239,7 +3239,8 @@ unsigned long wait_task_inactive(struct +@@ -3219,6 +3219,8 @@ unsigned long wait_task_inactive(struct + struct rq *rq; + + for (;;) { ++ int match_type = 0; ++ + /* + * We do the initial early heuristics without holding + * any task-queue locks at all. We'll only try to get +@@ -3239,7 +3241,8 @@ unsigned long wait_task_inactive(struct * is actually now running somewhere else! */ while (task_running(rq, p)) { @@ -238,12 +255,23 @@ Link: https://lore.kernel.org/r/Yh/b19JikC+Vnm8i@linutronix.de return 0; cpu_relax(); } -@@ -3254,7 +3255,7 @@ unsigned long wait_task_inactive(struct +@@ -3254,7 +3257,9 @@ unsigned long wait_task_inactive(struct running = task_running(rq, p); queued = task_on_rq_queued(p); ncsw = 0; - if (!match_state || READ_ONCE(p->__state) == match_state) -+ if (!match_state || __task_state_match_eq(p, match_state)) ++ if (match_state) ++ match_type = __task_state_match_eq(p, match_state); ++ if (!match_state || match_type) ncsw = p->nvcsw | LONG_MIN; /* sets MSB */ task_rq_unlock(rq, p, &rf); +@@ -3284,7 +3289,7 @@ unsigned long wait_task_inactive(struct + * running right now), it's preempted, and we should + * yield - it could be a while. + */ +- if (unlikely(queued)) { ++ if (unlikely(queued || match_type < 0)) { + ktime_t to = NSEC_PER_SEC / HZ; + + set_current_state(TASK_UNINTERRUPTIBLE); |