Commit e528561c authored by James Morris's avatar James Morris Committed by Linus Torvalds

[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: default avatarJames Morris <jmorris@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent d74fb703
...@@ -3174,12 +3174,12 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) ...@@ -3174,12 +3174,12 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
char *addrp; char *addrp;
int len, err = 0; int len, err = 0;
u32 netif_perm, node_perm, node_sid, recv_perm = 0; u32 netif_perm, node_perm, node_sid, recv_perm = 0;
u32 sock_sid = 0;
u16 sock_class = 0;
struct socket *sock; struct socket *sock;
struct inode *inode;
struct net_device *dev; struct net_device *dev;
struct sel_netif *netif; struct sel_netif *netif;
struct netif_security_struct *nsec; struct netif_security_struct *nsec;
struct inode_security_struct *isec;
struct avc_audit_data ad; struct avc_audit_data ad;
family = sk->sk_family; family = sk->sk_family;
...@@ -3190,14 +3190,20 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) ...@@ -3190,14 +3190,20 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP)) if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP))
family = PF_INET; family = PF_INET;
read_lock_bh(&sk->sk_callback_lock);
sock = sk->sk_socket; sock = sk->sk_socket;
if (sock) {
/* TCP control messages don't always have a socket. */ struct inode *inode;
if (!sock)
goto out;
inode = SOCK_INODE(sock); inode = SOCK_INODE(sock);
if (!inode) 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; goto out;
dev = skb->dev; dev = skb->dev;
...@@ -3211,9 +3217,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) ...@@ -3211,9 +3217,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
} }
nsec = &netif->nsec; nsec = &netif->nsec;
isec = inode->i_security;
switch (isec->sclass) { switch (sock_class) {
case SECCLASS_UDP_SOCKET: case SECCLASS_UDP_SOCKET:
netif_perm = NETIF__UDP_RECV; netif_perm = NETIF__UDP_RECV;
node_perm = NODE__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) ...@@ -3242,7 +3247,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
goto out; 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); netif_perm, &nsec->avcr, &ad);
sel_netif_put(netif); sel_netif_put(netif);
if (err) if (err)
...@@ -3253,7 +3258,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) ...@@ -3253,7 +3258,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (err) if (err)
goto out; 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) if (err)
goto out; goto out;
...@@ -3267,7 +3272,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) ...@@ -3267,7 +3272,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (err) if (err)
goto out; 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); recv_perm, NULL, &ad);
} }
out: out:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment