diff options
author | James Morris <jmorris@redhat.com> | 2004-06-17 18:12:41 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-06-17 18:12:41 -0700 |
commit | e528561c006fe2e965c97afb0d8432973876b3cd (patch) | |
tree | fd19b34f1a079ad727ffa0eeb65c74325ca20e90 /security | |
parent | d74fb703951e8643b431a5301fdcc68ccebf9f1f (diff) | |
download | history-e528561c006fe2e965c97afb0d8432973876b3cd.tar.gz |
[PATCH] Fix sock_orphan race.
The patch below fixes a race between sock_orphan() and
selinux_socket_sock_rcv_skb() which can lead to a null pointer deref oops
under heavy load. The sk_callback_lock is used in the patch to synchronize
access to the incoming socket's inode security state.
This patch has been under test in the Fedora kernel for over a month
without incident.
Author: Stephen Smalley <sds@epoch.ncsc.mil>
Signed-off-by: James Morris <jmorris@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'security')
-rw-r--r-- | security/selinux/hooks.c | 37 |
1 files changed, 21 insertions, 16 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c447232c6ae4c7..fdc5cecddec6d2 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3174,12 +3174,12 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) char *addrp; int len, err = 0; u32 netif_perm, node_perm, node_sid, recv_perm = 0; + u32 sock_sid = 0; + u16 sock_class = 0; struct socket *sock; - struct inode *inode; struct net_device *dev; struct sel_netif *netif; struct netif_security_struct *nsec; - struct inode_security_struct *isec; struct avc_audit_data ad; family = sk->sk_family; @@ -3190,15 +3190,21 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP)) family = PF_INET; - sock = sk->sk_socket; - - /* TCP control messages don't always have a socket. */ - if (!sock) - goto out; - - inode = SOCK_INODE(sock); - if (!inode) - goto out; + read_lock_bh(&sk->sk_callback_lock); + sock = sk->sk_socket; + if (sock) { + struct inode *inode; + inode = SOCK_INODE(sock); + if (inode) { + struct inode_security_struct *isec; + isec = inode->i_security; + sock_sid = isec->sid; + sock_class = isec->sclass; + } + } + read_unlock_bh(&sk->sk_callback_lock); + if (!sock_sid) + goto out; dev = skb->dev; if (!dev) @@ -3211,9 +3217,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) } nsec = &netif->nsec; - isec = inode->i_security; - switch (isec->sclass) { + switch (sock_class) { case SECCLASS_UDP_SOCKET: netif_perm = NETIF__UDP_RECV; node_perm = NODE__UDP_RECV; @@ -3242,7 +3247,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) goto out; } - err = avc_has_perm(isec->sid, nsec->if_sid, SECCLASS_NETIF, + err = avc_has_perm(sock_sid, nsec->if_sid, SECCLASS_NETIF, netif_perm, &nsec->avcr, &ad); sel_netif_put(netif); if (err) @@ -3253,7 +3258,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) if (err) goto out; - err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, node_perm, NULL, &ad); + err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, NULL, &ad); if (err) goto out; @@ -3267,7 +3272,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) if (err) goto out; - err = avc_has_perm(isec->sid, port_sid, isec->sclass, + err = avc_has_perm(sock_sid, port_sid, sock_class, recv_perm, NULL, &ad); } out: |