Skip to contentSkip to conversation

pid: make setpgid() system call use RCU read-side critical section

RFC PATCH In review Paul E. McKenney 4 participants 9 messages Aug 30, 2010 – Sep 16, 2010

lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 pid: make setpgid() system call use RCU read-side critical section

[   23.584719]
[   23.584720] ===================================================
[   23.585059] [ INFO: suspicious rcu_dereference_check() usage. ]
[   23.585176] ---------------------------------------------------
[   23.585176] kernel/pid.c:419 invoked rcu_dereference_check() without protection!
[   23.585176]
[   23.585176] other info that might help us debug this:
[   23.585176]
[   23.585176]
[   23.585176] rcu_scheduler_active = 1, debug_locks = 1
[   23.585176] 1 lock held by rc.sysinit/728:
[   23.585176]  #0:  (tasklist_lock){.+.+..}, at: [<ffffffff8104771f>] sys_setpgid+0x5f/0x193
[   23.585176]
[   23.585176] stack backtrace:
[   23.585176] Pid: 728, comm: rc.sysinit Not tainted 2.6.36-rc2 #2
[   23.585176] Call Trace:
[   23.585176]  [<ffffffff8105b436>] lockdep_rcu_dereference+0x99/0xa2
[   23.585176]  [<ffffffff8104c324>] find_task_by_pid_ns+0x50/0x6a
[   23.585176]  [<ffffffff8104c35b>] find_task_by_vpid+0x1d/0x1f
[   23.585176]  [<ffffffff81047727>] sys_setpgid+0x67/0x193
[   23.585176]  [<ffffffff810029eb>] system_call_fastpath+0x16/0x1b
[   24.959669] type=1400 audit(1282938522.956:4): avc:  denied  { module_request } for  pid=766 comm="hwclock" kmod="char-major-10-135" scontext=system_u:system_r:hwclock_t:s0 tcontext=system_u:system_r:kernel_t:s0 tclas

It turns out that the setpgid() system call fails to enter an RCU read-side critical section before doing a PID-to-task_struct translation. This commit therefore does rcu_read_lock() before the translation, and also does rcu_read_unlock() after the last use of the returned pointer.

---

 sys.c |    2 ++
 1 file changed, 2 insertions(+)
kernel/sys.c +2 -0
index e9ad444..05a4b0c 100644
@@ -938,6 +938,7 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
 	write_lock_irq(&tasklist_lock);
 
 	err = -ESRCH;
+	rcu_read_lock();
 	p = find_task_by_vpid(pid);
 	if (!p)
 		goto out;
@@ -983,6 +984,7 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
 	err = 0;
 out:
 	/* All paths lead to here, thus we are safe. -DaveM */
+	rcu_read_unlock();
 	write_unlock_irq(&tasklist_lock);
 	return err;
 }
#1 lore raw

Ccing Oleg.

Quoted 39 lines · Paul E. McKenney
[   23.584720] ===================================================
[   23.585059] [ INFO: suspicious rcu_dereference_check() usage. ]
[   23.585176] ---------------------------------------------------
[   23.585176] kernel/pid.c:419 invoked rcu_dereference_check() without protection!
[   23.585176]
[   23.585176] other info that might help us debug this:
[   23.585176]
[   23.585176]
[   23.585176] rcu_scheduler_active = 1, debug_locks = 1
[   23.585176] 1 lock held by rc.sysinit/728:
[   23.585176]  #0:  (tasklist_lock){.+.+..}, at: [<ffffffff8104771f>] sys_setpgid+0x5f/0x193
[   23.585176]
[   23.585176] stack backtrace:
[   23.585176] Pid: 728, comm: rc.sysinit Not tainted 2.6.36-rc2 #2
[   23.585176] Call Trace:
[   23.585176]  [<ffffffff8105b436>] lockdep_rcu_dereference+0x99/0xa2
[   23.585176]  [<ffffffff8104c324>] find_task_by_pid_ns+0x50/0x6a
[   23.585176]  [<ffffffff8104c35b>] find_task_by_vpid+0x1d/0x1f
[   23.585176]  [<ffffffff81047727>] sys_setpgid+0x67/0x193
[   23.585176]  [<ffffffff810029eb>] system_call_fastpath+0x16/0x1b
[   24.959669] type=1400 audit(1282938522.956:4): avc:  denied  { module_request } for  pid=766 comm="hwclock" kmod="char-major-10-135" scontext=system_u:system_r:hwclock_t:s0 tcontext=system_u:system_r:kernel_t:s0 tclas

It turns out that the setpgid() system call fails to enter an RCU read-side critical section before doing a PID-to-task_struct translation. This commit therefore does rcu_read_lock() before the translation, and also does rcu_read_unlock() after the last use of the returned pointer.

---

 sys.c |    2 ++
 1 file changed, 2 insertions(+)
kernel/sys.c +0 -0
index e9ad444..05a4b0c 100644

kernel/sys.c SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid) · line 942

Quoted 5 lines
kernel/sys.c +1 -0
@@ -938,6 +938,7 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
 	write_lock_irq(&tasklist_lock);
 
 	err = -ESRCH;
+	rcu_read_lock();
 	p = find_task_by_vpid(pid);

AFAICT the missing lock doesn't harm due to the write_lock of tasklist above. But is probably a good thing to do anyway.

regards,

Signature · 2 lines
js
suse labs
#2 lore raw
Quoted 44 lines · Jiri Slaby

Ccing Oleg.

Quoted 41 lines · Paul E. McKenney
[   23.584720] ===================================================
[   23.585059] [ INFO: suspicious rcu_dereference_check() usage. ]
[   23.585176] ---------------------------------------------------
[   23.585176] kernel/pid.c:419 invoked rcu_dereference_check() without protection!
[   23.585176]
[   23.585176] other info that might help us debug this:
[   23.585176]
[   23.585176]
[   23.585176] rcu_scheduler_active = 1, debug_locks = 1
[   23.585176] 1 lock held by rc.sysinit/728:
[   23.585176]  #0:  (tasklist_lock){.+.+..}, at: [<ffffffff8104771f>] sys_setpgid+0x5f/0x193
[   23.585176]
[   23.585176] stack backtrace:
[   23.585176] Pid: 728, comm: rc.sysinit Not tainted 2.6.36-rc2 #2
[   23.585176] Call Trace:
[   23.585176]  [<ffffffff8105b436>] lockdep_rcu_dereference+0x99/0xa2
[   23.585176]  [<ffffffff8104c324>] find_task_by_pid_ns+0x50/0x6a
[   23.585176]  [<ffffffff8104c35b>] find_task_by_vpid+0x1d/0x1f
[   23.585176]  [<ffffffff81047727>] sys_setpgid+0x67/0x193
[   23.585176]  [<ffffffff810029eb>] system_call_fastpath+0x16/0x1b
[   24.959669] type=1400 audit(1282938522.956:4): avc:  denied  { module_request } for  pid=766 comm="hwclock" kmod="char-major-10-135" scontext=system_u:system_r:hwclock_t:s0 tcontext=system_u:system_r:kernel_t:s0 tclas

It turns out that the setpgid() system call fails to enter an RCU read-side critical section before doing a PID-to-task_struct translation. This commit therefore does rcu_read_lock() before the translation, and also does rcu_read_unlock() after the last use of the returned pointer.

---

 sys.c |    2 ++
 1 file changed, 2 insertions(+)
kernel/sys.c +0 -0
index e9ad444..05a4b0c 100644
@@ -938,6 +938,7 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
 	write_lock_irq(&tasklist_lock);
 
Quoted 6 lines

kernel/sys.c SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid) · line 942

Quoted 3 lines
kernel/sys.c +1 -0
 	err = -ESRCH;
+	rcu_read_lock();
 	p = find_task_by_vpid(pid);

AFAICT the missing lock doesn't harm due to the write_lock of tasklist above. But is probably a good thing to do anyway.

Or we can add the tasklist lock to the rcu_dereference_check() condition.

Thanx, Paul

Quoted 9 lines

regards,

Signature · 7 lines
js
suse labs
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
#3 lore raw
In reply to Jiri Slaby
Quoted 2 lines · Jiri Slaby

Ccing Oleg.

Sorry for delay...

Quoted 11 lines

kernel/sys.c SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid) · line 942

Quoted 8 lines
kernel/sys.c +1 -0
@@ -938,6 +938,7 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
 	write_lock_irq(&tasklist_lock);
 
 	err = -ESRCH;
+	rcu_read_lock();
 	p = find_task_by_vpid(pid);

AFAICT the missing lock doesn't harm due to the write_lock of tasklist above. But is probably a good thing to do anyway.

The problem is, find_task_by_vpid() is not safe without RCU. It is not that the returned task_struct can't go away, find_pid_ns() itself is not safe. This is because the failing copy_process() calls free_pid() without tasklist_lock and modifies pid_hash[] list.

Oleg.

#4 lore raw
Quoted 13 lines · Oleg Nesterov
Quoted 11 lines · Jiri Slaby

kernel/sys.c SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid) · line 942

Quoted 8 lines
kernel/sys.c +1 -0
@@ -938,6 +938,7 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
 	write_lock_irq(&tasklist_lock);
 
 	err = -ESRCH;
+	rcu_read_lock();
 	p = find_task_by_vpid(pid);

AFAICT the missing lock doesn't harm due to the write_lock of tasklist above. But is probably a good thing to do anyway.

Quoted 4 lines

The problem is, find_task_by_vpid() is not safe without RCU. It is not that the returned task_struct can't go away, find_pid_ns() itself is not safe. This is because the failing copy_process() calls free_pid() without tasklist_lock and modifies pid_hash[] list.

That said, it (950eaaca681c4) should probably go into stable. (Apply to all 32-35 whichever are maintained currently.)

thanks,

Signature · 2 lines
js
suse labs
#5 lore raw
Quoted 15 lines · Jiri Slaby
Quoted 13 lines · Oleg Nesterov
Quoted 11 lines · Jiri Slaby

kernel/sys.c SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid) · line 942

Quoted 8 lines
kernel/sys.c +1 -0
@@ -938,6 +938,7 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
 	write_lock_irq(&tasklist_lock);
 
 	err = -ESRCH;
+	rcu_read_lock();
 	p = find_task_by_vpid(pid);

AFAICT the missing lock doesn't harm due to the write_lock of tasklist above. But is probably a good thing to do anyway.

Quoted 7 lines
Quoted 4 lines

The problem is, find_task_by_vpid() is not safe without RCU. It is not that the returned task_struct can't go away, find_pid_ns() itself is not safe. This is because the failing copy_process() calls free_pid() without tasklist_lock and modifies pid_hash[] list.

That said, it (950eaaca681c4) should probably go into stable. (Apply to all 32-35 whichever are maintained currently.)

Perhaps, but the race is mostly theoretical.

To be honest, I think 950eaaca681c4 needs a comment to explain what rcu_read_lock() protects, or perhaps we can make it more explicit.

Oleg.

kernel/sys.c +4 -2
@@ -931,7 +931,6 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid
 		pgid = pid;
 	if (pgid < 0)
 		return -EINVAL;
-	rcu_read_lock();
 
 	/* From this point forward we keep holding onto the tasklist lock
 	 * so that our parent does not change from under us. -DaveM
@@ -939,7 +938,9 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid
 	write_lock_irq(&tasklist_lock);
 
 	err = -ESRCH;
+	rcu_read_lock();
 	p = find_task_by_vpid(pid);
+	rcu_read_unlock();
 	if (!p)
 		goto out;
 
@@ -968,7 +969,9 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid
 	if (pgid != pid) {
 		struct task_struct *g;
 
+		rcu_read_lock();
 		pgrp = find_vpid(pgid);
+		rcu_read_unlock();
 		g = pid_task(pgrp, PIDTYPE_PGID);
 		if (!g || task_session(g) != task_session(group_leader))
 			goto out;
@@ -985,7 +988,6 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid
 out:
 	/* All paths lead to here, thus we are safe. -DaveM */
 	write_unlock_irq(&tasklist_lock);
-	rcu_read_unlock();
 	return err;
 }
 
#6 lore raw
In reply to Paul E. McKenney

kernel/sys.c SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)

Quoted 7 lines · Paul E. McKenney
kernel/sys.c +2 -0
 	write_lock_irq(&tasklist_lock);
 
 	err = -ESRCH;
+	rcu_read_lock();
...
+	rcu_read_unlock();
 	write_unlock_irq(&tasklist_lock);

I would generally put the rcu lock outside the IRQ disabled section on the basis that it's better to keep the amount of time we have interrupts disabled shortest.

Quoted 1 line

'Reported-by' might be more consistent with what others use.

David

#7 lore raw
Quoted 14 lines · David Howells

Review

Quoted 7 lines · Paul E. McKenney
(quoted diff) +2 -0
 	write_lock_irq(&tasklist_lock);
 
 	err = -ESRCH;
+	rcu_read_lock();
...
+	rcu_read_unlock();
 	write_unlock_irq(&tasklist_lock);

I would generally put the rcu lock outside the IRQ disabled section on the basis that it's better to keep the amount of time we have interrupts disabled shortest.

Quoted 5 lines
Quoted 1 line

'Reported-by' might be more consistent with what others use.

David

Good points, how about the following?

Thanx, Paul

------------------------------------------------------------------------

pid: make setpgid() system call use RCU read-side critical section

[   23.584719]
[   23.584720] ===================================================
[   23.585059] [ INFO: suspicious rcu_dereference_check() usage. ]
[   23.585176] ---------------------------------------------------
[   23.585176] kernel/pid.c:419 invoked rcu_dereference_check() without protection!
[   23.585176]
[   23.585176] other info that might help us debug this:
[   23.585176]
[   23.585176]
[   23.585176] rcu_scheduler_active = 1, debug_locks = 1
[   23.585176] 1 lock held by rc.sysinit/728:
[   23.585176]  #0:  (tasklist_lock){.+.+..}, at: [<ffffffff8104771f>] sys_setpgid+0x5f/0x193
[   23.585176]
[   23.585176] stack backtrace:
[   23.585176] Pid: 728, comm: rc.sysinit Not tainted 2.6.36-rc2 #2
[   23.585176] Call Trace:
[   23.585176]  [<ffffffff8105b436>] lockdep_rcu_dereference+0x99/0xa2
[   23.585176]  [<ffffffff8104c324>] find_task_by_pid_ns+0x50/0x6a
[   23.585176]  [<ffffffff8104c35b>] find_task_by_vpid+0x1d/0x1f
[   23.585176]  [<ffffffff81047727>] sys_setpgid+0x67/0x193
[   23.585176]  [<ffffffff810029eb>] system_call_fastpath+0x16/0x1b
[   24.959669] type=1400 audit(1282938522.956:4): avc:  denied  { module_request } for  pid=766 comm="hwclock" kmod="char-major-10-135" scontext=system_u:system_r:hwclock_t:s0 tcontext=system_u:system_r:kernel_t:s0 tclas

It turns out that the setpgid() system call fails to enter an RCU read-side critical section before doing a PID-to-task_struct translation. This commit therefore does rcu_read_lock() before the translation, and also does rcu_read_unlock() after the last use of the returned pointer.

kernel/sys.c +2 -0
index e9ad444..7f5a0cd 100644
@@ -931,6 +931,7 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
 		pgid = pid;
 	if (pgid < 0)
 		return -EINVAL;
+	rcu_read_lock();
 
 	/* From this point forward we keep holding onto the tasklist lock
 	 * so that our parent does not change from under us. -DaveM
@@ -984,6 +985,7 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
 out:
 	/* All paths lead to here, thus we are safe. -DaveM */
 	write_unlock_irq(&tasklist_lock);
+	rcu_read_unlock();
 	return err;
 }
 
Acked-by
#8 lore raw
Quoted 7 lines · Paul E. McKenney

It turns out that the setpgid() system call fails to enter an RCU read-side critical section before doing a PID-to-task_struct translation. This commit therefore does rcu_read_lock() before the translation, and also does rcu_read_unlock() after the last use of the returned pointer.