diff options
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r-- | security/selinux/hooks.c | 28 |
1 files changed, 23 insertions, 5 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 419491d8e7d2..57b0b49f4e6e 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -234,6 +234,14 @@ static int inode_alloc_security(struct inode *inode) | |||
234 | return 0; | 234 | return 0; |
235 | } | 235 | } |
236 | 236 | ||
237 | static void inode_free_rcu(struct rcu_head *head) | ||
238 | { | ||
239 | struct inode_security_struct *isec; | ||
240 | |||
241 | isec = container_of(head, struct inode_security_struct, rcu); | ||
242 | kmem_cache_free(sel_inode_cache, isec); | ||
243 | } | ||
244 | |||
237 | static void inode_free_security(struct inode *inode) | 245 | static void inode_free_security(struct inode *inode) |
238 | { | 246 | { |
239 | struct inode_security_struct *isec = inode->i_security; | 247 | struct inode_security_struct *isec = inode->i_security; |
@@ -244,8 +252,16 @@ static void inode_free_security(struct inode *inode) | |||
244 | list_del_init(&isec->list); | 252 | list_del_init(&isec->list); |
245 | spin_unlock(&sbsec->isec_lock); | 253 | spin_unlock(&sbsec->isec_lock); |
246 | 254 | ||
247 | inode->i_security = NULL; | 255 | /* |
248 | kmem_cache_free(sel_inode_cache, isec); | 256 | * The inode may still be referenced in a path walk and |
257 | * a call to selinux_inode_permission() can be made | ||
258 | * after inode_free_security() is called. Ideally, the VFS | ||
259 | * wouldn't do this, but fixing that is a much harder | ||
260 | * job. For now, simply free the i_security via RCU, and | ||
261 | * leave the current inode->i_security pointer intact. | ||
262 | * The inode will be freed after the RCU grace period too. | ||
263 | */ | ||
264 | call_rcu(&isec->rcu, inode_free_rcu); | ||
249 | } | 265 | } |
250 | 266 | ||
251 | static int file_alloc_security(struct file *file) | 267 | static int file_alloc_security(struct file *file) |
@@ -4334,8 +4350,10 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) | |||
4334 | } | 4350 | } |
4335 | err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER, | 4351 | err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER, |
4336 | PEER__RECV, &ad); | 4352 | PEER__RECV, &ad); |
4337 | if (err) | 4353 | if (err) { |
4338 | selinux_netlbl_err(skb, err, 0); | 4354 | selinux_netlbl_err(skb, err, 0); |
4355 | return err; | ||
4356 | } | ||
4339 | } | 4357 | } |
4340 | 4358 | ||
4341 | if (secmark_active) { | 4359 | if (secmark_active) { |
@@ -5586,11 +5604,11 @@ static int selinux_setprocattr(struct task_struct *p, | |||
5586 | /* Check for ptracing, and update the task SID if ok. | 5604 | /* Check for ptracing, and update the task SID if ok. |
5587 | Otherwise, leave SID unchanged and fail. */ | 5605 | Otherwise, leave SID unchanged and fail. */ |
5588 | ptsid = 0; | 5606 | ptsid = 0; |
5589 | task_lock(p); | 5607 | rcu_read_lock(); |
5590 | tracer = ptrace_parent(p); | 5608 | tracer = ptrace_parent(p); |
5591 | if (tracer) | 5609 | if (tracer) |
5592 | ptsid = task_sid(tracer); | 5610 | ptsid = task_sid(tracer); |
5593 | task_unlock(p); | 5611 | rcu_read_unlock(); |
5594 | 5612 | ||
5595 | if (tracer) { | 5613 | if (tracer) { |
5596 | error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, | 5614 | error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, |