Patchwork [Oneiric,CVE,1/3] ptrace: introduce signal_wake_up_state() and ptrace_signal_wake_up()

login
register
mail settings
Submitter Luis Henriques
Date Feb. 19, 2013, 2:18 p.m.
Message ID <1361283503-26362-2-git-send-email-luis.henriques@canonical.com>
Download mbox | patch
Permalink /patch/221706/
State New
Headers show

Comments

Luis Henriques - Feb. 19, 2013, 2:18 p.m.
From: Oleg Nesterov <oleg@redhat.com>

CVE-2013-0871

Cleanup and preparation for the next change.

signal_wake_up(resume => true) is overused. None of ptrace/jctl callers
actually want to wakeup a TASK_WAKEKILL task, but they can't specify the
necessary mask.

Turn signal_wake_up() into signal_wake_up_state(state), reintroduce
signal_wake_up() as a trivial helper, and add ptrace_signal_wake_up()
which adds __TASK_TRACED.

This way ptrace_signal_wake_up() can work "inside" ptrace_request()
even if the tracee doesn't have the TASK_WAKEKILL bit set.

Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
(backported from commit 910ffdb18a6408e14febbb6e4b6840fd2c928c82)

Conflicts:
	kernel/ptrace.c
	kernel/signal.c
Signed-off-by: Luis Henriques <luis.henriques@canonical.com>
---
 include/linux/sched.h | 11 ++++++++++-
 kernel/ptrace.c       |  4 ++--
 kernel/signal.c       | 12 +++---------
 3 files changed, 15 insertions(+), 12 deletions(-)
Colin King - Feb. 19, 2013, 4:04 p.m.
On 19/02/13 14:18, Luis Henriques wrote:
> From: Oleg Nesterov <oleg@redhat.com>
>
> CVE-2013-0871
>
> Cleanup and preparation for the next change.
>
> signal_wake_up(resume => true) is overused. None of ptrace/jctl callers
> actually want to wakeup a TASK_WAKEKILL task, but they can't specify the
> necessary mask.
>
> Turn signal_wake_up() into signal_wake_up_state(state), reintroduce
> signal_wake_up() as a trivial helper, and add ptrace_signal_wake_up()
> which adds __TASK_TRACED.
>
> This way ptrace_signal_wake_up() can work "inside" ptrace_request()
> even if the tracee doesn't have the TASK_WAKEKILL bit set.
>
> Signed-off-by: Oleg Nesterov <oleg@redhat.com>
> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
> (backported from commit 910ffdb18a6408e14febbb6e4b6840fd2c928c82)
>
> Conflicts:
> 	kernel/ptrace.c
> 	kernel/signal.c
> Signed-off-by: Luis Henriques <luis.henriques@canonical.com>
> ---
>   include/linux/sched.h | 11 ++++++++++-
>   kernel/ptrace.c       |  4 ++--
>   kernel/signal.c       | 12 +++---------
>   3 files changed, 15 insertions(+), 12 deletions(-)
>
> diff --git a/include/linux/sched.h b/include/linux/sched.h
> index 547e4a8..ed0f39a 100644
> --- a/include/linux/sched.h
> +++ b/include/linux/sched.h
> @@ -2571,7 +2571,16 @@ static inline void thread_group_cputime_init(struct signal_struct *sig)
>   extern void recalc_sigpending_and_wake(struct task_struct *t);
>   extern void recalc_sigpending(void);
>
> -extern void signal_wake_up(struct task_struct *t, int resume_stopped);
> +extern void signal_wake_up_state(struct task_struct *t, unsigned int state);
> +
> +static inline void signal_wake_up(struct task_struct *t, bool resume)
> +{
> +	signal_wake_up_state(t, resume ? TASK_WAKEKILL : 0);
> +}
> +static inline void ptrace_signal_wake_up(struct task_struct *t, bool resume)
> +{
> +	signal_wake_up_state(t, resume ? __TASK_TRACED : 0);
> +}
>
>   /*
>    * Wrappers for p->thread_info->cpu access. No-op on UP.
> diff --git a/kernel/ptrace.c b/kernel/ptrace.c
> index 2df1157..d77fa9e 100644
> --- a/kernel/ptrace.c
> +++ b/kernel/ptrace.c
> @@ -92,7 +92,7 @@ void __ptrace_unlink(struct task_struct *child)
>   	 * TASK_KILLABLE sleeps.
>   	 */
>   	if (child->group_stop & GROUP_STOP_PENDING || task_is_traced(child))
> -		signal_wake_up(child, task_is_traced(child));
> +		ptrace_signal_wake_up(child, true);
>
>   	spin_unlock(&child->sighand->siglock);
>   }
> @@ -245,7 +245,7 @@ static int ptrace_attach(struct task_struct *task)
>   	 */
>   	if (task_is_stopped(task)) {
>   		task->group_stop |= GROUP_STOP_PENDING | GROUP_STOP_TRAPPING;
> -		signal_wake_up(task, 1);
> +		signal_wake_up_state(task, __TASK_STOPPED);
>   		wait_trap = true;
>   	}
>
> diff --git a/kernel/signal.c b/kernel/signal.c
> index 43fee1c..8b0dd5b 100644
> --- a/kernel/signal.c
> +++ b/kernel/signal.c
> @@ -631,23 +631,17 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
>    * No need to set need_resched since signal event passing
>    * goes through ->blocked
>    */
> -void signal_wake_up(struct task_struct *t, int resume)
> +void signal_wake_up_state(struct task_struct *t, unsigned int state)
>   {
> -	unsigned int mask;
> -
>   	set_tsk_thread_flag(t, TIF_SIGPENDING);
> -
>   	/*
> -	 * For SIGKILL, we want to wake it up in the stopped/traced/killable
> +	 * TASK_WAKEKILL also means wake it up in the stopped/traced/killable
>   	 * case. We don't check t->state here because there is a race with it
>   	 * executing another processor and just now entering stopped state.
>   	 * By using wake_up_state, we ensure the process will wake up and
>   	 * handle its death signal.
>   	 */
> -	mask = TASK_INTERRUPTIBLE;
> -	if (resume)
> -		mask |= TASK_WAKEKILL;
> -	if (!wake_up_state(t, mask))
> +	if (!wake_up_state(t, state | TASK_INTERRUPTIBLE))
>   		kick_process(t);
>   }
>
>

ptrace_attach() for Oneiric does not support PTRACE_INTERRUPT or 
PTRACE_LISTEN which this backport has to omit, so that is sane looking 
to me.

Acked-by: Colin Ian King <colin.king@canonical.com>

Patch

diff --git a/include/linux/sched.h b/include/linux/sched.h
index 547e4a8..ed0f39a 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -2571,7 +2571,16 @@  static inline void thread_group_cputime_init(struct signal_struct *sig)
 extern void recalc_sigpending_and_wake(struct task_struct *t);
 extern void recalc_sigpending(void);
 
-extern void signal_wake_up(struct task_struct *t, int resume_stopped);
+extern void signal_wake_up_state(struct task_struct *t, unsigned int state);
+
+static inline void signal_wake_up(struct task_struct *t, bool resume)
+{
+	signal_wake_up_state(t, resume ? TASK_WAKEKILL : 0);
+}
+static inline void ptrace_signal_wake_up(struct task_struct *t, bool resume)
+{
+	signal_wake_up_state(t, resume ? __TASK_TRACED : 0);
+}
 
 /*
  * Wrappers for p->thread_info->cpu access. No-op on UP.
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 2df1157..d77fa9e 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -92,7 +92,7 @@  void __ptrace_unlink(struct task_struct *child)
 	 * TASK_KILLABLE sleeps.
 	 */
 	if (child->group_stop & GROUP_STOP_PENDING || task_is_traced(child))
-		signal_wake_up(child, task_is_traced(child));
+		ptrace_signal_wake_up(child, true);
 
 	spin_unlock(&child->sighand->siglock);
 }
@@ -245,7 +245,7 @@  static int ptrace_attach(struct task_struct *task)
 	 */
 	if (task_is_stopped(task)) {
 		task->group_stop |= GROUP_STOP_PENDING | GROUP_STOP_TRAPPING;
-		signal_wake_up(task, 1);
+		signal_wake_up_state(task, __TASK_STOPPED);
 		wait_trap = true;
 	}
 
diff --git a/kernel/signal.c b/kernel/signal.c
index 43fee1c..8b0dd5b 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -631,23 +631,17 @@  int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
  * No need to set need_resched since signal event passing
  * goes through ->blocked
  */
-void signal_wake_up(struct task_struct *t, int resume)
+void signal_wake_up_state(struct task_struct *t, unsigned int state)
 {
-	unsigned int mask;
-
 	set_tsk_thread_flag(t, TIF_SIGPENDING);
-
 	/*
-	 * For SIGKILL, we want to wake it up in the stopped/traced/killable
+	 * TASK_WAKEKILL also means wake it up in the stopped/traced/killable
 	 * case. We don't check t->state here because there is a race with it
 	 * executing another processor and just now entering stopped state.
 	 * By using wake_up_state, we ensure the process will wake up and
 	 * handle its death signal.
 	 */
-	mask = TASK_INTERRUPTIBLE;
-	if (resume)
-		mask |= TASK_WAKEKILL;
-	if (!wake_up_state(t, mask))
+	if (!wake_up_state(t, state | TASK_INTERRUPTIBLE))
 		kick_process(t);
 }