Skip to contentSkip to conversation

SMACK: Add missing rcu_read_lock/unlock for process capability walk.

PATCH NAK Andi Kleen 3 participants 8 messages Apr 20, 2011 – Apr 22, 2011

Subsystems (2)
lore
Share & tools
lore

Andi Kleen

author
#0 lore raw

PATCH 1/1 SMACK: Add missing rcu_read_lock/unlock for process capability walk.

From: Andi Kleen <[email protected]>

smk_access_entry does a RCU list walk for a list shared with other threads. It relies on the caller doing rcu_read_lock(). One caller forgot to do to this, which could lead to races on preemptible kernels.

Move the rcu_read_lock() into smk_access_entry instead.

---
 security/smack/smack_access.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
security/smack/smack_access.c +2 -2
index 7b0d3b3..43b20f3 100644
@@ -92,6 +92,7 @@ int smk_access_entry(char *subject_label, char *object_label,
 	int may = -ENOENT;
 	struct smack_rule *srp;
 
+	rcu_read_lock();
 	list_for_each_entry_rcu(srp, rule_list, list) {
 		if (srp->smk_subject == subject_label ||
 		    strcmp(srp->smk_subject, subject_label) == 0) {
@@ -102,6 +103,7 @@ int smk_access_entry(char *subject_label, char *object_label,
 			}
 		}
 	}
+	rcu_read_unlock();
 
 	return may;
 }
@@ -184,9 +186,7 @@ int smk_access_flags(char *subject_label, char *object_label, int request,
 	 * good. A negative response from smk_access_entry()
 	 * indicates there is no entry for this pair.
 	 */
-	rcu_read_lock();
 	may = smk_access_entry(subject_label, object_label, &smack_rule_list);
-	rcu_read_unlock();
 
 	if (may > 0 && (request & may) == request)
 		goto out_audit;
Signature · 1 line
1.7.4.2
maintainer Nacked-by on v1 1/1 NAK
#1 lore raw
Quoted 8 lines · Andi Kleen

From: Andi Kleen <[email protected]>

smk_access_entry does a RCU list walk for a list shared with other threads. It relies on the caller doing rcu_read_lock(). One caller forgot to do to this, which could lead to races on preemptible kernels.

Move the rcu_read_lock() into smk_access_entry instead.

The lock was moved out of smk_access_entry in support of the processing done in the smack_mmap_file() hook. Where do you see a potential race, and which caller "forgot" to do the lock?

Thank you.

Quoted 35 lines
---
 security/smack/smack_access.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
security/smack/smack_access.c +2 -2
index 7b0d3b3..43b20f3 100644
@@ -92,6 +92,7 @@ int smk_access_entry(char *subject_label, char *object_label,
 	int may = -ENOENT;
 	struct smack_rule *srp;
 
+	rcu_read_lock();
 	list_for_each_entry_rcu(srp, rule_list, list) {
 		if (srp->smk_subject == subject_label ||
 		    strcmp(srp->smk_subject, subject_label) == 0) {
@@ -102,6 +103,7 @@ int smk_access_entry(char *subject_label, char *object_label,
 			}
 		}
 	}
+	rcu_read_unlock();
 
 	return may;
 }
@@ -184,9 +186,7 @@ int smk_access_flags(char *subject_label, char *object_label, int request,
 	 * good. A negative response from smk_access_entry()
 	 * indicates there is no entry for this pair.
 	 */
-	rcu_read_lock();
 	may = smk_access_entry(subject_label, object_label, &smack_rule_list);
-	rcu_read_unlock();
 
 	if (may > 0 && (request & may) == request)
 		goto out_audit;
#2 lore raw
Quoted 8 lines · Casey Schaufler
Quoted 7 lines · Andi Kleen

From: Andi Kleen <[email protected]>

smk_access_entry does a RCU list walk for a list shared with other threads. It relies on the caller doing rcu_read_lock(). One caller forgot to do to this, which could lead to races on preemptible kernels.

Quoted 7 lines
Quoted 1 line

Move the rcu_read_lock() into smk_access_entry instead.

The lock was moved out of smk_access_entry in support of the processing done in the smack_mmap_file() hook. Where do you see a potential race, and which caller "forgot" to do the lock?

There are two callers and only one takes it.
The one that doesn't take it is smk_curacc.

I checked the callers of that and there is no rcu_read_lock() in those

As far as I understand the cred which holds this list is shared between threads and other threads can modify it. Which means it needs RCU read lock protection.

-Andi

maintainer
#3 lore raw
Quoted 9 lines · Andi Kleen
Quoted 8 lines · Casey Schaufler
Quoted 7 lines · Andi Kleen

From: Andi Kleen <[email protected]>

smk_access_entry does a RCU list walk for a list shared with other threads. It relies on the caller doing rcu_read_lock(). One caller forgot to do to this, which could lead to races on preemptible kernels.

Quoted 7 lines
Quoted 6 lines
Quoted 1 line

Move the rcu_read_lock() into smk_access_entry instead.

The lock was moved out of smk_access_entry in support of the processing done in the smack_mmap_file() hook. Where do you see a potential race, and which caller "forgot" to do the lock?

There are two callers and only one takes it.

There are two callers in smack_access.c.
There are four more in smack_lsm.c

Quoted 1 line

The one that doesn't take it is smk_curacc.

The call in smk_curacc() is using the task local list, not the system list.

Quoted 5 lines

I checked the callers of that and there is no rcu_read_lock() in those

As far as I understand the cred which holds this list is shared between threads and other threads can modify it. Which means it needs RCU read lock protection.

The global list, yes. The task specific list, no. Modifying the local list is like any other modification of the cred structure and requires the cred be copied.

Moving the lock into smk_access_entry() would introduce a potential deadlock in smack_mmap_file. There is a bit of convolution in the mmap hook that requires looking at the list in a way that does not allow the locking to be embedded where it used to be.

Quoted 8 lines

-Andi
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

author
#4 lore raw
Quoted 3 lines

The global list, yes. The task specific list, no. Modifying the local list is like any other modification of the cred structure and requires the cred be copied.

But you still need to free it eventually right? And that freeing will need RCU on the reader.

Quoted 5 lines

Moving the lock into smk_access_entry() would introduce a potential deadlock in smack_mmap_file. There is a bit of convolution in the mmap hook that requires looking at the list in a way that does not allow the locking to be embedded where it used to be.

rcu_read_lock() is just a per task counter and nests fine and never deadlocks.

-Andi

maintainer
#5 lore raw
Quoted 5 lines · Andi Kleen
Quoted 3 lines

The global list, yes. The task specific list, no. Modifying the local list is like any other modification of the cred structure and requires the cred be copied.

But you still need to free it eventually right? And that freeing will need RCU on the reader.

Entries are never freed from the global list. Someone is working on a patch to do that, but is running into - wait for it - locking issues.

Entries on the local lists are only freed when the task exits.

author
#6 lore raw
Quoted 10 lines · Casey Schaufler
Quoted 5 lines · Andi Kleen
Quoted 3 lines

The global list, yes. The task specific list, no. Modifying the local list is like any other modification of the cred structure and requires the cred be copied.

But you still need to free it eventually right? And that freeing will need RCU on the reader.

Entries are never freed from the global list. Someone is working on a patch to do that, but is running into - wait for it - locking issues.

Then why do you use rcu_read_lock() at all?

You can drop all the rcu_read_lock()s and probably the other *_rcu list accesses then. And my patch is indeed not needed.

Quoted 1 line

Entries on the local lists are only freed when the task exits.

You mean the last user of the cred?

-Andi

Signature · 1 line
[email protected] -- Speaking for myself only.
maintainer
#7 lore raw
Quoted 11 lines · Andi Kleen
Quoted 9 lines · Casey Schaufler
Quoted 5 lines · Andi Kleen
Quoted 3 lines

The global list, yes. The task specific list, no. Modifying the local list is like any other modification of the cred structure and requires the cred be copied.

But you still need to free it eventually right? And that freeing will need RCU on the reader.

Entries are never freed from the global list. Someone is working on a patch to do that, but is running into - wait for it - locking issues.

Then why do you use rcu_read_lock() at all?

Because the entries can be modified in place.

Quoted 5 lines

You can drop all the rcu_read_lock()s and probably the other *_rcu list accesses then. And my patch is indeed not needed.

Quoted 1 line

Entries on the local lists are only freed when the task exits.

You mean the last user of the cred?

Yes.

Quoted 2 lines

-Andi