Fix various __task_cred related invalid RCU assumptions
Subsystems (7)
cover Fix various __task_cred related invalid RCU assumptions
While auditing the read_lock(&tasklist_lock) sites for a possible conversion to rcu-read_lock() I stumbled over an unprotected user of __task_cred in kernel/sys.c
That caused me to audit all the __task_cred usage sites except in kernel/exit.c.
Most of the usage sites are correct, but some of them trip over invalid assumptions about the protection which is given by RCU.
- spinlocked/preempt_disabled regions are equivalent to rcu_read_lock():
That's wrong. RCU does not guarantee that.
It has been that way due to implementation details and it still is valid for CONFIG_TREE_PREEMPT_RCU=n, but there is no guarantee that this will be the case forever.
- interrupt disabled regions are equivalent to rcu_read_lock():
Wrong again. RCU does not guarantee that.
It's true for current mainline, but again this is an implementation detail and there is no guarantee by the RCU semantics.
Indeed we want to get rid of that to avoid scalability issues on large systems and preempt-rt got already rid of it to a certain extent.
I'm sure we are lucky that CONFIG_TREE_PREEMPT_RCU=y is not yet wide spread and the code pathes are esoteric enough not to trigger that subtle races (some of them might just error out silently).
Nevertheless we need to fix all invalid assumptions about RCU protection.
The following patch series fixes all yet known affected __task_cred() sites, but there is more auditing of all other rcu users necessary.
Thanks,
tglx
PATCH 1/9 sys: Fix missing rcu protection for __task_cred() access
commit c69e8d9 (CRED: Use RCU to access another task's creds and to release a task's own creds) added non rcu_read_lock() protected access to task creds of the target task in set_prio_one().
The comment above the function says:
* - the caller must hold the RCU read lock
The calling code in sys_setpriority does read_lock(&tasklist_lock) but not rcu_read_lock(). This works only when CONFIG_TREE_PREEMPT_RCU=n. With CONFIG_TREE_PREEMPT_RCU=y the rcu_callbacks can run in the tick interrupt when they see no read side critical section.
There is another instance of __task_cred() in sys_setpriority() itself which is equally unprotected.
Wrap the whole code section into a rcu read side critical section to fix this quick and dirty.
Will be revisited in course of the read_lock(&tasklist_lock) -> rcu crusade.
--- kernel/sys.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/sys.c +2 -0
@@ -163,6 +163,7 @@ SYSCALL_DEFINE3(setpriority, int, which,if (niceval >19 ) niceval =19 ; +rcu_read_lock ();read_lock (&tasklist_lock);switch (which) {case PRIO_PROCESS :
@@ -200,6 +201,7 @@ SYSCALL_DEFINE3(setpriority, int, which, } out_unlock:read_unlock (&tasklist_lock); +rcu_read_unlock (); out:return error; }
Re: [patch 1/9] sys: Fix missing rcu protection for __task_cred() access
Quoted 11 lines · Thomas Gleixner
commit c69e8d9 (CRED: Use RCU to access another task's creds and to release a task's own creds) added non
rcu_read_lock()protected access to task creds of the target task inset_prio_one().The comment above the function says:
* - the caller must hold the RCU read lockThe calling code in
sys_setprioritydoesread_lock(&tasklist_lock)but notrcu_read_lock(). This works only whenCONFIG_TREE_PREEMPT_RCU=n. WithCONFIG_TREE_PREEMPT_RCU=ythercu_callbackscan run in the tick interrupt when they see no read side critical section.
And this is called out in Documentation/RCU/checklist.txt, item #2:
2. Do the RCU read-side critical sections make proper use of
rcu_read_lock() and friends? These primitives are needed to prevent grace periods from ending prematurely, which could result in data being unceremoniously freed out from under your read-side code, which can greatly increase the actuarial risk of your kernel.
As a rough rule of thumb, any dereference of an RCU-protected pointer must be covered by rcu_read_lock() or rcu_read_lock_bh() or by the appropriate update-side lock.
Quoted 8 lines
There is another instance of
__task_cred()insys_setpriority()itself which is equally unprotected.Wrap the whole code section into a rcu read side critical section to fix this quick and dirty.
Will be revisited in course of the
read_lock(&tasklist_lock)-> rcu crusade.
Good catch!
Quoted 30 lines
Signed-off-by Thomas Gleixner <[email protected]>Cc David Howells <[email protected]>Cc James Morris <[email protected]>--- kernel/sys.c | 2 ++ 1 file changed, 2 insertions(+)Index:
linux-2.6-tip/kernel/sys.c
===================================================================kernel/sys.c +2
-0@@ -163,6 +163,7 @@ SYSCALL_DEFINE3(setpriority, int, which,if (niceval >19 ) niceval =19 ; +rcu_read_lock ();read_lock (&tasklist_lock);switch (which) {case PRIO_PROCESS :@@ -200,6 +201,7 @@ SYSCALL_DEFINE3(setpriority, int, which, } out_unlock:read_unlock (&tasklist_lock); +rcu_read_unlock (); out:return error; }
Re: [patch 1/9] sys: Fix missing rcu protection for __task_cred() access
Quoted 3 lines
As a rough rule of thumb, any dereference of an RCU-protected pointer must be covered by
rcu_read_lock()orrcu_read_lock_bh()or by the appropriate update-side lock.
Does this mean that we need both rcu_read_lock() *and* read_lock(&tasklist_lock) when calling find_task_by_vpid() because pid_task() uses rcu_dereference() and find_pid_ns() uses hlist_for_each_entry_rcu ?
378 /*
379 * Must be called under rcu_read_lock() or with tasklist_lock read-held. 380 */
381 struct task_struct *find_task_by_pid_ns(pid_t nr, struct pid_namespace *ns) 382 {
383 return pid_task(find_pid_ns(nr, ns), PIDTYPE_PID);
384 }
385
386 struct task_struct *find_task_by_vpid(pid_t vnr)
387 {
388 return find_task_by_pid_ns(vnr, current->nsproxy->pid_ns);
389 }
Re: [patch 1/9] sys: Fix missing rcu protection for __task_cred() access
In reply to Thomas GleixnerQuoted 12 lines · Thomas Gleixner
commit c69e8d9 (CRED: Use RCU to access another task's creds and to release a task's own creds) added non
rcu_read_lock()protected access to task creds of the target task inset_prio_one().The comment above the function says:
* - the caller must hold the RCU read lockThe calling code in
sys_setprioritydoesread_lock(&tasklist_lock)but notrcu_read_lock(). This works only whenCONFIG_TREE_PREEMPT_RCU=n. WithCONFIG_TREE_PREEMPT_RCU=ythercu_callbackscan run in the tick interrupt when they see no read side critical section.
Quoted 8 lines
There is another instance of
__task_cred()insys_setpriority()itself which is equally unprotected.Wrap the whole code section into a rcu read side critical section to fix this quick and dirty.
Will be revisited in course of the
read_lock(&tasklist_lock)-> rcu crusade.
OK, I will bite... Don't the corresponding updates write-hold tasklist_lock? If so, then the fact that the code below is read-holding tasklist_lock would prevent any of the data from changing, which would remove the need to do the rcu_read_lock().
Or are there updates that are carried out without write-holding tasklist_lock that I am missing?
Thanx, Paul
Quoted 30 lines
Signed-off-by Thomas Gleixner <[email protected]>Cc David Howells <[email protected]>Cc James Morris <[email protected]>--- kernel/sys.c | 2 ++ 1 file changed, 2 insertions(+)Index:
linux-2.6-tip/kernel/sys.c
===================================================================kernel/sys.c +2
-0@@ -163,6 +163,7 @@ SYSCALL_DEFINE3(setpriority, int, which,if (niceval >19 ) niceval =19 ; +rcu_read_lock ();read_lock (&tasklist_lock);switch (which) {case PRIO_PROCESS :@@ -200,6 +201,7 @@ SYSCALL_DEFINE3(setpriority, int, which, } out_unlock:read_unlock (&tasklist_lock); +rcu_read_unlock (); out:return error; }
Re: [patch 1/9] sys: Fix missing rcu protection for __task_cred() access
Quoted 23 lines · Paul E. McKenney
Quoted 12 lines · Thomas Gleixner
commit c69e8d9 (CRED: Use RCU to access another task's creds and to release a task's own creds) added non
rcu_read_lock()protected access to task creds of the target task inset_prio_one().The comment above the function says:
* - the caller must hold the RCU read lockThe calling code in
sys_setprioritydoesread_lock(&tasklist_lock)but notrcu_read_lock(). This works only whenCONFIG_TREE_PREEMPT_RCU=n. WithCONFIG_TREE_PREEMPT_RCU=ythercu_callbackscan run in the tick interrupt when they see no read side critical section.Quoted 8 lines
There is another instance of
__task_cred()insys_setpriority()itself which is equally unprotected.Wrap the whole code section into a rcu read side critical section to fix this quick and dirty.
Will be revisited in course of the
read_lock(&tasklist_lock)-> rcu crusade.
Quoted 7 lines
OK, I will bite... Don't the corresponding updates write-hold
tasklist_lock? If so, then the fact that the code below is read-holdingtasklist_lockwould prevent any of the data from changing, which would remove the need to do thercu_read_lock().Or are there updates that are carried out without write-holding
tasklist_lockthat I am missing?
Yes, commit_creds() is called lockless.
Or I misunderstood the question/patch...
Oleg.
Re: [patch 1/9] sys: Fix missing rcu protection for __task_cred() access
Quoted 29 lines · Oleg Nesterov
Quoted 28 lines · Paul E. McKenney
Quoted 12 lines · Thomas Gleixner
commit c69e8d9 (CRED: Use RCU to access another task's creds and to release a task's own creds) added non
rcu_read_lock()protected access to task creds of the target task inset_prio_one().The comment above the function says:
* - the caller must hold the RCU read lockThe calling code in
sys_setprioritydoesread_lock(&tasklist_lock)but notrcu_read_lock(). This works only whenCONFIG_TREE_PREEMPT_RCU=n. WithCONFIG_TREE_PREEMPT_RCU=ythercu_callbackscan run in the tick interrupt when they see no read side critical section.Quoted 8 lines
There is another instance of
__task_cred()insys_setpriority()itself which is equally unprotected.Wrap the whole code section into a rcu read side critical section to fix this quick and dirty.
Will be revisited in course of the
read_lock(&tasklist_lock)-> rcu crusade.OK, I will bite... Don't the corresponding updates write-hold
tasklist_lock? If so, then the fact that the code below is read-holdingtasklist_lockwould prevent any of the data from changing, which would remove the need to do thercu_read_lock().
Quoted 4 lines
Quoted 2 lines
Or are there updates that are carried out without write-holding
tasklist_lockthat I am missing?Yes,
commit_creds()is called lockless.
Right, and that's what the problem is. commit_creds(), which rcu frees the old creds, does not take tasklist lock write lock.
Thanks,
tglx
David Howells
maintainerRe: [patch 1/9] sys: Fix missing rcu protection for __task_cred() access
Quoted 7 lines · Thomas Gleixner
Quoted 4 lines
Quoted 2 lines
Or are there updates that are carried out without write-holding
tasklist_lockthat I am missing?Yes,
commit_creds()is called lockless.Right, and that's what the problem is.
commit_creds(), which rcu frees the old creds, does not take tasklist lock write lock.
commit_creds() does not need to hold a write lock, because it is implicitly write-locked by only being permitted to run in the thread to which it is committing.
I don't think commit_creds() needs to take the RCU read lock as no-one else can alter/delete the creds it is dealing with.
David
Re: [patch 1/9] sys: Fix missing rcu protection for __task_cred() access
Quoted 10 lines · David Howells
Quoted 7 lines · Thomas Gleixner
Quoted 4 lines
Quoted 2 lines
Or are there updates that are carried out without write-holding
tasklist_lockthat I am missing?Yes,
commit_creds()is called lockless.Right, and that's what the problem is.
commit_creds(), which rcu frees the old creds, does not take tasklist lock write lock.
Quoted 6 lines
commit_creds()does not need to hold a write lock, because it is implicitly write-locked by only being permitted to run in the thread to which it is committing.I don't think
commit_creds()needs to take the RCU read lock as no-one else can alter/delete the creds it is dealing with.
commit_cred() is not required to take anything, but the reader side needs to take rcu_read_lock() when accessing __task_cred() of another task as that task could update its own creds right at that point.
The point is that read_lock(tasklist_lock) is not sufficient for the reader side.
Thanks,
tglx
Re: [patch 1/9] sys: Fix missing rcu protection for __task_cred() access
In reply to Thomas GleixnerQuoted 16 lines · Thomas Gleixner
commit c69e8d9 (CRED: Use RCU to access another task's creds and to release a task's own creds) added non
rcu_read_lock()protected access to task creds of the target task inset_prio_one().The comment above the function says:
* - the caller must hold the RCU read lockThe calling code in
sys_setprioritydoesread_lock(&tasklist_lock)but notrcu_read_lock(). This works only whenCONFIG_TREE_PREEMPT_RCU=n. WithCONFIG_TREE_PREEMPT_RCU=ythercu_callbackscan run in the tick interrupt when they see no read side critical section.
...
kernel/sys.c SYSCALL_DEFINE3(setpriority, int, which, · line 167
Quoted 5 lines
kernel/sys.c +1
-0@@ -163,6 +163,7 @@ SYSCALL_DEFINE3(setpriority, int, which,if (niceval >19 ) niceval =19 ; +rcu_read_lock ();read_lock (&tasklist_lock);
Off-topic, but can't resist...
This also fixes another bug here. find_task_by_vpid() is not safe without rcu_read_lock(). I do not mean it is not safe to use the result, just find_pid_ns() by itself is not safe.
Usually tasklist gives enough protection, but if copy_process() fails it calls free_pid() lockless and does call_rcu(delayed_put_pid(). This means, without rcu lock find_pid_ns() can't scan the hash table safely.
Oleg.
Re: [patch 1/9] sys: Fix missing rcu protection for __task_cred() access
Quoted 25 lines · Oleg Nesterov
Quoted 16 lines · Thomas Gleixner
commit c69e8d9 (CRED: Use RCU to access another task's creds and to release a task's own creds) added non
rcu_read_lock()protected access to task creds of the target task inset_prio_one().The comment above the function says:
* - the caller must hold the RCU read lockThe calling code in
sys_setprioritydoesread_lock(&tasklist_lock)but notrcu_read_lock(). This works only whenCONFIG_TREE_PREEMPT_RCU=n. WithCONFIG_TREE_PREEMPT_RCU=ythercu_callbackscan run in the tick interrupt when they see no read side critical section.
...kernel/sys.c SYSCALL_DEFINE3(setpriority, int, which, · line 167
Quoted 5 lines
kernel/sys.c +1
-0@@ -163,6 +163,7 @@ SYSCALL_DEFINE3(setpriority, int, which,if (niceval >19 ) niceval =19 ; +rcu_read_lock ();read_lock (&tasklist_lock);Off-topic, but can't resist...
Quoted 8 lines
This also fixes another bug here.
find_task_by_vpid()is not safe withoutrcu_read_lock(). I do not mean it is not safe to use the result, justfind_pid_ns()by itself is not safe.Usually tasklist gives enough protection, but if
copy_process()fails it callsfree_pid()lockless and does call_rcu(delayed_put_pid(). This means, without rcu lockfind_pid_ns()can't scan the hash table safely.
I guess we have whole bunch of auditing to do all over the place. And we definitely need some debugging aid (preferrably a lockdep extension) which allows us to detect such nasty details.
Thanks,
tglx
Re: [patch 1/9] sys: Fix missing rcu protection for __task_cred()access
In reply to Oleg NesterovQuoted 25 lines · Oleg Nesterov
Quoted 16 lines · Thomas Gleixner
commit c69e8d9 (CRED: Use RCU to access another task's creds and to release a task's own creds) added non
rcu_read_lock()protected access to task creds of the target task inset_prio_one().The comment above the function says:
* - the caller must hold the RCU read lockThe calling code in
sys_setprioritydoesread_lock(&tasklist_lock)but notrcu_read_lock(). This works only whenCONFIG_TREE_PREEMPT_RCU=n. WithCONFIG_TREE_PREEMPT_RCU=ythercu_callbackscan run in the tick interrupt when they see no read side critical section.
...kernel/sys.c SYSCALL_DEFINE3(setpriority, int, which, · line 167
Quoted 5 lines
kernel/sys.c +1
-0@@ -163,6 +163,7 @@ SYSCALL_DEFINE3(setpriority, int, which,if (niceval >19 ) niceval =19 ; +rcu_read_lock ();read_lock (&tasklist_lock);Off-topic, but can't resist...
Quoted 8 lines
This also fixes another bug here.
find_task_by_vpid()is not safe withoutrcu_read_lock(). I do not mean it is not safe to use the result, justfind_pid_ns()by itself is not safe.Usually tasklist gives enough protection, but if
copy_process()fails it callsfree_pid()lockless and does call_rcu(delayed_put_pid(). This means, without rcu lockfind_pid_ns()can't scan the hash table safely.
So, we need to change below comment from "or" to "and" ?
378 /*
379 * Must be called under rcu_read_lock() or with tasklist_lock read-held. 380 */
381 struct task_struct *find_task_by_pid_ns(pid_t nr, struct pid_namespace *ns) 382 {
383 return pid_task(find_pid_ns(nr, ns), PIDTYPE_PID);
384 }
385
386 struct task_struct *find_task_by_vpid(pid_t vnr)
387 {
388 return find_task_by_pid_ns(vnr, current->nsproxy->pid_ns);
389 }
Re: [patch 1/9] sys: Fix missing rcu protection for __task_cred()access
Quoted 6 lines · Tetsuo Handa
Quoted 4 lines
Usually tasklist gives enough protection, but if
copy_process()fails it callsfree_pid()lockless and does call_rcu(delayed_put_pid(). This means, without rcu lockfind_pid_ns()can't scan the hash table safely.So, we need to change below comment from "or" to "and" ?
No, both functions must be called with rcu_read_lock()
tasklist_lock read-held is not protecting the rcu lists and does not protect against a concurrent update. It merily protects against tasks going away or being added while we look up the lists.
Quoted 4 lines
378 /*
379 * Must be called underrcu_read_lock()or withtasklist_lockread-held. 380 */
381 structtask_struct*find_task_by_pid_ns(pid_t nr, structpid_namespace*ns)
Quoted 9 lines
382 {
383 returnpid_task(find_pid_ns(nr, ns), PIDTYPE_PID);
384 }
385
386 structtask_struct*find_task_by_vpid(pid_t vnr) 387 {
388 returnfind_task_by_pid_ns(vnr, current->nsproxy->pid_ns);
389 }
Thanks,
tglx
Re: [patch 1/9] sys: Fix missing rcu protection for __task_cred()access
Quoted 6 lines · Thomas Gleixner
Quoted 5 lines · Tetsuo Handa
Quoted 4 lines
Usually tasklist gives enough protection, but if
copy_process()fails it callsfree_pid()lockless and does call_rcu(delayed_put_pid(). This means, without rcu lockfind_pid_ns()can't scan the hash table safely.
Quoted 7 lines
Quoted 1 line
So, we need to change below comment from "or" to "and" ?
No, both functions must be called with
rcu_read_lock()
tasklist_lockread-held is not protecting the rcu lists and does not protect against a concurrent update. It merily protects against tasks going away or being added while we look up the lists.
So, rcu_read_lock() is mandatory.
But I couldn't understand why tasklist_lock being held is not mandatory.
Caller of these functions will use "struct task_struct" and expect that values and pointers retrieved by dereferencing returned pointer are valid.
If "struct task_struct" was removed from the list due to missing tasklist_lock between returning from find_task_by_pid_ns() and dereferencing, I worry that values and pointers retrieved by dereferencing are invalid.
rcu_read_lock() being held will prevent the returned "struct task_struct" from being kfree()d, but I think rcu_read_lock() being held does not prevent values and pointers of "struct task_struct" from being invalidated.
Anyway, I browsed 2.6.32 for find_task_by_vpid() / find_task_by_pid_ns() users and found below users which lacks read_lock(&tasklist_lock) or rcu_read_lock().
Users missing read_lock(&tasklist_lock) when calling find_task_by_vpid():
get_net_ns_by_pid() in net/core/net_namespace.c seq_print_userip_objs() in kernel/trace/trace_output.c fill_pid() in kernel/taskstats.c
fill_tgid() in kernel/taskstats.c futex_find_get_task() in kernel/futex.c SYSCALL_DEFINE3(get_robust_list) in kernel/futex.c compat_sys_get_robust_list() in kernel/futex_compat.c ptrace_get_task_struct() in kernel/ptrace.c do_send_specific() in kernel/signal.c find_get_context() in kernel/perf_event.c posix_cpu_clock_get() in kernel/posix-cpu-timers.c good_sigevent() in kernel/posix-timers.c attach_task_by_pid() in kernel/cgroup.c SYSCALL_DEFINE1(getpgid) in kernel/sys.c SYSCALL_DEFINE1(getsid) in kernel/sys.c do_sched_setscheduler() in kernel/sched.c
Users missing rcu_read_lock() when calling find_task_by_vpid():
SYSCALL_DEFINE3(ioprio_set) in fs/ioprio.c SYSCALL_DEFINE2(ioprio_get) in fs/ioprio.c cap_get_target_pid() in kernel/capability.c audit_prepare_user_tty() in kernel/audit.c audit_receive_msg() in kernel/audit.c check_clock() in kernel/posix-cpu-timers.c posix_cpu_timer_create() in kernel/posix-cpu-timers.c SYSCALL_DEFINE3(setpriority) in kernel/sys.c SYSCALL_DEFINE2(getpriority) in kernel/sys.c SYSCALL_DEFINE2(setpgid) in kernel/sys.c SYSCALL_DEFINE1(sched_getscheduler) in kernel/sched.c SYSCALL_DEFINE2(sched_getparam) in kernel/sched.c sched_setaffinity() in kernel/sched.c sched_getaffinity() in kernel/sched.c SYSCALL_DEFINE2(sched_rr_get_interval) in kernel/sched.c tomoyo_is_select_one() in security/tomoyo/common.c tomoyo_read_pid() in security/tomoyo/common.c SYSCALL_DEFINE6(move_pages) in mm/migrate.c SYSCALL_DEFINE4(migrate_pages) in mm/mempolicy.c find_process_by_pid() in arch/mips/kernel/mips-mt-fpaff.c pfm_get_task() in arch/ia64/kernel/perfmon.c cxn_pin_by_pid() in arch/frv/mm/mmu-context.c
Users missing read_lock(&tasklist_lock) when calling find_task_by_pid_ns():
rest_init() in init/main.c proc_pid_lookup() in fs/proc/base.c proc_task_lookup() in fs/proc/base.c first_tid() in fs/proc/base.c
getthread() in kernel/kgdb.c mconsole_stack() in arch/um/drivers/mconsole_kern.c
Users missing rcu_read_lock() when calling find_task_by_pid_ns():
rest_init() in init/main.c
getthread() in kernel/kgdb.c mconsole_stack() in arch/um/drivers/mconsole_kern.c
Regarding users which lack rcu_read_lock(), users call read_lock(&tasklist_lock) in order to access "struct task_struct" returned by find_task_by_pid_ns() but they do not want to be bothered by internal pid structure. Thus, the fix would be to add rcu_read_lock() and rcu_read_unlock() inside find_task_by_pid_ns().
But how to check users which lack read_lock(&tasklist_lock) but expecting that it is safe to access "struct task_struct" returned by find_task_by_pid_ns() ?
PATCH 1/1 Update comment on find_task_by_pid_ns
In reply to Thomas GleixnerQuoted 28 lines · Thomas Gleixner
Quoted 6 lines · Tetsuo Handa
Quoted 4 lines
Usually tasklist gives enough protection, but if
copy_process()fails it callsfree_pid()lockless and does call_rcu(delayed_put_pid(). This means, without rcu lockfind_pid_ns()can't scan the hash table safely.So, we need to change below comment from "or" to "and" ?
No, both functions must be called with
rcu_read_lock()
tasklist_lockread-held is not protecting the rcu lists and does not protect against a concurrent update. It merily protects against tasks going away or being added while we look up the lists.Quoted 4 lines
378 /*
379 * Must be called underrcu_read_lock()or withtasklist_lockread-held. 380 */
381 structtask_struct*find_task_by_pid_ns(pid_t nr, structpid_namespace*ns)Quoted 9 lines
382 {
383 returnpid_task(find_pid_ns(nr, ns), PIDTYPE_PID);
384 }
385
386 structtask_struct*find_task_by_vpid(pid_t vnr) 387 {
388 returnfind_task_by_pid_ns(vnr, current->nsproxy->pid_ns);
389 }
Quoted 3 lines
Thanks,
tglx
So, we need to update the comment on these functions?
----------------------------------------
[PATCH] Update comment on find_task_by_pid_ns
Caller of find_task_by_vpid() and find_task_by_pid_ns() needs to call rcu_read_lock() rather than read_lock(&tasklist_lock) because find_pid_ns() uses RCU primitives but spinlock does not prevent RCU callback if preemptive RCU ( CONFIG_TREE_PREEMPT_RCU ) is enabled.
--- kernel/pid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/pid.c +1 -1
@@ -376,7 +376,7 @@ struct task_struct *pid_task(struct pidEXPORT_SYMBOL (pid_task);/* -* Must be called under rcu_read_lock() or with tasklist_lock read-held. +* Must be called under rcu_read_lock(). */ struct task_struct *find_task_by_pid_ns (pid_t nr,struct pid_namespace *ns) {
Re: [PATCH] Update comment on find_task_by_pid_ns
Quoted 7 lines · Tetsuo Handa
[PATCH] Update comment on
find_task_by_pid_nsCaller of
find_task_by_vpid()andfind_task_by_pid_ns()needs to callrcu_read_lock()rather thanread_lock(&tasklist_lock)becausefind_pid_ns()uses RCU primitives but spinlock does not prevent RCU callback if preemptive RCU (CONFIG_TREE_PREEMPT_RCU) is enabled.
I agree with the patch, but the changelog looks a bit confusing to me. Perhaps this is just me, though.
tasklist does protect the task and its pid, it can't go away. The problem is that find_pid_ns() itself is unsafe without rcu lock, it can race with copy_process()->free_pid(any_pid).
IOW, if we change copy_process()
--- kernel/fork.c
+++ kernel/fork.c
@@ -1304,8 +1304,11 @@ static struct task_struct *copy_process(
return p;
bad_fork_free_pid:
- if (pid != &init_struct_pid)
+ if (pid != &init_struct_pid) {
+ read_lock(&tasklist_lock);
free_pid(pid);
+ read_unlock(&tasklist_lock);
+ }
bad_fork_cleanup_io:
if (p->io_context)
exit_io_context(p);then find_task_by_pid_ns/etc could be used under tasklist safely even with PREEMPT_RCU.
In any case, I think the patch is nice.
Oleg.
Re: [PATCH] Update comment on find_task_by_pid_ns
Quoted 34 lines · Oleg Nesterov
Quoted 7 lines · Tetsuo Handa
[PATCH] Update comment on
find_task_by_pid_nsCaller of
find_task_by_vpid()andfind_task_by_pid_ns()needs to callrcu_read_lock()rather thanread_lock(&tasklist_lock)becausefind_pid_ns()uses RCU primitives but spinlock does not prevent RCU callback if preemptive RCU (CONFIG_TREE_PREEMPT_RCU) is enabled.I agree with the patch, but the changelog looks a bit confusing to me. Perhaps this is just me, though.
tasklist does protect the task and its pid, it can't go away. The problem is that
find_pid_ns()itself is unsafe without rcu lock, it can race with copy_process()->free_pid(any_pid).IOW, if we change
copy_process()--- kernel/fork.c +++ kernel/fork.c @@ -1304,8 +1304,11 @@ static struct task_struct *copy_process( return p; bad_fork_free_pid: - if (pid != &init_struct_pid) + if (pid != &init_struct_pid) { + read_lock(&tasklist_lock); free_pid(pid); + read_unlock(&tasklist_lock); + } bad_fork_cleanup_io: if (p->io_context) exit_io_context(p);
Quoted 2 lines
then find_task_by_pid_ns/etc could be used under tasklist safely even with
PREEMPT_RCU.
We try to get rid of the read_lock sites of tasklist_lock, so please let's not think about adding more :)
Thanks,
tglx
Re: [PATCH] Update comment on find_task_by_pid_ns
Quoted 21 lines · Thomas Gleixner
Quoted 18 lines · Oleg Nesterov
IOW, if we change
copy_process()--- kernel/fork.c +++ kernel/fork.c @@ -1304,8 +1304,11 @@ static struct task_struct *copy_process( return p; bad_fork_free_pid: - if (pid != &init_struct_pid) + if (pid != &init_struct_pid) { + read_lock(&tasklist_lock); free_pid(pid); + read_unlock(&tasklist_lock); + } bad_fork_cleanup_io: if (p->io_context) exit_io_context(p);
Quoted 5 lines
Quoted 2 lines
then find_task_by_pid_ns/etc could be used under tasklist safely even with
PREEMPT_RCU.We try to get rid of the
read_locksites oftasklist_lock, so please let's not think about adding more :)
Yes, yes, I agree. I didn't mean this patch makes sense.
Oleg.
Re: [PATCH] Update comment on find_task_by_pid_ns
OK. I updated description.
As of 2.6.32 , below users are missing rcu_read_lock().
Users missing rcu_read_lock() when calling find_task_by_vpid():
SYSCALL_DEFINE3(ioprio_set) in fs/ioprio.c SYSCALL_DEFINE2(ioprio_get) in fs/ioprio.c cap_get_target_pid() in kernel/capability.c audit_prepare_user_tty() in kernel/audit.c audit_receive_msg() in kernel/audit.c check_clock() in kernel/posix-cpu-timers.c posix_cpu_timer_create() in kernel/posix-cpu-timers.c SYSCALL_DEFINE3(setpriority) in kernel/sys.c SYSCALL_DEFINE2(getpriority) in kernel/sys.c SYSCALL_DEFINE2(setpgid) in kernel/sys.c SYSCALL_DEFINE1(sched_getscheduler) in kernel/sched.c SYSCALL_DEFINE2(sched_getparam) in kernel/sched.c sched_setaffinity() in kernel/sched.c sched_getaffinity() in kernel/sched.c SYSCALL_DEFINE2(sched_rr_get_interval) in kernel/sched.c tomoyo_is_select_one() in security/tomoyo/common.c tomoyo_read_pid() in security/tomoyo/common.c SYSCALL_DEFINE6(move_pages) in mm/migrate.c SYSCALL_DEFINE4(migrate_pages) in mm/mempolicy.c find_process_by_pid() in arch/mips/kernel/mips-mt-fpaff.c pfm_get_task() in arch/ia64/kernel/perfmon.c cxn_pin_by_pid() in arch/frv/mm/mmu-context.c
Users missing rcu_read_lock() when calling find_task_by_pid_ns():
rest_init() in init/main.c
getthread() in kernel/kgdb.c mconsole_stack() in arch/um/drivers/mconsole_kern.c
What should we do? Adding rcu_read_lock()/rcu_read_unlock() to each callers? Or adding rcu_read_lock()/rcu_read_unlock() inside find_task_by_pid_ns()?
--------------------
[PATCH] Update comment on find_task_by_pid_ns
tasklist does protect the task and its pid, it can't go away. The problem is that find_pid_ns() itself is unsafe without rcu lock, it can race with copy_process()->free_pid(any_pid).
Protecting copy_process()->free_pid(any_pid) with tasklist_lock would make it possible to call find_task_by_pid_ns() under tasklist safely, but we don't do so because we are trying to get rid of the read_lock sites of tasklist_lock.
--- kernel/pid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/pid.c +1 -1
@@ -376,7 +376,7 @@ struct task_struct *pid_task(struct pidEXPORT_SYMBOL (pid_task);/* -* Must be called under rcu_read_lock() or with tasklist_lock read-held. +* Must be called under rcu_read_lock(). */ struct task_struct *find_task_by_pid_ns (pid_t nr,struct pid_namespace *ns) {
Andrew Morton
maintainerRe: [PATCH] Update comment on find_task_by_pid_ns
Quoted 9 lines · On Tue, 9 Feb 2010 06:42:45 +0900 Tetsuo Handa…
OK. I updated description.
As of 2.6.32 , below users are missing
rcu_read_lock().Users missing
rcu_read_lock()when callingfind_task_by_vpid():
SYSCALL_DEFINE3(ioprio_set)infs/ioprio.c
SYSCALL_DEFINE2(ioprio_get)infs/ioprio.c
cap_get_target_pid()inkernel/capability.c
Actually, cap_get_target_pid() uses rcu_read_lock() and doesn't take tasklist_lock.
Quoted 22 lines
audit_prepare_user_tty()inkernel/audit.caudit_receive_msg()inkernel/audit.ccheck_clock()inkernel/posix-cpu-timers.cposix_cpu_timer_create()inkernel/posix-cpu-timers.cSYSCALL_DEFINE3(setpriority)inkernel/sys.cSYSCALL_DEFINE2(getpriority)inkernel/sys.cSYSCALL_DEFINE2(setpgid)inkernel/sys.cSYSCALL_DEFINE1(sched_getscheduler)inkernel/sched.cSYSCALL_DEFINE2(sched_getparam)inkernel/sched.csched_setaffinity()inkernel/sched.csched_getaffinity()inkernel/sched.cSYSCALL_DEFINE2(sched_rr_get_interval)inkernel/sched.ctomoyo_is_select_one()insecurity/tomoyo/common.ctomoyo_read_pid()insecurity/tomoyo/common.cSYSCALL_DEFINE6(move_pages)inmm/migrate.cSYSCALL_DEFINE4(migrate_pages)inmm/mempolicy.cfind_process_by_pid()inarch/mips/kernel/mips-mt-fpaff.cpfm_get_task()inarch/ia64/kernel/perfmon.ccxn_pin_by_pid()inarch/frv/mm/mmu-context.cUsers missing
rcu_read_lock()when callingfind_task_by_pid_ns():
Quoted 7 lines
rest_init()ininit/main.c
getthread()inkernel/kgdb.cmconsole_stack()inarch/um/drivers/mconsole_kern.cWhat should we do? Adding rcu_read_lock()/rcu_read_unlock() to each callers? Or adding rcu_read_lock()/rcu_read_unlock() inside
find_task_by_pid_ns()?
Putting rcu_read_lock() in the callee isn't a complete solution. Because the function would still be returning a task_struct* without any locking held and without taking a reference against it. So that pointer is useless to the caller!
We could add a new function which looks up the task and then takes a reference on it, insde suitable locks. The caller would then use the task_struct and then remember to call put_task_struct() to unpin it. This prevents the task_struct from getting freed while it's being manipulated, but it doesn't prevent fields within it from being altered
- that's up to the caller to sort out.
One fix is to go through all those callsites and add the rcu_read_lock. That kinda sucks. Perhaps writing the new function which returns a pinned task_struct is better?
Re: [PATCH] Update comment on find_task_by_pid_ns
Quoted 7 lines · Andrew Morton
Quoted 4 lines · On Tue, 9 Feb 2010 06:42:45 +0900 Tetsuo Handa…
OK. I updated description.
As of 2.6.32 , below users are missing
rcu_read_lock().
Quoted 8 lines
Quoted 5 lines
Users missing
rcu_read_lock()when callingfind_task_by_vpid():
SYSCALL_DEFINE3(ioprio_set)infs/ioprio.c
SYSCALL_DEFINE2(ioprio_get)infs/ioprio.c
cap_get_target_pid()inkernel/capability.cActually,
cap_get_target_pid()usesrcu_read_lock()and doesn't taketasklist_lock.
Hmm - is that in -mm? In my copy here it takes read_lock(&tasklist_lock)
And I'll admit I'm a bit confused as to the current state of things:
do I understand correctly that we now need to take both the tasklist_lock and rcu_read_lock? (Presumably only for read_lock()?)
Quoted 50 lines
Quoted 22 lines
audit_prepare_user_tty()inkernel/audit.caudit_receive_msg()inkernel/audit.ccheck_clock()inkernel/posix-cpu-timers.cposix_cpu_timer_create()inkernel/posix-cpu-timers.cSYSCALL_DEFINE3(setpriority)inkernel/sys.cSYSCALL_DEFINE2(getpriority)inkernel/sys.cSYSCALL_DEFINE2(setpgid)inkernel/sys.cSYSCALL_DEFINE1(sched_getscheduler)inkernel/sched.cSYSCALL_DEFINE2(sched_getparam)inkernel/sched.csched_setaffinity()inkernel/sched.csched_getaffinity()inkernel/sched.cSYSCALL_DEFINE2(sched_rr_get_interval)inkernel/sched.ctomoyo_is_select_one()insecurity/tomoyo/common.ctomoyo_read_pid()insecurity/tomoyo/common.cSYSCALL_DEFINE6(move_pages)inmm/migrate.cSYSCALL_DEFINE4(migrate_pages)inmm/mempolicy.cfind_process_by_pid()inarch/mips/kernel/mips-mt-fpaff.cpfm_get_task()inarch/ia64/kernel/perfmon.ccxn_pin_by_pid()inarch/frv/mm/mmu-context.cUsers missing
rcu_read_lock()when callingfind_task_by_pid_ns():Quoted 7 lines
rest_init()ininit/main.c
getthread()inkernel/kgdb.cmconsole_stack()inarch/um/drivers/mconsole_kern.cWhat should we do? Adding rcu_read_lock()/rcu_read_unlock() to each callers? Or adding rcu_read_lock()/rcu_read_unlock() inside
find_task_by_pid_ns()?Putting
rcu_read_lock()in the callee isn't a complete solution. Because the function would still be returning a task_struct* without any locking held and without taking a reference against it. So that pointer is useless to the caller!We could add a new function which looks up the task and then takes a reference on it, insde suitable locks. The caller would then use the
task_structand then remember to callput_task_struct()to unpin it. This prevents thetask_structfrom getting freed while it's being manipulated, but it doesn't prevent fields within it from being altered
- that's up to the caller to sort out.One fix is to go through all those callsites and add the
rcu_read_lock. That kinda sucks. Perhaps writing the new function which returns a pinnedtask_structis better?--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Andrew Morton
maintainerRe: [PATCH] Update comment on find_task_by_pid_ns
Quoted 10 lines · On Wed, 10 Feb 2010 10:30:33 -0600 "Serge E. Hallyn" <[email protected]> wrote:
Quoted 9 lines · Andrew Morton
Quoted 6 lines · On Tue, 9 Feb 2010 06:42:45 +0900 Tetsuo Handa…
OK. I updated description.
As of 2.6.32 , below users are missing
rcu_read_lock().Users missing
rcu_read_lock()when callingfind_task_by_vpid():
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
SYSCALL_DEFINE3(ioprio_set)infs/ioprio.c
SYSCALL_DEFINE2(ioprio_get)infs/ioprio.c
cap_get_target_pid()inkernel/capability.cActually,
cap_get_target_pid()usesrcu_read_lock()and doesn't taketasklist_lock.Hmm - is that in -mm? In my copy here it takes
read_lock(&tasklist_lock)
yup. It got changed in linux-next.
Quoted 3 lines
And I'll admit I'm a bit confused as to the current state of things:
do I understand correctly that we now need to take both thetasklist_lockandrcu_read_lock? (Presumably only forread_lock()?)
Beats me. We need to protect both the pid->task_struct lookup data structures (during the lookup) and protect the resulting task_struct while the caller is playing with it. It's unclear whether rcu_read_lock() suffices for both purposes.
Re: [PATCH] Update comment on find_task_by_pid_ns
Quoted 23 lines · Andrew Morton
Quoted 10 lines · On Wed, 10 Feb 2010 10:30:33 -0600 "Serge E. Hallyn" <[email protected]> wrote:
Quoted 9 lines · Andrew Morton
Quoted 6 lines · On Tue, 9 Feb 2010 06:42:45 +0900 Tetsuo Handa…
OK. I updated description.
As of 2.6.32 , below users are missing
rcu_read_lock().Users missing
rcu_read_lock()when callingfind_task_by_vpid():Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
SYSCALL_DEFINE3(ioprio_set)infs/ioprio.c
SYSCALL_DEFINE2(ioprio_get)infs/ioprio.c
cap_get_target_pid()inkernel/capability.cActually,
cap_get_target_pid()usesrcu_read_lock()and doesn't taketasklist_lock.Hmm - is that in -mm? In my copy here it takes
read_lock(&tasklist_lock)yup. It got changed in linux-next.
Quoted 8 lines
Quoted 3 lines
And I'll admit I'm a bit confused as to the current state of things:
do I understand correctly that we now need to take both thetasklist_lockandrcu_read_lock? (Presumably only forread_lock()?)Beats me. We need to protect both the
pid->task_structlookup data structures (during the lookup) and protect the resultingtask_structwhile the caller is playing with it. It's unclear whetherrcu_read_lock()suffices for both purposes.
The rcu_read_lock section is sufficient. task_struct can not go away before the rcu_read_unlock()
Thanks,
tglx
Re: [PATCH] Update comment on find_task_by_pid_ns
Quoted 28 lines · Thomas Gleixner
Quoted 27 lines · Andrew Morton
Quoted 10 lines · On Wed, 10 Feb 2010 10:30:33 -0600 "Serge E. Hallyn" <[email protected]> wrote:
Quoted 9 lines · Andrew Morton
Quoted 6 lines · On Tue, 9 Feb 2010 06:42:45 +0900 Tetsuo Handa…
OK. I updated description.
As of 2.6.32 , below users are missing
rcu_read_lock().Users missing
rcu_read_lock()when callingfind_task_by_vpid():Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
SYSCALL_DEFINE3(ioprio_set)infs/ioprio.c
SYSCALL_DEFINE2(ioprio_get)infs/ioprio.c
cap_get_target_pid()inkernel/capability.cActually,
cap_get_target_pid()usesrcu_read_lock()and doesn't taketasklist_lock.Hmm - is that in -mm? In my copy here it takes
read_lock(&tasklist_lock)yup. It got changed in linux-next.
Quoted 3 lines
And I'll admit I'm a bit confused as to the current state of things:
do I understand correctly that we now need to take both thetasklist_lockandrcu_read_lock? (Presumably only forread_lock()?)
Quoted 7 lines
Quoted 4 lines
Beats me. We need to protect both the
pid->task_structlookup data structures (during the lookup) and protect the resultingtask_structwhile the caller is playing with it. It's unclear whetherrcu_read_lock()suffices for both purposes.The
rcu_read_locksection is sufficient.task_structcan not go away before thercu_read_unlock()
But, more generally, it used to be the case that a spinlock (or rwlock) would imply an rcu read cycle, but now it no longer does, do I understand that right?
thanks,
-serge
Re: [PATCH] Update comment on find_task_by_pid_ns
Quoted 34 lines · Serge E. Hallyn
Quoted 33 lines · Thomas Gleixner
Quoted 31 lines · Andrew Morton
Quoted 10 lines · On Wed, 10 Feb 2010 10:30:33 -0600 "Serge E. Hallyn" <[email protected]> wrote:
Quoted 9 lines · Andrew Morton
Quoted 6 lines · On Tue, 9 Feb 2010 06:42:45 +0900 Tetsuo Handa…
OK. I updated description.
As of 2.6.32 , below users are missing
rcu_read_lock().Users missing
rcu_read_lock()when callingfind_task_by_vpid():Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
SYSCALL_DEFINE3(ioprio_set)infs/ioprio.c
SYSCALL_DEFINE2(ioprio_get)infs/ioprio.c
cap_get_target_pid()inkernel/capability.cActually,
cap_get_target_pid()usesrcu_read_lock()and doesn't taketasklist_lock.Hmm - is that in -mm? In my copy here it takes
read_lock(&tasklist_lock)yup. It got changed in linux-next.
Quoted 3 lines
And I'll admit I'm a bit confused as to the current state of things:
do I understand correctly that we now need to take both thetasklist_lockandrcu_read_lock? (Presumably only forread_lock()?)Beats me. We need to protect both the
pid->task_structlookup data structures (during the lookup) and protect the resultingtask_structwhile the caller is playing with it. It's unclear whetherrcu_read_lock()suffices for both purposes.
Quoted 6 lines
Quoted 2 lines
The
rcu_read_locksection is sufficient.task_structcan not go away before thercu_read_unlock()But, more generally, it used to be the case that a spinlock (or rwlock) would imply an rcu read cycle, but now it no longer does, do I understand that right?
You are correct, with CONFIG_PREEMPT_RCU, acquiring a lock does -not- imply an RCU read-side critical section. And acquiring a lock does -not- imply any sort of RCU read-side critical section in -rt kernels in any case.
Thanx, Paul
Re: [PATCH] Update comment on find_task_by_pid_ns
In reply to Andrew MortonQuoted 9 lines · Andrew Morton
Quoted 3 lines
What should we do? Adding rcu_read_lock()/rcu_read_unlock() to each callers? Or adding rcu_read_lock()/rcu_read_unlock() inside
find_task_by_pid_ns()?Putting
rcu_read_lock()in the callee isn't a complete solution. Because the function would still be returning a task_struct* without any locking held and without taking a reference against it. So that pointer is useless to the caller!
Quoted 6 lines
We could add a new function which looks up the task and then takes a reference on it, insde suitable locks. The caller would then use the
task_structand then remember to callput_task_struct()to unpin it. This prevents thetask_structfrom getting freed while it's being manipulated, but it doesn't prevent fields within it from being altered
- that's up to the caller to sort out.
Code for "struct task_struct" is too complicated for me to understand, but my understanding is that
(1) tasklist_lock is acquired for writing.
(2) "struct task_struct" (to exit()) is removed from task's list.
(3) tasklist_lock is released.
(4) Wait for RCU grace period.
(5) kfree() members of "struct task_struct".
(6) kfree() "struct task_struct" itself.
If above sequence is correct, I think
rcu_read_lock(); task = find_task_by_pid_ns(); if (task) do_something(task); rcu_read_unlock();
do_something() can safely access all members of task without read_lock(&tasklist_lock), except task->prev (I don't know the exact member) and task->usage, because do_something() finishes its work before (5). I think we need to call find_task_by_pid_ns() with both read_lock(&tasklist_lock) and rcu_read_lock()
read_lock(&tasklist_lock); rcu_read_lock(); task = find_task_by_pid_ns(); if (task) atomido_something(task); rcu_read_unlock(); read_unlock(&tasklist_lock);
only when do_something() wants to access task->prev or task->usage .
Quoted 4 lines
One fix is to go through all those callsites and add the
rcu_read_lock. That kinda sucks. Perhaps writing the new function which returns a pinnedtask_structis better?
PATCH 1/1 sys: Fix missing rcu protection for sys_getpriority.
In reply to Oleg NesterovQuoted 8 lines · Oleg Nesterov
This also fixes another bug here.
find_task_by_vpid()is not safe withoutrcu_read_lock(). I do not mean it is not safe to use the result, justfind_pid_ns()by itself is not safe.Usually tasklist gives enough protection, but if
copy_process()fails it callsfree_pid()lockless and does call_rcu(delayed_put_pid(). This means, without rcu lockfind_pid_ns()can't scan the hash table safely.
This bug for sys_setpriority() was fixed, but not fixed for sys_getpriority(). Why not to add it as well?
--------------------
[PATCH] sys: Fix missing rcu protection for sys_setpriority.
find_task_by_vpid() is not safe without rcu_read_lock(). 2.6.33-rc7 got RCU protection for sys_setpriority() but missed it for sys_getpriority().
--- kernel/sys.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/sys.c +2 -0
@@ -222,6 +222,7 @@ SYSCALL_DEFINE2(getpriority, int, which,if (which >PRIO_USER || which <PRIO_PROCESS )return -EINVAL ; +rcu_read_lock ();read_lock (&tasklist_lock);switch (which) {case PRIO_PROCESS :
@@ -267,6 +268,7 @@ SYSCALL_DEFINE2(getpriority, int, which, } out_unlock:read_unlock (&tasklist_lock); +rcu_read_unlock ();return retval; }
Re: [PATCH] sys: Fix missing rcu protection for sys_getpriority.
Quoted 15 lines · Tetsuo Handa
Quoted 8 lines · Oleg Nesterov
This also fixes another bug here.
find_task_by_vpid()is not safe withoutrcu_read_lock(). I do not mean it is not safe to use the result, justfind_pid_ns()by itself is not safe.Usually tasklist gives enough protection, but if
copy_process()fails it callsfree_pid()lockless and does call_rcu(delayed_put_pid(). This means, without rcu lockfind_pid_ns()can't scan the hash table safely.This bug for
sys_setpriority()was fixed, but not fixed forsys_getpriority(). Why not to add it as well?
--------------------
[PATCH] sys: Fix missing rcu protection forsys_setpriority.
Quoted 5 lines
find_task_by_vpid()is not safe withoutrcu_read_lock(). 2.6.33-rc7 got RCU protection forsys_setpriority()but missed it forsys_getpriority().Signed-off-by Tetsuo Handa <[email protected]>
Would be needed indeed but I don't have a copy of linux-next handy - if this isn't changed there yet then
thanks,
-serge
Quoted 26 lines
--- kernel/sys.c | 2 ++ 1 file changed, 2 insertions(+)kernel/sys.c +2
-1@@ -222,6 +222,7 @@ SYSCALL_DEFINE2(getpriority, int, which,if (which >PRIO_USER || which <PRIO_PROCESS )return -EINVAL ; +rcu_read_lock ();read_lock (&tasklist_lock);switch (which) {case PRIO_PROCESS :@@ -267,6 +268,7 @@ SYSCALL_DEFINE2(getpriority, int, which, } out_unlock:read_unlock (&tasklist_lock); +rcu_read_unlock ();return retval; } --To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
[tip:core/urgent] sys: Fix missing rcu protection for __task_cred() access
In reply to Thomas GleixnerCommit-ID: d4581a239a40319205762b76c01eb6363f277efa Gitweb: http://git.kernel.org/tip/d4581a239a40319205762b76c01eb6363f277efa Author: Thomas Gleixner <[email protected]> AuthorDate: Thu, 10 Dec 2009 00:52:51 +0000 Committer: Thomas Gleixner <[email protected]> CommitDate: Thu, 10 Dec 2009 23:04:11 +0100
sys: Fix missing rcu protection for __task_cred() access
commit c69e8d9 (CRED: Use RCU to access another task's creds and to release a task's own creds) added non rcu_read_lock() protected access to task creds of the target task in set_prio_one().
The comment above the function says:
* - the caller must hold the RCU read lock
The calling code in sys_setpriority does read_lock(&tasklist_lock) but not rcu_read_lock(). This works only when CONFIG_TREE_PREEMPT_RCU=n. With CONFIG_TREE_PREEMPT_RCU=y the rcu_callbacks can run in the tick interrupt when they see no read side critical section.
There is another instance of __task_cred() in sys_setpriority() itself which is equally unprotected.
Wrap the whole code section into a rcu read side critical section to fix this quick and dirty.
Will be revisited in course of the read_lock(&tasklist_lock) -> rcu crusade.
Oleg noted further:
This also fixes another bug here. find_task_by_vpid() is not safe without rcu_read_lock(). I do not mean it is not safe to use the result, just find_pid_ns() by itself is not safe.
Usually tasklist gives enough protection, but if copy_process() fails it calls free_pid() lockless and does call_rcu(delayed_put_pid(). This means, without rcu lock find_pid_ns() can't scan the hash table safely.
LKML-Reference: <[email protected]>
--- kernel/sys.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
kernel/sys.c +2 -0
@@ -163,6 +163,7 @@ SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval)if (niceval >19 ) niceval =19 ; +rcu_read_lock ();read_lock (&tasklist_lock);switch (which) {case PRIO_PROCESS :
@@ -200,6 +201,7 @@ SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval) } out_unlock:read_unlock (&tasklist_lock); +rcu_read_unlock (); out:return error; }
Re: [patch 1/9] sys: Fix missing rcu protection for __task_cred() access
In reply to Thomas GleixnerQuoted 18 lines · Thomas Gleixner
commit c69e8d9 (CRED: Use RCU to access another task's creds and to release a task's own creds) added non
rcu_read_lock()protected access to task creds of the target task inset_prio_one().The comment above the function says:
* - the caller must hold the RCU read lockThe calling code in
sys_setprioritydoesread_lock(&tasklist_lock)but notrcu_read_lock(). This works only whenCONFIG_TREE_PREEMPT_RCU=n. WithCONFIG_TREE_PREEMPT_RCU=ythercu_callbackscan run in the tick interrupt when they see no read side critical section.There is another instance of
__task_cred()insys_setpriority()itself which is equally unprotected.Wrap the whole code section into a rcu read side critical section to fix this quick and dirty.
Quoted 6 lines
Will be revisited in course of the
read_lock(&tasklist_lock)-> rcu crusade.Signed-off-by Thomas Gleixner <[email protected]>Cc James Morris <[email protected]>
PATCH 2/9 fs: Add missing rcu protection for __task_cred() in sys_ioprio_get
In reply to Thomas Gleixnersys_ioprio_get() accesses __task_cred() without being in a RCU read side critical section. tasklist_lock is not protecting that when CONFIG_TREE_PREEMPT_RCU=y.
Add a rcu_read_lock/unlock() section around the code which accesses __task_cred().
--- fs/ioprio.c | 2 ++ 1 file changed, 2 insertions(+)
fs/ioprio.c +2 -0
@@ -230,6 +230,7 @@ SYSCALL_DEFINE2(ioprio_get, int, which,if (!user)break ; +rcu_read_lock (); do_each_thread(g, p) {if (__task_cred (p)->uid != user->uid)continue ;
@@ -241,6 +242,7 @@ SYSCALL_DEFINE2(ioprio_get, int, which,else ret =ioprio_best (ret, tmpio); }while_each_thread (g, p); +rcu_read_unlock ();if (who)free_uid (user);
Re: [patch 2/9] fs: Add missing rcu protection for __task_cred() in sys_ioprio_get
Quoted 8 lines · Thomas Gleixner
sys_ioprio_get()accesses__task_cred()without being in a RCU read side critical section.tasklist_lockis not protecting that whenCONFIG_TREE_PREEMPT_RCU=y.Add a rcu_read_lock/unlock() section around the code which accesses
__task_cred().Signed-off-by Thomas Gleixner <[email protected]>
PATCH 3/9 proc: Add missing rcu protection for __task_cred() in task_sig()
In reply to Thomas Gleixnertask_sig() accesses __task_cred() without being in a RCU read side critical section. tasklist_lock is not protecting that when CONFIG_TREE_PREEMPT_RCU=y.
Add a rcu_read_lock/unlock() section around the code which accesses __task_cred().
--- fs/proc/array.c | 2 ++ 1 file changed, 2 insertions(+)
fs/proc/array.c +2 -0
@@ -265,7 +265,9 @@ static inline void task_sig(struct seq_f blocked = p->blocked;collect_sigign_sigcatch (p, &ignored, &caught); num_threads =atomic_read (&p->signal->count); +rcu_read_lock (); qsize =atomic_read (&__task_cred (p)->user->sigpending); +rcu_read_unlock (); qlim = p->signal->rlim[RLIMIT_SIGPENDING ].rlim_cur;unlock_task_sighand (p, &flags); }
Re: [patch 3/9] proc: Add missing rcu protection for __task_cred() in task_sig()
Quoted 8 lines · Thomas Gleixner
task_sig()accesses__task_cred()without being in a RCU read side critical section.tasklist_lockis not protecting that whenCONFIG_TREE_PREEMPT_RCU=y.Add a rcu_read_lock/unlock() section around the code which accesses
__task_cred().Signed-off-by Thomas Gleixner <[email protected]>
PATCH 4/9 oom: Add missing rcu protection of __task_cred() in dump_tasks
In reply to Thomas Gleixnerdump_tasks accesses __task_cred() without being in a RCU read side critical section. tasklist_lock is not protecting that when CONFIG_TREE_PREEMPT_RCU=y.
Add a rcu_read_lock/unlock() section around the code which accesses __task_cred().
--- mm/oom_kill.c | 3 +++ 1 file changed, 3 insertions(+)
mm/oom_kill.c +3 -0
@@ -329,10 +329,13 @@ static void dump_tasks(const struct mem_task_unlock (p);continue ; } +/* Protect __task_cred() access */ +rcu_read_lock ();printk (KERN_INFO "[%5d] %5d %5d %8lu %8lu %3d %3d %s\n" , p->pid,__task_cred (p)->uid, p->tgid, mm->total_vm,get_mm_rss (mm), (int )task_cpu (p), p->signal->oom_adj, p->comm); +rcu_read_unlock ();task_unlock (p); }while_each_thread (g, p); }
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [patch 4/9] oom: Add missing rcu protection of __task_cred() in dump_tasks
Quoted 21 lines
dump_tasksaccesses__task_cred()without being in a RCU read side critical section.tasklist_lockis not protecting that whenCONFIG_TREE_PREEMPT_RCU=y.Add a rcu_read_lock/unlock() section around the code which accesses
__task_cred().Signed-off-by Thomas Gleixner <[email protected]>--- mm/oom_kill.c | 3 +++ 1 file changed, 3 insertions(+)Index:
linux-2.6-tip/mm/oom_kill.c
===================================================================mm/oom_kill.c +0
-0@@ -329,10 +329,13 @@ static void dump_tasks(const struct mem_task_unlock (p);continue ; }
mm/oom_kill.c static void dump_tasks(const struct mem_ · line 341
Quoted 10 lines
mm/oom_kill.c +3
-0+/* Protect __task_cred() access */ +rcu_read_lock ();printk (KERN_INFO "[%5d] %5d %5d %8lu %8lu %3d %3d %s\n" , p->pid,__task_cred (p)->uid, p->tgid, mm->total_vm,get_mm_rss (mm), (int )task_cpu (p), p->signal->oom_adj, p->comm); +rcu_read_unlock ();task_unlock (p); }while_each_thread (g, p); }
Looks straight forward and correct to me.
David Howells
maintainerRe: [patch 4/9] oom: Add missing rcu protection of __task_cred() in dump_tasks
In reply to Thomas Gleixnermm/oom_kill.c static void dump_tasks(const struct mem_ · line 338
Quoted 7 lines · Thomas Gleixner
mm/oom_kill.c +3
-0+/* Protect __task_cred() access */ +rcu_read_lock ();printk (KERN_INFO "[%5d] %5d %5d %8lu %8lu %3d %3d %s\n" , p->pid,__task_cred (p)->uid, p->tgid, mm->total_vm,get_mm_rss (mm), (int )task_cpu (p), p->signal->oom_adj, p->comm); +rcu_read_unlock ();
No. If there's only one access to __task_cred() like this, use task_cred_xxx() or one of its wrappers instead:
- p->pid, __task_cred(p)->uid, p->tgid, mm->total_vm,
+ p->pid, task_uid(p), p->tgid, mm->total_vm,
that limits the size of the critical section.
David
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [patch 4/9] oom: Add missing rcu protection of __task_cred() in dump_tasks
Quoted 16 lines · David Howells
mm/oom_kill.c static void dump_tasks(const struct mem_ · line 338
Quoted 7 lines · Thomas Gleixner
mm/oom_kill.c +3
-0+/* Protect __task_cred() access */ +rcu_read_lock ();printk (KERN_INFO "[%5d] %5d %5d %8lu %8lu %3d %3d %s\n" , p->pid,__task_cred (p)->uid, p->tgid, mm->total_vm,get_mm_rss (mm), (int )task_cpu (p), p->signal->oom_adj, p->comm); +rcu_read_unlock ();No. If there's only one access to
__task_cred()like this, usetask_cred_xxx()or one of its wrappers instead:(quoted diff) +1
-1- p->pid, __task_cred(p)->uid, p->tgid, mm->total_vm, + p->pid, task_uid(p), p->tgid, mm->total_vm,
Quoted 1 line
that limits the size of the critical section.
Fair enough.
tglx
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 5/9 security: Use get_task_cred() in keyctl_session_to_parent()
In reply to Thomas GleixnerStrictly this is not necessary today, but according to Paul McKenney it's not guaranteed that irq disabled regions will prevent RCU progress. That's a property of the current implementation. In preempt-rt this assumption is not true anymore.
Use get_task_cred() to make this future proof.
--- security/keys/keyctl.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
security/keys/keyctl.c +5 -2
@@ -1237,7 +1237,7 @@ long keyctl_get_security(key_serial_t kelong keyctl_session_to_parent (void ) {struct task_struct *me, *parent; -const struct cred *mycred, *pcred; +const struct cred *mycred, *pcred =NULL ;struct cred *cred, *oldcred;key_ref_t keyring_r;int ret;
@@ -1274,7 +1274,7 @@ long keyctl_session_to_parent(void)/* the parent and the child must have different session keyrings or * there's no point */ mycred =current_cred (); - pcred =__task_cred (parent); + pcred =get_task_cred (parent);if (mycred == pcred || mycred->tgcred->session_keyring == pcred->tgcred->session_keyring) goto already_same;
@@ -1312,6 +1312,7 @@ long keyctl_session_to_parent(void)set_ti_thread_flag (task_thread_info (parent),TIF_NOTIFY_RESUME );write_unlock_irq (&tasklist_lock); +put_cred (pcred);if (oldcred)put_cred (oldcred);return 0 ;
@@ -1321,6 +1322,8 @@ already_same: not_permitted:write_unlock_irq (&tasklist_lock);put_cred (cred); +if (pcred) +put_cred (pcred);return ret; error_keyring:
Re: [patch 5/9] security: Use get_task_cred() in keyctl_session_to_parent()
Quoted 6 lines · Thomas Gleixner
Strictly this is not necessary today, but according to Paul McKenney it's not guaranteed that irq disabled regions will prevent RCU progress. That's a property of the current implementation. In preempt-rt this assumption is not true anymore.
Use
get_task_cred()to make this future proof.
Quoted 48 lines
Signed-off-by Thomas Gleixner <[email protected]>Cc James Morris <[email protected]>--- security/keys/keyctl.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)Index:
linux-2.6-tip/security/keys/keyctl.c
===================================================================security/keys/keyctl.c +5
-2@@ -1237,7 +1237,7 @@ long keyctl_get_security(key_serial_t kelong keyctl_session_to_parent (void ) {struct task_struct *me, *parent; -const struct cred *mycred, *pcred; +const struct cred *mycred, *pcred =NULL ;struct cred *cred, *oldcred;key_ref_t keyring_r;int ret;@@ -1274,7 +1274,7 @@ long keyctl_session_to_parent(void)/* the parent and the child must have different session keyrings or * there's no point */ mycred =current_cred (); - pcred =__task_cred (parent); + pcred =get_task_cred (parent);if (mycred == pcred || mycred->tgcred->session_keyring == pcred->tgcred->session_keyring) goto already_same;@@ -1312,6 +1312,7 @@ long keyctl_session_to_parent(void)set_ti_thread_flag (task_thread_info (parent),TIF_NOTIFY_RESUME );write_unlock_irq (&tasklist_lock); +put_cred (pcred);if (oldcred)put_cred (oldcred);return 0 ;@@ -1321,6 +1322,8 @@ already_same: not_permitted:write_unlock_irq (&tasklist_lock);put_cred (cred); +if (pcred) +put_cred (pcred);return ret; error_keyring:
Re: [patch 5/9] security: Use get_task_cred() in keyctl_session_to_parent()
In reply to Thomas GleixnerQuoted 8 lines · Thomas Gleixner
Strictly this is not necessary today, but according to Paul McKenney it's not guaranteed that irq disabled regions will prevent RCU progress. That's a property of the current implementation. In preempt-rt this assumption is not true anymore.
Use
get_task_cred()to make this future proof.Signed-off-by Thomas Gleixner <[email protected]>
PATCH 6/9 signal: Fix racy access to __task_cred in kill_pid_info_as_uid()
In reply to Thomas Gleixnerkill_pid_info_as_uid() accesses __task_cred() without being in a RCU read side critical section. tasklist_lock is not protecting that when CONFIG_TREE_PREEMPT_RCU=y.
Convert the whole tasklist_lock section to rcu and use lock_task_sighand to prevent the exit race.
--- kernel/signal.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-)
kernel/signal.c +10 -7
@@ -1175,11 +1175,12 @@ int kill_pid_info_as_uid(int sig, structint ret = -EINVAL ;struct task_struct *p;const struct cred *pcred; +unsigned long flags;if (!valid_signal (sig))return ret; -read_lock (&tasklist_lock); +rcu_read_lock (); p =pid_task (pid,PIDTYPE_PID );if (!p) { ret = -ESRCH ;
@@ -1196,14 +1197,16 @@ int kill_pid_info_as_uid(int sig, struct ret =security_task_kill (p, info, sig, secid);if (ret) goto out_unlock; -if (sig && p->sighand) { -unsigned long flags; -spin_lock_irqsave (&p->sighand->siglock, flags); - ret =__send_signal (sig, info, p,1 ,0 ); -spin_unlock_irqrestore (&p->sighand->siglock, flags); + +if (sig) { +if (lock_task_sighand (p, &flags)) { + ret =__send_signal (sig, info, p,1 ,0 ); +unlock_task_sighand (p, &flags); + }else + ret = -ESRCH ; } out_unlock: -read_unlock (&tasklist_lock); +rcu_read_unlock ();return ret; }EXPORT_SYMBOL_GPL (kill_pid_info_as_uid);
Re: [patch 6/9] signal: Fix racy access to __task_cred in kill_pid_info_as_uid()
Quoted 5 lines · Thomas Gleixner
kill_pid_info_as_uid()accesses__task_cred()without being in a RCU read side critical section.tasklist_lockis not protecting that whenCONFIG_TREE_PREEMPT_RCU=y.
Quoted 8 lines
Convert the whole
tasklist_locksection to rcu and uselock_task_sighandto prevent the exit race.Signed-off-by Thomas Gleixner <[email protected]>Cc Oleg Nesterov <[email protected]>--- kernel/signal.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-)
Quoted 43 lines
Index:
linux-2.6-tip/kernel/signal.c
===================================================================kernel/signal.c +10
-7@@ -1175,11 +1175,12 @@ int kill_pid_info_as_uid(int sig, structint ret = -EINVAL ;struct task_struct *p;const struct cred *pcred; +unsigned long flags;if (!valid_signal (sig))return ret; -read_lock (&tasklist_lock); +rcu_read_lock (); p =pid_task (pid,PIDTYPE_PID );if (!p) { ret = -ESRCH ;@@ -1196,14 +1197,16 @@ int kill_pid_info_as_uid(int sig, struct ret =security_task_kill (p, info, sig, secid);if (ret) goto out_unlock; -if (sig && p->sighand) { -unsigned long flags; -spin_lock_irqsave (&p->sighand->siglock, flags); - ret =__send_signal (sig, info, p,1 ,0 ); -spin_unlock_irqrestore (&p->sighand->siglock, flags); + +if (sig) { +if (lock_task_sighand (p, &flags)) { + ret =__send_signal (sig, info, p,1 ,0 ); +unlock_task_sighand (p, &flags); + }else + ret = -ESRCH ; } out_unlock: -read_unlock (&tasklist_lock); +rcu_read_unlock ();return ret; }EXPORT_SYMBOL_GPL (kill_pid_info_as_uid);
[tip:core/urgent] signal: Fix racy access to __task_cred in kill_pid_info_as_uid()
In reply to Thomas GleixnerCommit-ID: 14d8c9f3c09e7fd7b9af80904289fe204f5b93c6 Gitweb: http://git.kernel.org/tip/14d8c9f3c09e7fd7b9af80904289fe204f5b93c6 Author: Thomas Gleixner <[email protected]> AuthorDate: Thu, 10 Dec 2009 00:53:17 +0000 Committer: Thomas Gleixner <[email protected]> CommitDate: Thu, 10 Dec 2009 23:04:11 +0100
signal: Fix racy access to __task_cred in kill_pid_info_as_uid()
kill_pid_info_as_uid() accesses __task_cred() without being in a RCU read side critical section. tasklist_lock is not protecting that when CONFIG_TREE_PREEMPT_RCU=y.
Convert the whole tasklist_lock section to rcu and use lock_task_sighand to prevent the exit race.
LKML-Reference: <[email protected]>
--- kernel/signal.c | 17 ++++++++++------- 1 files changed, 10 insertions(+), 7 deletions(-)
kernel/signal.c +10 -7
@@ -1175,11 +1175,12 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid,int ret = -EINVAL ;struct task_struct *p;const struct cred *pcred; +unsigned long flags;if (!valid_signal (sig))return ret; -read_lock (&tasklist_lock); +rcu_read_lock (); p =pid_task (pid,PIDTYPE_PID );if (!p) { ret = -ESRCH ;
@@ -1196,14 +1197,16 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid, ret =security_task_kill (p, info, sig, secid);if (ret) goto out_unlock; -if (sig && p->sighand) { -unsigned long flags; -spin_lock_irqsave (&p->sighand->siglock, flags); - ret =__send_signal (sig, info, p,1 ,0 ); -spin_unlock_irqrestore (&p->sighand->siglock, flags); + +if (sig) { +if (lock_task_sighand (p, &flags)) { + ret =__send_signal (sig, info, p,1 ,0 ); +unlock_task_sighand (p, &flags); + }else + ret = -ESRCH ; } out_unlock: -read_unlock (&tasklist_lock); +rcu_read_unlock ();return ret; }EXPORT_SYMBOL_GPL (kill_pid_info_as_uid);
Re: [patch 6/9] signal: Fix racy access to __task_cred in kill_pid_info_as_uid()
In reply to Thomas GleixnerQuoted 8 lines · Thomas Gleixner
kill_pid_info_as_uid()accesses__task_cred()without being in a RCU read side critical section.tasklist_lockis not protecting that whenCONFIG_TREE_PREEMPT_RCU=y.Convert the whole
tasklist_locksection to rcu and uselock_task_sighandto prevent the exit race.Signed-off-by Thomas Gleixner <[email protected]>
PATCH 7/9 signals: Fix more rcu assumptions
In reply to Thomas Gleixner1) Remove the misleading comment in __sigqueue_alloc() which claims that holding a spinlock is equivalent to rcu_read_lock().
2) Wrap the __send_signal() call in send_signal() into a rcu read side critical section to guarantee that the __sigqueue_alloc() requirement is met in any case.
This needs to be revisited to remove the remaining users of read_lock(&tasklist_lock) but that's outside the scope of this patch.
--- kernel/signal.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
kernel/signal.c +7 -4
@@ -220,8 +220,7 @@ __sigqueue_alloc(int sig, struct task_st/* * We won't get problems with the target's UID changing under us * because changing it requires RCU be used, and if t != current, the -* caller must be holding the RCU readlock (by way of a spinlock) and -* we use RCU protection here +* caller must be holding the RCU readlock. */ user =get_uid (__task_cred (t)->user);atomic_inc (&user->sigpending);
@@ -946,7 +945,7 @@ out_set:static int send_signal (int sig,struct siginfo *info,struct task_struct *t,int group) { -int from_ancestor_ns =0 ; +int ret, from_ancestor_ns =0 ;#ifdef CONFIG_PID_NS if (!is_si_special(info) && SI_FROMUSER(info) &&
@@ -954,7 +953,11 @@ static int send_signal(int sig, struct s from_ancestor_ns =1 ;#endif -return __send_signal (sig, info, t, group, from_ancestor_ns); +rcu_read_lock (); + ret =__send_signal (sig, info, t, group, from_ancestor_ns); +rcu_read_unlock (); + +return ret; }static void print_fatal_signal (struct pt_regs *regs,int signr)
Re: [patch 7/9] signals: Fix more rcu assumptions
Quoted 17 lines · Thomas Gleixner
1) Remove the misleading comment in
__sigqueue_alloc()which claims that holding a spinlock is equivalent torcu_read_lock().2) Wrap the
__send_signal()call insend_signal()into a rcu read side critical section to guarantee that the__sigqueue_alloc()requirement is met in any case.
...
static int send_signal(int sig, struct siginfo *info, structtask_struct*t,kernel/signal.c +1
-1int group) { -int from_ancestor_ns =0 ; +int ret, from_ancestor_ns =0 ;#ifdef CONFIG_PID_NS if (!is_si_special(info) && SI_FROMUSER(info) &&
kernel/signal.c static int send_signal(int sig, struct s · line 958
Quoted 7 lines
kernel/signal.c +3
-1@@ -954,7 +953,11 @@ static int send_signal(int sig, struct s from_ancestor_ns =1 ;#endif -return __send_signal (sig, info, t, group, from_ancestor_ns); +rcu_read_lock (); + ret =__send_signal (sig, info, t, group, from_ancestor_ns); +rcu_read_unlock ();
But, without a comment it is very unobvious why do we need rcu_read_lock().
Perhaps it is better to modify __sigqueue_alloc() instead? It can take rcu_lock() around cred->user itself.
Oleg.
Re: [patch 7/9] signals: Fix more rcu assumptions
Quoted 28 lines · Oleg Nesterov
Quoted 17 lines · Thomas Gleixner
1) Remove the misleading comment in
__sigqueue_alloc()which claims that holding a spinlock is equivalent torcu_read_lock().2) Wrap the
__send_signal()call insend_signal()into a rcu read side critical section to guarantee that the__sigqueue_alloc()requirement is met in any case.
...
static int send_signal(int sig, struct siginfo *info, structtask_struct*t,kernel/signal.c +1
-1int group) { -int from_ancestor_ns =0 ; +int ret, from_ancestor_ns =0 ;#ifdef CONFIG_PID_NS if (!is_si_special(info) && SI_FROMUSER(info) &&kernel/signal.c static int send_signal(int sig, struct s · line 958
Quoted 7 lines
kernel/signal.c +3
-1@@ -954,7 +953,11 @@ static int send_signal(int sig, struct s from_ancestor_ns =1 ;#endif -return __send_signal (sig, info, t, group, from_ancestor_ns); +rcu_read_lock (); + ret =__send_signal (sig, info, t, group, from_ancestor_ns); +rcu_read_unlock ();But, without a comment it is very unobvious why do we need
rcu_read_lock().
Quoted 2 lines
Perhaps it is better to modify
__sigqueue_alloc()instead? It can takercu_lock()aroundcred->useritself.
Indeed. Was too tired to see the obvious :)
Thanks,
tglx
David Howells
maintainerRe: [patch 7/9] signals: Fix more rcu assumptions
Quoted 4 lines · Thomas Gleixner
Quoted 2 lines
Perhaps it is better to modify
__sigqueue_alloc()instead? It can takercu_lock()aroundcred->useritself.Indeed. Was too tired to see the obvious :)
Ah, but... If __sigqueue_alloc() is called from sigqueue_alloc(), then you don't need the RCU read lock as the target task is current.
So perhaps the callsite for __sigqueue_alloc() in __send_signal()? That at least puts the rcu_read_lock() call in proximity to the function that actually needs it.
David
[tip:core/urgent] signals: Fix more rcu assumptions
In reply to Thomas GleixnerCommit-ID: 7cf7db8df0b78076eafa4ead47559344ca7b7a43 Gitweb: http://git.kernel.org/tip/7cf7db8df0b78076eafa4ead47559344ca7b7a43 Author: Thomas Gleixner <[email protected]> AuthorDate: Thu, 10 Dec 2009 00:53:21 +0000 Committer: Thomas Gleixner <[email protected]> CommitDate: Thu, 10 Dec 2009 23:04:11 +0100
signals: Fix more rcu assumptions
1) Remove the misleading comment in __sigqueue_alloc() which claims that holding a spinlock is equivalent to rcu_read_lock().
2) Add a rcu_read_lock/unlock around the __task_cred() access in __sigqueue_alloc()
This needs to be revisited to remove the remaining users of read_lock(&tasklist_lock) but that's outside the scope of this patch.
LKML-Reference: <[email protected]>
--- kernel/signal.c | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-)
kernel/signal.c +4 -4
@@ -218,13 +218,13 @@ __sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimistruct user_struct *user;/* -* We won't get problems with the target's UID changing under us -* because changing it requires RCU be used, and if t != current, the -* caller must be holding the RCU readlock (by way of a spinlock) and -* we use RCU protection here +* Protect access to @t credentials. This can go away when all +* callers hold rcu read lock. */ +rcu_read_lock (); user =get_uid (__task_cred (t)->user);atomic_inc (&user->sigpending); +rcu_read_unlock ();if (override_rlimit || atomic_read(&user->sigpending) <=
PATCH 8/9 Documentation: Fix invalid rcu assumptions
In reply to Thomas Gleixner1) spinlock held is not equivalent to rcu_read_lock()
2) remove the stale signal code snippet
--- Documentation/credentials.txt | 3 --- Documentation/kmemcheck.txt | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-)
Documentation/credentials.txt +0 -3
@@ -408,9 +408,6 @@ This should be used inside the RCU read ... } -A function need not get RCU read lock to use __task_cred() if it is holding a -spinlock at the time as this implicitly holds the RCU read lock. - Should it be necessary to hold another task's credentials for a long period of time, and possibly to sleep whilst doing so, then the caller should get a reference on them using:
Documentation/kmemcheck.txt +1 -2
@@ -429,8 +429,7 @@ Let's take a look at it: 193 /* 194 * We won't get problems with the target's UID changing under us 195 * because changing it requires RCU be used, and if t != current, the -196 * caller must be holding the RCU readlock (by way of a spinlock) and -197 * we use RCU protection here +196 * caller must be holding the RCU readlocke 198 */ 199 user = get_uid(__task_cred(t)->user); 200 atomic_inc(&user->sigpending);
Re: [patch 8/9] Documentation: Fix invalid rcu assumptions
[trimmed Cc]
Quoted 1 line
2) remove the stale signal code snippet
...
2009/12/10 Thomas Gleixner <[email protected]>:
Quoted 6 lines
Index:
linux-2.6-tip/Documentation/kmemcheck.txt
===================================================================Documentation/kmemcheck.txt +0
-0@@ -429,8 +429,7 @@ Let's take a look at it:193 /*
Quoted 8 lines
194 * We won't get problems with the target's UID changing under us 195 * because changing it requires RCU be used, and if t != current, the -196 * caller must be holding the RCU readlock (by way of a spinlock) and -197 * we use RCU protection here
+196 * caller must be holding the RCU readlocke
198 */
199 user = get_uid(__task_cred(t)->user);
200atomic_inc(&user->sigpending);
I am not sure that I really agree with this change. This is not a code example for the sake of showing how to do a particular thing, it's an example of real code from the tree.
I don't remember if the document is referring to a particular git version of the code, but I think it might not, in which case it doesn't REALLY matter even on the microscopic level.
But I won't make a big fuss about it :-)
Vegard
PS: Upon closer inspection, I noticed that one line (line 197) goes completely missing, there seems to be a typo there too, "readlocke". Still it's not a huge deal, I admit.
Re: [patch 8/9] Documentation: Fix invalid rcu assumptions
In reply to Thomas GleixnerQuoted 2 lines · Thomas Gleixner
-197 * we use RCU protection here
+196 * caller must be holding the RCU readlocke
You mean "readlock" I suspect.
Otherwise, feel free to add:
Re: [patch 8/9] Documentation: Fix invalid rcu assumptions
Quoted 6 lines · David Howells
Quoted 2 lines · Thomas Gleixner
-197 * we use RCU protection here
+196 * caller must be holding the RCU readlockeYou mean "readlock" I suspect.
Or maybe he's talking about ye olde readlocke, used widely for OS research throughout the middle ages. You still find that spelling in some really old CS literature.
Linus
Re: [patch 8/9] Documentation: Fix invalid rcu assumptions
Quoted 6 lines · Linus Torvalds
Quoted 2 lines · David Howells
Thomas Gleixner <[email protected]> wrote:
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
-197 * we use RCU protection here
+196 * caller must be holding the RCU readlockeYou mean "readlock" I suspect.
Or maybe he's talking about ye olde readlocke, used widely for OS research throughout the middle ages. You still find that spelling in some really old CS literature.
Interestingly enough, they also tended to split it into two words and capitalize it, as can be seen by searching for "Read Locke" at
http://faculty.uml.edu/enelson/modern07.htm
Thanx, Paul
Re: [patch 8/9] Documentation: Fix invalid rcu assumptions
Quoted 12 lines · Paul E. McKenney
Quoted 11 lines · Linus Torvalds
Quoted 6 lines · David Howells
Quoted 2 lines · Thomas Gleixner
-197 * we use RCU protection here
+196 * caller must be holding the RCU readlockeYou mean "readlock" I suspect.
Quoted 8 lines
Quoted 3 lines
Or maybe he's talking about ye olde readlocke, used widely for OS research throughout the middle ages. You still find that spelling in some really old CS literature.
Interestingly enough, they also tended to split it into two words and capitalize it, as can be seen by searching for "Read Locke" at
The good thing about "Read Locke" is: there is no "Write Locke".
Thanks,
tglx
Re: [patch 8/9] Documentation: Fix invalid rcu assumptions
In reply to Thomas GleixnerQuoted 5 lines · On Thursday 10 December 2009 00:53:26 Thomas Gleixner wrote:
Index:
linux-2.6-tip/Documentation/credentials.txt
===================================================================
Documentation/credentials.txt This should be used inside the RCU read · line 413
Quoted 9 lines
Documentation/credentials.txt +0
-3@@ -408,9 +408,6 @@ This should be used inside the RCU read } -A function need not get RCU read lock to use __task_cred() if it is holding a -spinlock at the time as this implicitly holds the RCU read lock. - Should it be necessary to hold another task's credentials for a long period of time, and possibly to sleep whilst doing so, then the caller should get a reference on them using:
How about changing the documentation to explain why you can't just use a spinlock or local_irq_disable instead of rcu_read_lock? You explained it well in your [patch 0/9], but that part had not occurred to me yet and having it in the kernel sources might prevent more people from getting it wrong in the future.
Arnd
Re: [patch 8/9] Documentation: Fix invalid rcu assumptions
Quoted 9 lines · Arnd Bergmann
Quoted 8 lines · On Thursday 10 December 2009 00:53:26 Thomas Gleixner wrote:
Index:
linux-2.6-tip/Documentation/credentials.txt
===================================================================Documentation/credentials.txt +0
-0@@ -408,9 +408,6 @@ This should be used inside the RCU read }
Quoted 11 lines
Documentation/credentials.txt This should be used inside the RCU read · line 413
Quoted 6 lines
Documentation/credentials.txt +0
-3-A function need not get RCU read lock to use __task_cred() if it is holding a -spinlock at the time as this implicitly holds the RCU read lock. - Should it be necessary to hold another task's credentials for a long period of time, and possibly to sleep whilst doing so, then the caller should get a reference on them using:How about changing the documentation to explain why you can't just use a spinlock or
local_irq_disableinstead ofrcu_read_lock? You explained it well in your [patch 0/9], but that part had not occurred to me yet and having it in the kernel sources might prevent more people from getting it wrong in the future.
That does make a lot of sense... Will add that in.
Thanx, Paul
PATCH 9/9 security: Fix invalid rcu assumptions in comments
In reply to Thomas Gleixner1) held spinlocks are not equivalent to rcu_read_lock
2) access to current_cred() is safe as only current can modify its own credentials.
--- security/keys/permission.c | 3 +-- security/keys/proc.c | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-)
security/keys/permission.c +1 -2
@@ -23,8 +23,7 @@* Check to see whether permission is granted to use a key in the desired way, * but permit the security modules to override. * -* The caller must hold either a ref on cred or must hold the RCU readlock or a -* spinlock. +* The caller must hold either a ref on cred or must hold the RCU readlock. */ int key_task_permission (const key_ref_t key_ref,const struct cred *cred,key_perm_t perm)
security/keys/proc.c +0 -2
@@ -194,8 +194,6 @@ static int proc_keys_show(struct seq_fil/* check whether the current task is allowed to view the key (assuming * non-possession) -* - the caller holds a spinlock, and thus the RCU read lock, making our -* access to __current_cred() safe */ rc =key_task_permission (make_key_ref (key,0 ),current_cred (),KEY_VIEW );
Re: [patch 9/9] security: Fix invalid rcu assumptions in comments
Quoted 6 lines · Thomas Gleixner
1) held spinlocks are not equivalent to
rcu_read_lock2) access to
current_cred()is safe as only current can modify its own credentials.Signed-off-by Thomas Gleixner <[email protected]>
Quoted 10 lines · Thomas Gleixner
While auditing the
read_lock(&tasklist_lock)sites for a possible conversion to rcu-read_lock() I stumbled over an unprotected user of__task_credinkernel/sys.cThat caused me to audit all the
__task_credusage sites except inkernel/exit.c.Most of the usage sites are correct, but some of them trip over invalid assumptions about the protection which is given by RCU.
Quoted 7 lines
- spinlocked/preempt_disabled regions are equivalent to
rcu_read_lock():That's wrong. RCU does not guarantee that.
It has been that way due to implementation details and it still is valid for
CONFIG_TREE_PREEMPT_RCU=n, but there is no guarantee that this will be the case forever.
To back this up, item #2 from Documentation/RCU/checklist.txt says:
2. Do the RCU read-side critical sections make proper use of
rcu_read_lock() and friends? These primitives are needed to prevent grace periods from ending prematurely, which could result in data being unceremoniously freed out from under your read-side code, which can greatly increase the actuarial risk of your kernel.
As a rough rule of thumb, any dereference of an RCU-protected pointer must be covered by rcu_read_lock() or rcu_read_lock_bh() or by the appropriate update-side lock.
Quoted 10 lines
- interrupt disabled regions are equivalent to
rcu_read_lock():Wrong again. RCU does not guarantee that.
It's true for current mainline, but again this is an implementation detail and there is no guarantee by the RCU semantics.
Indeed we want to get rid of that to avoid scalability issues on large systems and preempt-rt got already rid of it to a certain extent.
Same item #2 above covers this.
The only exception is when you use synchronize_sched(), as described in the "Defer"/"Protect" list near line 323 of the 2.6.32 version of Documentation/RCU/whatisRCU.txt:
Defer Protect
a. synchronize_rcu() rcu_read_lock() / rcu_read_unlock()
call_rcu()
b. call_rcu_bh() rcu_read_lock_bh() / rcu_read_unlock_bh()
c. synchronize_sched() preempt_disable() / preempt_enable()
local_irq_save() / local_irq_restore() hardirq enter / hardirq exit NMI enter / NMI exit
And yes, I need to update this based on the addition of rcu_read_lock_sched() and friends. I will be doing another documentation update soon.
Quoted 6 lines
I'm sure we are lucky that
CONFIG_TREE_PREEMPT_RCU=yis not yet wide spread and the code pathes are esoteric enough not to trigger that subtle races (some of them might just error out silently).Nevertheless we need to fix all invalid assumptions about RCU protection.
Agreed!!!
Quoted 2 lines
The following patch series fixes all yet known affected
__task_cred()sites, but there is more auditing of all other rcu users necessary.
Thank you very much for putting this series together -- I will take a quick look at them.
Thanx, Paul
Quoted 13 lines · Paul E. McKenney
Quoted 12 lines · Thomas Gleixner
While auditing the
read_lock(&tasklist_lock)sites for a possible conversion to rcu-read_lock() I stumbled over an unprotected user of__task_credinkernel/sys.cThat caused me to audit all the
__task_credusage sites except inkernel/exit.c.Most of the usage sites are correct, but some of them trip over invalid assumptions about the protection which is given by RCU.
- spinlocked/preempt_disabled regions are equivalent to
rcu_read_lock():
Quoted 7 lines
Quoted 5 lines
That's wrong. RCU does not guarantee that.
It has been that way due to implementation details and it still is valid for
CONFIG_TREE_PREEMPT_RCU=n, but there is no guarantee that this will be the case forever.To back this up, item #2 from
Documentation/RCU/checklist.txtsays:
Hmm. This seems to be a difference that the tree-RCU things introduced, no? I wonder if we have other areas where we just knew that a spinlock would make an rcu read-lock unnecessary (which used to be true..)
Linus
Quoted 22 lines · Linus Torvalds
Quoted 19 lines · Paul E. McKenney
Quoted 17 lines · Thomas Gleixner
While auditing the
read_lock(&tasklist_lock)sites for a possible conversion to rcu-read_lock() I stumbled over an unprotected user of__task_credinkernel/sys.cThat caused me to audit all the
__task_credusage sites except inkernel/exit.c.Most of the usage sites are correct, but some of them trip over invalid assumptions about the protection which is given by RCU.
- spinlocked/preempt_disabled regions are equivalent to
rcu_read_lock():That's wrong. RCU does not guarantee that.
It has been that way due to implementation details and it still is valid for
CONFIG_TREE_PREEMPT_RCU=n, but there is no guarantee that this will be the case forever.
Quoted 5 lines
Quoted 1 line
To back this up, item #2 from
Documentation/RCU/checklist.txtsays:Hmm. This seems to be a difference that the tree-RCU things introduced, no? I wonder if we have other areas where we just knew that a spinlock would make an rcu read-lock unnecessary (which used to be true..)
That failed being true when we merged PREEMPT_RCU,.. which was a long time ago.
Quoted 23 lines · Peter Zijlstra
Quoted 22 lines · Linus Torvalds
Quoted 19 lines · Paul E. McKenney
Quoted 17 lines · Thomas Gleixner
While auditing the
read_lock(&tasklist_lock)sites for a possible conversion to rcu-read_lock() I stumbled over an unprotected user of__task_credinkernel/sys.cThat caused me to audit all the
__task_credusage sites except inkernel/exit.c.Most of the usage sites are correct, but some of them trip over invalid assumptions about the protection which is given by RCU.
- spinlocked/preempt_disabled regions are equivalent to
rcu_read_lock():That's wrong. RCU does not guarantee that.
It has been that way due to implementation details and it still is valid for
CONFIG_TREE_PREEMPT_RCU=n, but there is no guarantee that this will be the case forever.
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
To back this up, item #2 from
Documentation/RCU/checklist.txtsays:Hmm. This seems to be a difference that the tree-RCU things introduced, no? I wonder if we have other areas where we just knew that a spinlock would make an rcu read-lock unnecessary (which used to be true..)
That failed being true when we merged
PREEMPT_RCU,.. which was a long time ago.
Ah -- I have a related lockdep question. Is there a primitive that says whether or not the current task holds at least one lock of any type? If so, I would like to make rcu_dereference() do at least a little crude checking for this problem.
Thanx, Paul
Quoted 4 lines · Paul E. McKenney
Ah -- I have a related lockdep question. Is there a primitive that says whether or not the current task holds at least one lock of any type? If so, I would like to make
rcu_dereference()do at least a little crude checking for this problem.
Hmm, no, but that's not hard to do, however I actually implemented something like that for RCU a long while ago and that gives a metric TON of false positives due to things like the radix tree which are RCU-safe but are not required to be used with RCU.
Quoted 11 lines · Peter Zijlstra
Quoted 4 lines · Paul E. McKenney
Ah -- I have a related lockdep question. Is there a primitive that says whether or not the current task holds at least one lock of any type? If so, I would like to make
rcu_dereference()do at least a little crude checking for this problem.Hmm, no, but that's not hard to do, however I actually implemented something like that for RCU a long while ago and that gives a metric TON of false positives due to things like the radix tree which are RCU-safe but are not required to be used with RCU.
Understood -- my current guess is that there needs to be a way to tag a variant of the rcu_dereference() API with the conditions that must be met, for example, either in an rcu-sched read-side critical section or holding a specific type of lock.
This does make it a little harder to retroactively add checking to existing calls to rcu_dereference(), but should allow a good balance between false positives and false negatives going forward.
Seem reasonable, or am I still missing something?
Thanx, Paul
Quoted 18 lines · Paul E. McKenney
Quoted 11 lines · Peter Zijlstra
Quoted 4 lines · Paul E. McKenney
Ah -- I have a related lockdep question. Is there a primitive that says whether or not the current task holds at least one lock of any type? If so, I would like to make
rcu_dereference()do at least a little crude checking for this problem.Hmm, no, but that's not hard to do, however I actually implemented something like that for RCU a long while ago and that gives a metric TON of false positives due to things like the radix tree which are RCU-safe but are not required to be used with RCU.
Understood -- my current guess is that there needs to be a way to tag a variant of the
rcu_dereference()API with the conditions that must be met, for example, either in an rcu-sched read-side critical section or holding a specific type of lock.
Quoted 5 lines
This does make it a little harder to retroactively add checking to existing calls to
rcu_dereference(), but should allow a good balance between false positives and false negatives going forward.Seem reasonable, or am I still missing something?
The only concern is drowning in rcu_dereference() annotations. But I guess that is unavoidable.
I think you can use lock_is_held(&rcu_lock_map), except you need to deal with the !debug_locks case, because lockdep stops once debug_locks becomes false, which means lock_is_held() will return rubbish.
Quoted 19 lines · Peter Zijlstra
Quoted 18 lines · Paul E. McKenney
Quoted 11 lines · Peter Zijlstra
Quoted 4 lines · Paul E. McKenney
Ah -- I have a related lockdep question. Is there a primitive that says whether or not the current task holds at least one lock of any type? If so, I would like to make
rcu_dereference()do at least a little crude checking for this problem.Hmm, no, but that's not hard to do, however I actually implemented something like that for RCU a long while ago and that gives a metric TON of false positives due to things like the radix tree which are RCU-safe but are not required to be used with RCU.
Understood -- my current guess is that there needs to be a way to tag a variant of the
rcu_dereference()API with the conditions that must be met, for example, either in an rcu-sched read-side critical section or holding a specific type of lock.
Quoted 8 lines
Quoted 5 lines
This does make it a little harder to retroactively add checking to existing calls to
rcu_dereference(), but should allow a good balance between false positives and false negatives going forward.Seem reasonable, or am I still missing something?
The only concern is drowning in
rcu_dereference()annotations. But I guess that is unavoidable.
So far, I haven't been able to think of anything better. :-/
Quoted 3 lines
I think you can use
lock_is_held(&rcu_lock_map), except you need to deal with the !debug_locks case, because lockdep stops oncedebug_locksbecomes false, which meanslock_is_held()will return rubbish.
OK, so I need to do something like the following, then?
debug_locks ? lock_is_held(&rcu_lock_map) : 1
Thanx, Paul
Quoted 7 lines · Paul E. McKenney
Quoted 3 lines
I think you can use
lock_is_held(&rcu_lock_map), except you need to deal with the !debug_locks case, because lockdep stops oncedebug_locksbecomes false, which meanslock_is_held()will return rubbish.OK, so I need to do something like the following, then?
debug_locks?lock_is_held(&rcu_lock_map): 1
Depending on what the safe return value is, yeah. If you want an assert like function, false is usually the safe one.
#define lockdep_assert_held(l) WARN_ON(debug_locks && !lockdep_is_held(l))
is the one in-tree user of this.
Quoted 8 lines · Peter Zijlstra
Quoted 6 lines · Paul E. McKenney
Quoted 3 lines
I think you can use
lock_is_held(&rcu_lock_map), except you need to deal with the !debug_locks case, because lockdep stops oncedebug_locksbecomes false, which meanslock_is_held()will return rubbish.OK, so I need to do something like the following, then?
Quoted 8 lines
Quoted 1 line
debug_locks?lock_is_held(&rcu_lock_map): 1Depending on what the safe return value is, yeah. If you want an assert like function, false is usually the safe one.
#define
lockdep_assert_held(l)WARN_ON(debug_locks && !lockdep_is_held(l))is the one in-tree user of this.
Sounds good, thank you for the tip!!!
Thanx, Paul
David Howells
maintainerQuoted 3 lines · Thomas Gleixner
While auditing the
read_lock(&tasklist_lock)sites for a possible conversion to rcu-read_lock() I stumbled over an unprotected user of__task_credinkernel/sys.c
I'm sure last time I looked, spinlock primitives implied RCU read locks. Maybe I was mistaken or maybe it's changed. Whatever, good catch, Thomas!
Thanks,
David
Quoted 8 lines · David Howells
Quoted 3 lines · Thomas Gleixner
While auditing the
read_lock(&tasklist_lock)sites for a possible conversion to rcu-read_lock() I stumbled over an unprotected user of__task_credinkernel/sys.cI'm sure last time I looked, spinlock primitives implied RCU read locks. Maybe I was mistaken or maybe it's changed. Whatever, good catch, Thomas!
It did indeed change with the split of the old synchronize_kernel() primitive into the synchronize_rcu() and synchronize_sched() primitives in 2.6.12 -- after this point, synchronize_rcu() was only guaranteed to respect "real" rcu_read_lock()-base critical sections. But actual failures would not show up until PREEMPT_RCU was introduced into 2.6.25.
The -rt effort rooted out a bunch of these sorts of problems, but we clearly missed a few.
Thanx, Paul