Re: [PATCH] signals: check_kill_permission: check session under tasklist_lock

Previous thread: [RFC,PATCH 1/2] signals: re-assign CLD_CONTINUED notification from the sender to reciever by Oleg Nesterov on Tuesday, March 4, 2008 - 11:57 am. (3 messages)

Next thread: [PATCH] vfs: Fix NULL pointer dereference in fsync_buffers_list() by Jan Kara on Tuesday, March 4, 2008 - 12:11 pm. (1 message)
From: Oleg Nesterov
Date: Tuesday, March 4, 2008 - 11:57 am

Previously handle_stop_signal(SIGCONT) could drop ->siglock. That is why
kill_pid_info(SIGCONT) takes tasklist_lock to make sure the target task
can't go away after unlock. Not needed now.

Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru>

--- 25/include/linux/signal.h~1__KILL_NO_TASKLIST	2008-02-15 16:59:17.000000000 +0300
+++ 25/include/linux/signal.h	2008-03-04 21:43:25.000000000 +0300
@@ -362,8 +362,6 @@ int unhandled_signal(struct task_struct 
 #define sig_kernel_stop(sig) \
 	(((sig) < SIGRTMIN) && siginmask(sig, SIG_KERNEL_STOP_MASK))
 
-#define sig_needs_tasklist(sig)	((sig) == SIGCONT)
-
 #define sig_user_defined(t, signr) \
 	(((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_DFL) &&	\
 	 ((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_IGN))
--- 25/kernel/signal.c~1__KILL_NO_TASKLIST	2008-03-04 21:24:20.000000000 +0300
+++ 25/kernel/signal.c	2008-03-04 21:44:58.000000000 +0300
@@ -1030,9 +1030,6 @@ int kill_pid_info(int sig, struct siginf
 	struct task_struct *p;
 
 	rcu_read_lock();
-	if (unlikely(sig_needs_tasklist(sig)))
-		read_lock(&tasklist_lock);
-
 retry:
 	p = pid_task(pid, PIDTYPE_PID);
 	if (p) {
@@ -1046,10 +1043,8 @@ retry:
 			 */
 			goto retry;
 	}
-
-	if (unlikely(sig_needs_tasklist(sig)))
-		read_unlock(&tasklist_lock);
 	rcu_read_unlock();
+
 	return error;
 }
 

--

From: Roland McGrath
Date: Thursday, March 6, 2008 - 3:56 am

A nice payoff for the earlier cleanups.  
Maybe it even improves parallelism for a real workload someday.

Thanks,
Roland
--

From: Atsushi Tsuji
Date: Monday, March 17, 2008 - 4:30 am

Hi Oleg,

I tried your patches on vanila kernel 2.6.25-rc3 (ia64).  Then, I got
the NULL pointer dereference at task_session_nr(t) in
check_kill_permission(). That is why t->signal->__session is accessed
after t->signal was released.  It is reproducible by sending many
SIGCONT signals to exiting processes.

The trace is as follows:

Pid: 7807, CPU 9, comm:           kill_sigcont
psr : 00001210085a6010 ifs : 800000000000030a ip  : [<a0000001000ab441>]    Not 
tainted (2.6.25-rc3-debug)
ip is at check_kill_permission+0x181/0x2a0
unat: 0000000000000000 pfs : 000000000000038a rsc : 0000000000000003
rnat: 0000000000000206 bsps: 0000000000000003 pr  : 000000000056a999
ldrs: 0000000000000000 ccv : 0000000000000000 fpsr: 0009804c0270033f
csd : 0000000000000000 ssd : 0000000000000000
b0  : a0000001000ad510 b6  : a00000010000eb80 b7  : a00000010000eb50
f6  : 000000000000000000000 f7  : 000000000000000000000
f8  : 000000000000000000000 f9  : 000000000000000000000
f10 : 000000000000000000000 f11 : 000000000000000000000
r1  : a000000100cc6a70 r2  : 0000000000000000 r3  : e000001f43f07dc8
r8  : a000000100add08c r9  : a000000100add08c r10 : 0000000000003dc4
r11 : e000001f49d901e4 r12 : e000001f43f07da0 r13 : e000001f43f00000
r14 : 0000000000000012 r15 : 0000000000000000 r16 : a000000100add0a8
r17 : a000000100add0a8 r18 : fb00000000000000 r19 : d800000000000000
r20 : e000001f4493de48 r21 : 0000000000001df6 r22 : 0000000000000100
r23 : e000001f424c5280 r24 : 0000000000000000 r25 : e000001f424c5180
r26 : e000001f49d90b08 r27 : e000001f43f00b08 r28 : 0000000000003dc4
r29 : a000000100adcec0 r30 : a000000100a73a28 r31 : e000001f4493de40

Call Trace:
  [<a000000100014440>] show_stack+0x40/0xa0
                                 sp=e000001f43f077f0 bsp=e000001f43f01060
  [<a000000100014d50>] show_regs+0x850/0x8a0
                                 sp=e000001f43f079c0 bsp=e000001f43f01008
  [<a0000001000360b0>] die+0x1b0/0x2c0
                                 sp=e000001f43f079c0 ...
From: Oleg Nesterov
Date: Monday, March 17, 2008 - 10:01 am

Ah. Indeed!!! Thanks a lot Atsushi.

Note that check_kill_permission() is the last user of the deprecated
signal->__session/session, I was going to change this code later, but
missed the issue you pointed out.

I'll make the patch tomorrow.

Thanks!

Oleg.

--

From: Oleg Nesterov
Date: Tuesday, March 18, 2008 - 7:44 am

(on top of signals-cleanup-security_task_kill-usage-implementation.patch)

This wasn't documented, but as Atsushi Tsuji <a-tsuji@bk.jp.nec.com> pointed
out check_kill_permission() needs tasklist_lock for task_session_nr().
I missed this fact when removed tasklist from the callers.

Change check_kill_permission() to take tasklist_lock for the SIGCONT case.
Re-order security checks so that we take tasklist_lock only if/when it is
actually needed. This is a minimal fix for now, tasklist will be removed
later.

Also change the code to use task_session() instead of task_session_nr().

Also, remove the SIGCONT check from cap_task_kill(), it is bogus (and the
whole function is bogus. Serge, Eric, why it is still alive?).

Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru>

--- 25/kernel/signal.c~CKP_TAKE_TASKLIST	2008-03-18 14:47:00.000000000 +0300
+++ 25/kernel/signal.c	2008-03-18 17:25:19.000000000 +0300
@@ -533,6 +533,7 @@ static int rm_from_queue(unsigned long m
 static int check_kill_permission(int sig, struct siginfo *info,
 				 struct task_struct *t)
 {
+	struct pid *sid;
 	int error;
 
 	if (!valid_signal(sig))
@@ -545,11 +546,24 @@ static int check_kill_permission(int sig
 	if (error)
 		return error;
 
-	if (((sig != SIGCONT) || (task_session_nr(current) != task_session_nr(t)))
-	    && (current->euid ^ t->suid) && (current->euid ^ t->uid)
-	    && (current->uid ^ t->suid) && (current->uid ^ t->uid)
-	    && !capable(CAP_KILL))
-		return -EPERM;
+	if ((current->euid ^ t->suid) && (current->euid ^ t->uid) &&
+	    (current->uid  ^ t->suid) && (current->uid  ^ t->uid) &&
+	    !capable(CAP_KILL)) {
+		switch (sig) {
+		case SIGCONT:
+			read_lock(&tasklist_lock);
+			sid = task_session(t);
+			read_unlock(&tasklist_lock);
+			/*
+			 * We don't return the error if sid == NULL. The
+			 * task was unhashed, the caller must notice this.
+			 */
+			if (!sid || sid == task_session(current))
+				break;
+		default:
+			return -EPERM;
+		}
+	}
 
 	return ...
From: serge
Date: Tuesday, March 18, 2008 - 1:03 pm

Note that cap_task_kill() should be gone anyway.  What tree were you
basing this on?

thanks,
-serge
--

From: Oleg Nesterov
Date: Tuesday, March 18, 2008 - 1:17 pm

Ah. I realy hoped that cap_task_kill() was already killed. And I googled
this patch: http://marc.info/?l=linux-kernel&m=120422062515386

But I checked 2.6.25-rc5-mm1.bz2, it is still here. And I didn't find
anything related in http://userweb.kernel.org/~akpm/mmotm/. I even checked
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=history;f=security/...

So, is it still here or killed? If it is dead - great ;)

Oleg.

--

From: serge
Date: Tuesday, March 18, 2008 - 4:14 pm

Hmm, I thought it had been pulled in.  I don't see it.  I'll re-spin and
re-send against -mm, -stable, and -git.

thanks,
-serge
--

From: Atsushi Tsuji
Date: Tuesday, March 18, 2008 - 7:19 pm

Previous thread: [RFC,PATCH 1/2] signals: re-assign CLD_CONTINUED notification from the sender to reciever by Oleg Nesterov on Tuesday, March 4, 2008 - 11:57 am. (3 messages)

Next thread: [PATCH] vfs: Fix NULL pointer dereference in fsync_buffers_list() by Jan Kara on Tuesday, March 4, 2008 - 12:11 pm. (1 message)