Skip to contentSkip to conversation

Fix RCU lockdep splats in keys and sched

PATCH New Paul E. McKenney 2 participants 6 messages Apr 22, 2010

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Fix RCU lockdep splats in keys and sched

Hello!

This series fixes a pair of RCU lockdep splats in the keys subsystem (courtesy of David Howells) and another in the scheduler.

Thanx, Paul

 kernel/sched.c               |    6 +++++-
 security/keys/request_key.c  |   13 ++++++++-----
 security/keys/user_defined.c |    3 ++-
 3 files changed, 15 insertions(+), 7 deletions(-)
#1 lore raw

PATCH 1/3 KEYS: Fix an RCU warning

From: David Howells <[email protected]>

Fix the following RCU warning:

===================================================
[ INFO: suspicious rcu_dereference_check() usage. ]
---------------------------------------------------
security/keys/request_key.c:116 invoked rcu_dereference_check() without protection!

other info that might help us debug this:

rcu_scheduler_active = 1, debug_locks = 0
1 lock held by keyctl/5372:
#0: (key_types_sem){.+.+.+}, at: [<ffffffff811a4e3d>] key_type_lookup+0x1c/0x70

stack backtrace:
Pid: 5372, comm: keyctl Not tainted 2.6.34-rc3-cachefs #150
Call Trace:
 [<ffffffff810515f8>] lockdep_rcu_dereference+0xaa/0xb2
 [<ffffffff811a9220>] call_sbin_request_key+0x156/0x2b6
 [<ffffffff811a4c66>] ? __key_instantiate_and_link+0xb1/0xdc
 [<ffffffff811a4cd3>] ? key_instantiate_and_link+0x42/0x5f
 [<ffffffff811a96b8>] ? request_key_auth_new+0x17b/0x1f3
 [<ffffffff811a8e00>] ? request_key_and_link+0x271/0x400
 [<ffffffff810aba6f>] ? kmem_cache_alloc+0xe1/0x118
 [<ffffffff811a8f1a>] request_key_and_link+0x38b/0x400
 [<ffffffff811a7b72>] sys_request_key+0xf7/0x14a
 [<ffffffff81052227>] ? trace_hardirqs_on_caller+0x10c/0x130
 [<ffffffff81393f5c>] ? trace_hardirqs_on_thunk+0x3a/0x3f
 [<ffffffff81001eeb>] system_call_fastpath+0x16/0x1b

This was caused by doing:

	[root@andromeda ~]# keyctl newring fred @s
	539196288
	[root@andromeda ~]# keyctl request2 user a a 539196288
	request_key: Required key not available
---
 security/keys/request_key.c |   13 ++++++++-----
 1 files changed, 8 insertions(+), 5 deletions(-)
security/keys/request_key.c +8 -5
index 03fe63e..ea97c31 100644
@@ -68,7 +68,8 @@ static int call_sbin_request_key(struct key_construction *cons,
 {
 	const struct cred *cred = current_cred();
 	key_serial_t prkey, sskey;
-	struct key *key = cons->key, *authkey = cons->authkey, *keyring;
+	struct key *key = cons->key, *authkey = cons->authkey, *keyring,
+		*session;
 	char *argv[9], *envp[3], uid_str[12], gid_str[12];
 	char key_str[12], keyring_str[3][12];
 	char desc[20];
@@ -112,10 +113,12 @@ static int call_sbin_request_key(struct key_construction *cons,
 	if (cred->tgcred->process_keyring)
 		prkey = cred->tgcred->process_keyring->serial;
 
-	if (cred->tgcred->session_keyring)
-		sskey = rcu_dereference(cred->tgcred->session_keyring)->serial;
-	else
-		sskey = cred->user->session_keyring->serial;
+	rcu_read_lock();
+	session = rcu_dereference(cred->tgcred->session_keyring);
+	if (!session)
+		session = cred->user->session_keyring;
+	sskey = session->serial;
+	rcu_read_unlock();
 
 	sprintf(keyring_str[2], "%d", sskey);
 
Signature · 1 line
1.7.0
#2 lore raw

PATCH 2/3 KEYS: Fix an RCU warning in the reading of user keys

In reply to Paul E. McKenney

From: David Howells <[email protected]>

Fix an RCU warning in the reading of user keys:

===================================================
[ INFO: suspicious rcu_dereference_check() usage. ]
---------------------------------------------------
security/keys/user_defined.c:202 invoked rcu_dereference_check() without protection!

other info that might help us debug this:

rcu_scheduler_active = 1, debug_locks = 0
1 lock held by keyctl/3637:
#0: (&key->sem){+++++.}, at: [<ffffffff811a80ae>] keyctl_read_key+0x9c/0xcf

stack backtrace:
Pid: 3637, comm: keyctl Not tainted 2.6.34-rc5-cachefs #18
Call Trace:
 [<ffffffff81051f6c>] lockdep_rcu_dereference+0xaa/0xb2
 [<ffffffff811aa55f>] user_read+0x47/0x91
 [<ffffffff811a80be>] keyctl_read_key+0xac/0xcf
 [<ffffffff811a8a06>] sys_keyctl+0x75/0xb7
 [<ffffffff81001eeb>] system_call_fastpath+0x16/0x1b
---
 security/keys/user_defined.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)
security/keys/user_defined.c +2 -1
index 7c687d5..e9aa079 100644
@@ -199,7 +199,8 @@ long user_read(const struct key *key, char __user *buffer, size_t buflen)
 	struct user_key_payload *upayload;
 	long ret;
 
-	upayload = rcu_dereference(key->payload.data);
+	upayload = rcu_dereference_protected(
+		key->payload.data, rwsem_is_locked(&((struct key *)key)->sem));
 	ret = upayload->datalen;
 
 	/* we can return the data as is */
Signature · 1 line
1.7.0
#3 lore raw

PATCH 3/3 sched: protect __sched_setscheduler() access to cgroups

In reply to Paul E. McKenney

A given task's cgroups structures must remain while that task is running due to reference counting, so this is presumably a false positive. Updated to reflect feedback from Tetsuo Handa.

---
 kernel/sched.c |    6 +++++-
 1 files changed, 5 insertions(+), 1 deletions(-)
kernel/sched.c +5 -1
index 14c44ec..f425a2b 100644
@@ -4575,9 +4575,13 @@ recheck:
 		 * Do not allow realtime tasks into groups that have no runtime
 		 * assigned.
 		 */
+		rcu_read_lock();
 		if (rt_bandwidth_enabled() && rt_policy(policy) &&
-				task_group(p)->rt_bandwidth.rt_runtime == 0)
+				task_group(p)->rt_bandwidth.rt_runtime == 0) {
+			rcu_read_unlock();
 			return -EPERM;
+		}
+		rcu_read_unlock();
 #endif
 
 		retval = security_task_setscheduler(p, policy, param);
Signature · 1 line
1.7.0
#4 lore raw

Re: [PATCH tip/core/urgent 3/3] sched: protect __sched_setscheduler() access to cgroups

Quoted 3 lines · Paul E. McKenney

A given task's cgroups structures must remain while that task is running due to reference counting, so this is presumably a false positive. Updated to reflect feedback from Tetsuo Handa.

I think its not a false positive, I think we can race with the task being placed in another cgroup. We don't hold task_lock() [our other discussion] nor does it hold rq->lock [used by the sched ->attach() method].

That said, we should probably cure the race condition of sched_setscheduler() vs ->attach().

Something like the below perhaps?

---
 kernel/sched.c |   38 ++++++++++++++++++++++++++------------
 1 files changed, 26 insertions(+), 12 deletions(-)
kernel/sched.c +26 -12
index 95eaecc..345df67 100644
@@ -4425,16 +4425,6 @@ recheck:
 	}
 
 	if (user) {
-#ifdef CONFIG_RT_GROUP_SCHED
-		/*
-		 * Do not allow realtime tasks into groups that have no runtime
-		 * assigned.
-		 */
-		if (rt_bandwidth_enabled() && rt_policy(policy) &&
-				task_group(p)->rt_bandwidth.rt_runtime == 0)
-			return -EPERM;
-#endif
-
 		retval = security_task_setscheduler(p, policy, param);
 		if (retval)
 			return retval;
@@ -4450,6 +4440,28 @@ recheck:
 	 * runqueue lock must be held.
 	 */
 	rq = __task_rq_lock(p);
+	retval = 0;
+#ifdef CONFIG_RT_GROUP_SCHED
+	if (user) {
+		/*
+		 * Do not allow realtime tasks into groups that have no runtime
+		 * assigned.
+		 *
+		 * RCU read lock not strictly required but here for PROVE_RCU,
+		 * the task is pinned by holding rq->lock which avoids races
+		 * with ->attach().
+		 */
+		rcu_read_lock();
+		if (rt_bandwidth_enabled() && rt_policy(policy) &&
+				task_group(p)->rt_bandwidth.rt_runtime == 0)
+			retval = -EPERM;
+		rcu_read_unlock();
+
+		if (retval)
+			goto unlock;
+	}
+#endif
+
 	/* recheck policy now with rq lock held */
 	if (unlikely(oldpolicy != -1 && oldpolicy != p->policy)) {
 		policy = oldpolicy = -1;
@@ -4477,12 +4489,14 @@ recheck:
 
 		check_class_changed(rq, p, prev_class, oldprio, running);
 	}
+unlock:
 	__task_rq_unlock(rq);
 	raw_spin_unlock_irqrestore(&p->pi_lock, flags);
 
-	rt_mutex_adjust_pi(p);
+	if (!retval)
+		rt_mutex_adjust_pi(p);
 
-	return 0;
+	return retval;
 }
 
 /**
#5 lore raw

Re: [PATCH tip/core/urgent 3/3] sched: protect __sched_setscheduler() access to cgroups

Quoted 9 lines · Peter Zijlstra
Quoted 3 lines · Paul E. McKenney

A given task's cgroups structures must remain while that task is running due to reference counting, so this is presumably a false positive. Updated to reflect feedback from Tetsuo Handa.

I think its not a false positive, I think we can race with the task being placed in another cgroup. We don't hold task_lock() [our other discussion] nor does it hold rq->lock [used by the sched ->attach() method].

Ah, I am dropping this patch then.

Ingo, please accept my apologies for the confusion submitting it too soon!

Thanx, Paul

Quoted 79 lines

That said, we should probably cure the race condition of sched_setscheduler() vs ->attach().

Something like the below perhaps?

---
 kernel/sched.c |   38 ++++++++++++++++++++++++++------------
 1 files changed, 26 insertions(+), 12 deletions(-)
kernel/sched.c +26 -12
index 95eaecc..345df67 100644
@@ -4425,16 +4425,6 @@ recheck:
 	}
 
 	if (user) {
-#ifdef CONFIG_RT_GROUP_SCHED
-		/*
-		 * Do not allow realtime tasks into groups that have no runtime
-		 * assigned.
-		 */
-		if (rt_bandwidth_enabled() && rt_policy(policy) &&
-				task_group(p)->rt_bandwidth.rt_runtime == 0)
-			return -EPERM;
-#endif
-
 		retval = security_task_setscheduler(p, policy, param);
 		if (retval)
 			return retval;
@@ -4450,6 +4440,28 @@ recheck:
 	 * runqueue lock must be held.
 	 */
 	rq = __task_rq_lock(p);
+	retval = 0;
+#ifdef CONFIG_RT_GROUP_SCHED
+	if (user) {
+		/*
+		 * Do not allow realtime tasks into groups that have no runtime
+		 * assigned.
+		 *
+		 * RCU read lock not strictly required but here for PROVE_RCU,
+		 * the task is pinned by holding rq->lock which avoids races
+		 * with ->attach().
+		 */
+		rcu_read_lock();
+		if (rt_bandwidth_enabled() && rt_policy(policy) &&
+				task_group(p)->rt_bandwidth.rt_runtime == 0)
+			retval = -EPERM;
+		rcu_read_unlock();
+
+		if (retval)
+			goto unlock;
+	}
+#endif
+
 	/* recheck policy now with rq lock held */
 	if (unlikely(oldpolicy != -1 && oldpolicy != p->policy)) {
 		policy = oldpolicy = -1;
@@ -4477,12 +4489,14 @@ recheck:
 
 		check_class_changed(rq, p, prev_class, oldprio, running);
 	}
+unlock:
 	__task_rq_unlock(rq);
 	raw_spin_unlock_irqrestore(&p->pi_lock, flags);
 
-	rt_mutex_adjust_pi(p);
+	if (!retval)
+		rt_mutex_adjust_pi(p);
 
-	return 0;
+	return retval;
 }
 
 /**