summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--patches/Add_localversion_for_-RT_release.patch2
-rw-r--r--patches/ptrace-fix-ptrace-vs-tasklist_lock-race-on-PREEMPT_R.patch68
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);