ipv6: handle -EFAULT from skb_copy_bits
[pandora-kernel.git] / net / ipv6 / raw.c
index 331af3b..ce501a7 100644 (file)
@@ -107,21 +107,20 @@ found:
  *     0 - deliver
  *     1 - block
  */
-static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb)
+static int icmpv6_filter(const struct sock *sk, const struct sk_buff *skb)
 {
-       struct icmp6hdr *icmph;
-       struct raw6_sock *rp = raw6_sk(sk);
-
-       if (pskb_may_pull(skb, sizeof(struct icmp6hdr))) {
-               __u32 *data = &rp->filter.data[0];
-               int bit_nr;
+       struct icmp6hdr *_hdr;
+       const struct icmp6hdr *hdr;
 
-               icmph = (struct icmp6hdr *) skb->data;
-               bit_nr = icmph->icmp6_type;
+       hdr = skb_header_pointer(skb, skb_transport_offset(skb),
+                                sizeof(_hdr), &_hdr);
+       if (hdr) {
+               const __u32 *data = &raw6_sk(sk)->filter.data[0];
+               unsigned int type = hdr->icmp6_type;
 
-               return (data[bit_nr >> 5] & (1 << (bit_nr & 31))) != 0;
+               return (data[type >> 5] & (1U << (type & 31))) != 0;
        }
-       return 0;
+       return 1;
 }
 
 #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
@@ -131,7 +130,7 @@ static mh_filter_t __rcu *mh_filter __read_mostly;
 
 int rawv6_mh_filter_register(mh_filter_t filter)
 {
-       RCU_INIT_POINTER(mh_filter, filter);
+       rcu_assign_pointer(mh_filter, filter);
        return 0;
 }
 EXPORT_SYMBOL(rawv6_mh_filter_register);
@@ -457,14 +456,11 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk,
        if (flags & MSG_OOB)
                return -EOPNOTSUPP;
 
-       if (addr_len)
-               *addr_len=sizeof(*sin6);
-
        if (flags & MSG_ERRQUEUE)
-               return ipv6_recv_error(sk, msg, len);
+               return ipv6_recv_error(sk, msg, len, addr_len);
 
        if (np->rxpmtu && np->rxopt.bits.rxpmtu)
-               return ipv6_recv_rxpmtu(sk, msg, len);
+               return ipv6_recv_rxpmtu(sk, msg, len, addr_len);
 
        skb = skb_recv_datagram(sk, flags, noblock, &err);
        if (!skb)
@@ -499,6 +495,7 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk,
                sin6->sin6_scope_id = 0;
                if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
                        sin6->sin6_scope_id = IP6CB(skb)->iif;
+               *addr_len = sizeof(*sin6);
        }
 
        sock_recv_ts_and_drops(msg, sk, skb);
@@ -579,8 +576,11 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
        }
 
        offset += skb_transport_offset(skb);
-       if (skb_copy_bits(skb, offset, &csum, 2))
-               BUG();
+       err = skb_copy_bits(skb, offset, &csum, 2);
+       if (err < 0) {
+               ip6_flush_pending_frames(sk);
+               goto out;
+       }
 
        /* in case cksum was not initialized */
        if (unlikely(csum))
@@ -610,6 +610,8 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
        struct sk_buff *skb;
        int err;
        struct rt6_info *rt = (struct rt6_info *)*dstp;
+       int hlen = LL_RESERVED_SPACE(rt->dst.dev);
+       int tlen = rt->dst.dev->needed_tailroom;
 
        if (length > rt->dst.dev->mtu) {
                ipv6_local_error(sk, EMSGSIZE, fl6, rt->dst.dev->mtu);
@@ -619,11 +621,11 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
                goto out;
 
        skb = sock_alloc_send_skb(sk,
-                                 length + LL_ALLOCATED_SPACE(rt->dst.dev) + 15,
+                                 length + hlen + tlen + 15,
                                  flags & MSG_DONTWAIT, &err);
        if (skb == NULL)
                goto error;
-       skb_reserve(skb, LL_RESERVED_SPACE(rt->dst.dev));
+       skb_reserve(skb, hlen);
 
        skb->priority = sk->sk_priority;
        skb->mark = sk->sk_mark;
@@ -725,6 +727,7 @@ static int rawv6_probe_proto_opt(struct flowi6 *fl6, struct msghdr *msg)
 static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
                   struct msghdr *msg, size_t len)
 {
+       struct ipv6_txoptions *opt_to_free = NULL;
        struct ipv6_txoptions opt_space;
        struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name;
        struct in6_addr *daddr, *final_p, final;
@@ -831,8 +834,10 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
                if (!(opt->opt_nflen|opt->opt_flen))
                        opt = NULL;
        }
-       if (opt == NULL)
-               opt = np->opt;
+       if (!opt) {
+               opt = txopt_get(np);
+               opt_to_free = opt;
+       }
        if (flowlabel)
                opt = fl6_merge_options(&opt_space, flowlabel, opt);
        opt = ipv6_fixup_options(&opt_space, opt);
@@ -897,6 +902,7 @@ done:
        dst_release(dst);
 out:
        fl6_sock_release(flowlabel);
+       txopt_put(opt_to_free);
        return err<0?err:len;
 do_confirm:
        dst_confirm(dst);