Merge remote-tracking branches 'asoc/fix/tlv320aic3x' and 'asoc/fix/wm8962' into...
[linux-drm-fsl-dcu.git] / net / netlink / af_netlink.c
index 7f86d3b550601839f730d4c9f15951f5410e3fd4..8f060d7f9a0e107a410d3ffe71722f49059f7bc8 100644 (file)
@@ -125,6 +125,24 @@ static inline u32 netlink_group_mask(u32 group)
        return group ? 1 << (group - 1) : 0;
 }
 
+static struct sk_buff *netlink_to_full_skb(const struct sk_buff *skb,
+                                          gfp_t gfp_mask)
+{
+       unsigned int len = skb_end_offset(skb);
+       struct sk_buff *new;
+
+       new = alloc_skb(len, gfp_mask);
+       if (new == NULL)
+               return NULL;
+
+       NETLINK_CB(new).portid = NETLINK_CB(skb).portid;
+       NETLINK_CB(new).dst_group = NETLINK_CB(skb).dst_group;
+       NETLINK_CB(new).creds = NETLINK_CB(skb).creds;
+
+       memcpy(skb_put(new, len), skb->data, len);
+       return new;
+}
+
 int netlink_add_tap(struct netlink_tap *nt)
 {
        if (unlikely(nt->dev->type != ARPHRD_NETLINK))
@@ -206,7 +224,11 @@ static int __netlink_deliver_tap_skb(struct sk_buff *skb,
        int ret = -ENOMEM;
 
        dev_hold(dev);
-       nskb = skb_clone(skb, GFP_ATOMIC);
+
+       if (netlink_skb_is_mmaped(skb) || is_vmalloc_addr(skb->head))
+               nskb = netlink_to_full_skb(skb, GFP_ATOMIC);
+       else
+               nskb = skb_clone(skb, GFP_ATOMIC);
        if (nskb) {
                nskb->dev = dev;
                nskb->protocol = htons((u16) sk->sk_protocol);
@@ -279,11 +301,6 @@ static void netlink_rcv_wake(struct sock *sk)
 }
 
 #ifdef CONFIG_NETLINK_MMAP
-static bool netlink_skb_is_mmaped(const struct sk_buff *skb)
-{
-       return NETLINK_CB(skb).flags & NETLINK_SKB_MMAPED;
-}
-
 static bool netlink_rx_is_mmaped(struct sock *sk)
 {
        return nlk_sk(sk)->rx_ring.pg_vec != NULL;
@@ -846,7 +863,6 @@ static void netlink_ring_set_copied(struct sock *sk, struct sk_buff *skb)
 }
 
 #else /* CONFIG_NETLINK_MMAP */
-#define netlink_skb_is_mmaped(skb)     false
 #define netlink_rx_is_mmaped(sk)       false
 #define netlink_tx_is_mmaped(sk)       false
 #define netlink_mmap                   sock_no_mmap
@@ -1094,8 +1110,8 @@ static int netlink_insert(struct sock *sk, u32 portid)
 
        lock_sock(sk);
 
-       err = -EBUSY;
-       if (nlk_sk(sk)->portid)
+       err = nlk_sk(sk)->portid == portid ? 0 : -EBUSY;
+       if (nlk_sk(sk)->bound)
                goto err;
 
        err = -ENOMEM;
@@ -1115,10 +1131,14 @@ static int netlink_insert(struct sock *sk, u32 portid)
                        err = -EOVERFLOW;
                if (err == -EEXIST)
                        err = -EADDRINUSE;
-               nlk_sk(sk)->portid = 0;
                sock_put(sk);
+               goto err;
        }
 
+       /* We need to ensure that the socket is hashed and visible. */
+       smp_wmb();
+       nlk_sk(sk)->bound = portid;
+
 err:
        release_sock(sk);
        return err;
@@ -1503,6 +1523,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
        struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr;
        int err;
        long unsigned int groups = nladdr->nl_groups;
+       bool bound;
 
        if (addr_len < sizeof(struct sockaddr_nl))
                return -EINVAL;
@@ -1519,9 +1540,14 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
                        return err;
        }
 
-       if (nlk->portid)
+       bound = nlk->bound;
+       if (bound) {
+               /* Ensure nlk->portid is up-to-date. */
+               smp_rmb();
+
                if (nladdr->nl_pid != nlk->portid)
                        return -EINVAL;
+       }
 
        if (nlk->netlink_bind && groups) {
                int group;
@@ -1537,7 +1563,10 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
                }
        }
 
-       if (!nlk->portid) {
+       /* No need for barriers here as we return to user-space without
+        * using any of the bound attributes.
+        */
+       if (!bound) {
                err = nladdr->nl_pid ?
                        netlink_insert(sk, nladdr->nl_pid) :
                        netlink_autobind(sock);
@@ -1585,7 +1614,10 @@ static int netlink_connect(struct socket *sock, struct sockaddr *addr,
            !netlink_allowed(sock, NL_CFG_F_NONROOT_SEND))
                return -EPERM;
 
-       if (!nlk->portid)
+       /* No need for barriers here as we return to user-space without
+        * using any of the bound attributes.
+        */
+       if (!nlk->bound)
                err = netlink_autobind(sock);
 
        if (err == 0) {
@@ -2426,10 +2458,13 @@ static int netlink_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
                dst_group = nlk->dst_group;
        }
 
-       if (!nlk->portid) {
+       if (!nlk->bound) {
                err = netlink_autobind(sock);
                if (err)
                        goto out;
+       } else {
+               /* Ensure nlk is hashed and visible. */
+               smp_rmb();
        }
 
        /* It's a really convoluted way for userland to ask for mmaped