xt_owner: fix race with sock_orphan()
By using a read_lock on sk->sk_callback_lock we can avoid Oops
due to someone else calling sock_orphan() at same time on
another CPU, e. g. when using TPROXY.
Signed-off-by: Holger Eitzenberger <holger@eitzenberger.org>
===================================================================
@@ -14,6 +14,7 @@
#include <linux/skbuff.h>
#include <linux/file.h>
#include <net/sock.h>
+#include <net/tcp.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_owner.h>
@@ -32,21 +33,32 @@ static bool
owner_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_owner_match_info *info = par->matchinfo;
+ struct sock *sk = skb->sk;
const struct file *filp;
- if (skb->sk == NULL || skb->sk->sk_socket == NULL)
+ if (sk == NULL || sk->sk_state == TCP_TIME_WAIT)
return (info->match ^ info->invert) == 0;
- else if (info->match & info->invert & XT_OWNER_SOCKET)
+
+ read_lock_bh(&sk->sk_callback_lock);
+
+ if (sk->sk_socket == NULL) {
+ read_unlock_bh(&sk->sk_callback_lock);
+ return (info->match ^ info->invert) == 0;
+ }
+
+ if (info->match & info->invert & XT_OWNER_SOCKET)
/*
* Socket exists but user wanted ! --socket-exists.
* (Single ampersands intended.)
*/
- return false;
+ goto out_false;
- filp = skb->sk->sk_socket->file;
- if (filp == NULL)
+ filp = sk->sk_socket->file;
+ if (filp == NULL) {
+ read_unlock_bh(&sk->sk_callback_lock);
return ((info->match ^ info->invert) &
(XT_OWNER_UID | XT_OWNER_GID)) == 0;
+ }
if (info->match & XT_OWNER_UID) {
kuid_t uid_min = make_kuid(&init_user_ns, info->uid_min);
@@ -54,7 +66,7 @@ owner_mt(const struct sk_buff *skb, stru
if ((uid_gte(filp->f_cred->fsuid, uid_min) &&
uid_lte(filp->f_cred->fsuid, uid_max)) ^
!(info->invert & XT_OWNER_UID))
- return false;
+ goto out_false;
}
if (info->match & XT_OWNER_GID) {
@@ -63,10 +75,16 @@ owner_mt(const struct sk_buff *skb, stru
if ((gid_gte(filp->f_cred->fsgid, gid_min) &&
gid_lte(filp->f_cred->fsgid, gid_max)) ^
!(info->invert & XT_OWNER_GID))
- return false;
+ goto out_false;
}
+ read_unlock_bh(&sk->sk_callback_lock);
+
return true;
+
+out_false:
+ read_unlock_bh(&sk->sk_callback_lock);
+ return false;
}
static struct xt_match owner_mt_reg __read_mostly = {