]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - net/bridge/br_multicast.c
inetpeer: fix data-race in inet_putpeer / inet_putpeer
[mirror_ubuntu-bionic-kernel.git] / net / bridge / br_multicast.c
index cb4729539b82d1b3013b9d6ca60f20ded0062a2d..229e081d31128221004834080ab91492cb859ea1 100644 (file)
@@ -1147,6 +1147,7 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
        int type;
        int err = 0;
        __be32 group;
+       u16 nsrcs;
 
        ih = igmpv3_report_hdr(skb);
        num = ntohs(ih->ngrec);
@@ -1160,8 +1161,9 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
                grec = (void *)(skb->data + len - sizeof(*grec));
                group = grec->grec_mca;
                type = grec->grec_type;
+               nsrcs = ntohs(grec->grec_nsrcs);
 
-               len += ntohs(grec->grec_nsrcs) * 4;
+               len += nsrcs * 4;
                if (!pskb_may_pull(skb, len))
                        return -EINVAL;
 
@@ -1182,7 +1184,7 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
                src = eth_hdr(skb)->h_source;
                if ((type == IGMPV3_CHANGE_TO_INCLUDE ||
                     type == IGMPV3_MODE_IS_INCLUDE) &&
-                   ntohs(grec->grec_nsrcs) == 0) {
+                   nsrcs == 0) {
                        br_ip4_multicast_leave_group(br, port, group, vid, src);
                } else {
                        err = br_ip4_multicast_add_group(br, port, group, vid,
@@ -1217,23 +1219,26 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
        len = skb_transport_offset(skb) + sizeof(*icmp6h);
 
        for (i = 0; i < num; i++) {
-               __be16 *nsrcs, _nsrcs;
-
-               nsrcs = skb_header_pointer(skb,
-                                          len + offsetof(struct mld2_grec,
-                                                         grec_nsrcs),
-                                          sizeof(_nsrcs), &_nsrcs);
-               if (!nsrcs)
+               __be16 *_nsrcs, __nsrcs;
+               u16 nsrcs;
+
+               _nsrcs = skb_header_pointer(skb,
+                                           len + offsetof(struct mld2_grec,
+                                                          grec_nsrcs),
+                                           sizeof(__nsrcs), &__nsrcs);
+               if (!_nsrcs)
                        return -EINVAL;
 
+               nsrcs = ntohs(*_nsrcs);
+
                if (!pskb_may_pull(skb,
                                   len + sizeof(*grec) +
-                                  sizeof(struct in6_addr) * ntohs(*nsrcs)))
+                                  sizeof(struct in6_addr) * nsrcs))
                        return -EINVAL;
 
                grec = (struct mld2_grec *)(skb->data + len);
                len += sizeof(*grec) +
-                      sizeof(struct in6_addr) * ntohs(*nsrcs);
+                      sizeof(struct in6_addr) * nsrcs;
 
                /* We treat these as MLDv1 reports for now. */
                switch (grec->grec_type) {
@@ -1252,7 +1257,7 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
                src = eth_hdr(skb)->h_source;
                if ((grec->grec_type == MLD2_CHANGE_TO_INCLUDE ||
                     grec->grec_type == MLD2_MODE_IS_INCLUDE) &&
-                   ntohs(*nsrcs) == 0) {
+                   nsrcs == 0) {
                        br_ip6_multicast_leave_group(br, port, &grec->grec_mca,
                                                     vid, src);
                } else {
@@ -1507,7 +1512,6 @@ static int br_ip6_multicast_query(struct net_bridge *br,
                                  struct sk_buff *skb,
                                  u16 vid)
 {
-       const struct ipv6hdr *ip6h = ipv6_hdr(skb);
        struct mld_msg *mld;
        struct net_bridge_mdb_entry *mp;
        struct mld2_query *mld2q;
@@ -1551,7 +1555,7 @@ static int br_ip6_multicast_query(struct net_bridge *br,
 
        if (is_general_query) {
                saddr.proto = htons(ETH_P_IPV6);
-               saddr.u.ip6 = ip6h->saddr;
+               saddr.u.ip6 = ipv6_hdr(skb)->saddr;
 
                br_multicast_query_received(br, port, &br->ip6_other_query,
                                            &saddr, max_delay);
@@ -1619,6 +1623,9 @@ br_multicast_leave_group(struct net_bridge *br,
                        if (!br_port_group_equal(p, port, src))
                                continue;
 
+                       if (p->flags & MDB_PG_FLAGS_PERMANENT)
+                               break;
+
                        rcu_assign_pointer(*pp, p->next);
                        hlist_del_init(&p->mglist);
                        del_timer(&p->timer);
@@ -2154,7 +2161,8 @@ static void br_multicast_start_querier(struct net_bridge *br,
 
        __br_multicast_open(br, query);
 
-       list_for_each_entry(port, &br->port_list, list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(port, &br->port_list, list) {
                if (port->state == BR_STATE_DISABLED ||
                    port->state == BR_STATE_BLOCKING)
                        continue;
@@ -2166,6 +2174,7 @@ static void br_multicast_start_querier(struct net_bridge *br,
                        br_multicast_enable(&port->ip6_own_query);
 #endif
        }
+       rcu_read_unlock();
 }
 
 int br_multicast_toggle(struct net_bridge *br, unsigned long val)