diff options
Diffstat (limited to 'net')
64 files changed, 1037 insertions, 216 deletions
diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index ae39ceaa2e29..c40c9e02391b 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -1066,6 +1066,9 @@ out: * @skb: the buffer containing the packet to extract the VID from * @hdr_size: the size of the batman-adv header encapsulating the packet * + * The caller must ensure that at least @hdr_size + ETH_HLEN bytes are + * accessible after skb->data. + * * Return: If the packet embedded in the skb is vlan tagged this function * returns the VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS * is returned. @@ -1148,6 +1151,10 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, if (!READ_ONCE(bat_priv->distributed_arp_table)) goto out; + /* first, find out the vid. */ + if (!pskb_may_pull(skb, hdr_size + ETH_HLEN)) + goto out; + vid = batadv_dat_get_vid(skb, &hdr_size); type = batadv_arp_get_type(bat_priv, skb, hdr_size); @@ -1243,6 +1250,10 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, if (!READ_ONCE(bat_priv->distributed_arp_table)) goto out; + /* first, find out the vid. */ + if (!pskb_may_pull(skb, hdr_size + ETH_HLEN)) + goto out; + vid = batadv_dat_get_vid(skb, &hdr_size); type = batadv_arp_get_type(bat_priv, skb, hdr_size); @@ -1305,6 +1316,10 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, if (!READ_ONCE(bat_priv->distributed_arp_table)) return; + /* first, find out the vid. */ + if (!pskb_may_pull(skb, hdr_size + ETH_HLEN)) + return; + vid = batadv_dat_get_vid(skb, &hdr_size); type = batadv_arp_get_type(bat_priv, skb, hdr_size); @@ -1353,6 +1368,10 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, if (!READ_ONCE(bat_priv->distributed_arp_table)) goto out; + /* first, find out the vid. */ + if (!pskb_may_pull(skb, hdr_size + ETH_HLEN)) + goto out; + vid = batadv_dat_get_vid(skb, &hdr_size); type = batadv_arp_get_type(bat_priv, skb, hdr_size); @@ -1747,6 +1766,7 @@ void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv, struct ethhdr *ethhdr; __be32 ip_src, yiaddr; unsigned short vid; + int hdr_size_tmp; __be16 proto; u8 *hw_src; @@ -1763,8 +1783,10 @@ void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv, if (!batadv_dat_check_dhcp_ack(skb, proto, &ip_src, chaddr, &yiaddr)) return; + hdr_size_tmp = hdr_size; + vid = batadv_dat_get_vid(skb, &hdr_size_tmp); + ethhdr = (struct ethhdr *)(skb->data + hdr_size); hw_src = ethhdr->h_source; - vid = batadv_dat_get_vid(skb, &hdr_size); batadv_dat_entry_add(bat_priv, yiaddr, chaddr, vid); batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid); @@ -1804,6 +1826,10 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, if (batadv_forw_packet_is_rebroadcast(forw_packet)) goto out; + /* first, find out the vid. */ + if (!pskb_may_pull(forw_packet->skb, hdr_size + ETH_HLEN)) + goto out; + vid = batadv_dat_get_vid(forw_packet->skb, &hdr_size); type = batadv_arp_get_type(bat_priv, forw_packet->skb, hdr_size); diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 305488a74a25..a5ac82eabd25 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -684,12 +684,13 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, struct batadv_gw_node *gw_node = NULL; struct batadv_gw_node *curr_gw = NULL; struct batadv_neigh_ifinfo *curr_ifinfo, *old_ifinfo; - struct ethhdr *ethhdr = (struct ethhdr *)skb->data; + struct ethhdr *ethhdr; bool out_of_range = false; u8 curr_tq_avg; unsigned short vid; vid = batadv_get_vid(skb, 0); + ethhdr = (struct ethhdr *)skb->data; if (is_multicast_ether_addr(ethhdr->h_dest)) goto out; diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 3c4572284b53..4d3807a645b7 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -580,6 +580,9 @@ void batadv_recv_handler_unregister(u8 packet_type) * @skb: the buffer containing the packet * @header_len: length of the batman header preceding the ethernet header * + * The caller must ensure that at least @header_len + ETH_HLEN bytes are + * accessible after skb->data. + * * Return: VID with the BATADV_VLAN_HAS_TAG flag when the packet embedded in the * skb is vlan tagged. Otherwise BATADV_NO_FLAGS. */ diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index 44026810b99c..511f70e0706a 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -434,6 +434,7 @@ void batadv_interface_rx(struct net_device *mesh_iface, if (!pskb_may_pull(skb, VLAN_ETH_HLEN)) goto dropped; + ethhdr = eth_hdr(skb); vhdr = skb_vlan_eth_hdr(skb); /* drop batman-in-batman packets to prevent loops */ diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index c05fcc9241ad..bbd40fe3a8e5 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -855,8 +855,8 @@ static bool batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, if (skb_cow(skb, sizeof(*unicast_packet)) < 0) return false; - unicast_packet = (struct batadv_unicast_packet *)skb->data; vid = batadv_get_vid(skb, hdr_len); + unicast_packet = (struct batadv_unicast_packet *)skb->data; ethhdr = (struct ethhdr *)(skb->data + hdr_len); /* do not reroute multicast frames in a unicast header */ @@ -1029,6 +1029,7 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, hdr_size); batadv_orig_node_put(orig_node_gw); if (is_gw) { + orig_addr_gw = eth_hdr(skb)->h_source; batadv_dbg(BATADV_DBG_BLA, bat_priv, "%s(): Dropped unicast pkt received from another backbone gw %pM.\n", __func__, orig_addr_gw); diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 7ed19aa8ae59..c52613431f88 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -392,6 +392,9 @@ void br_dev_delete(struct net_device *dev, struct list_head *head) br_fdb_delete_by_port(br, NULL, 0, 1); + timer_shutdown_sync(&br->hello_timer); + timer_shutdown_sync(&br->topology_change_timer); + timer_shutdown_sync(&br->tcn_timer); cancel_delayed_work_sync(&br->gc_work); br_sysfs_delbr(br->dev); diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 46919d73d42f..c7e7e924f155 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -382,7 +382,8 @@ void br_topology_change_detection(struct net_bridge *br) { int isroot = br_is_root_bridge(br); - if (br->stp_enabled != BR_KERNEL_STP) + if (br->stp_enabled != BR_KERNEL_STP || + !(br->dev->flags & IFF_UP)) return; br_info(br, "topology change detected, %s\n", diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 229dde818ab3..96d5945e6a30 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -633,14 +633,6 @@ static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head) container_of(rcu_head, struct netpoll_info, rcu); skb_queue_purge(&npinfo->txq); - - /* we can't call cancel_delayed_work_sync here, as we are in softirq */ - cancel_delayed_work(&npinfo->tx_work); - - /* clean after last, unfinished work */ - __skb_queue_purge(&npinfo->txq); - /* now cancel it again */ - cancel_delayed_work(&npinfo->tx_work); kfree(npinfo); } @@ -664,6 +656,7 @@ static void __netpoll_cleanup(struct netpoll *np) ops->ndo_netpoll_cleanup(np->dev); RCU_INIT_POINTER(np->dev->npinfo, NULL); + disable_delayed_work_sync(&npinfo->tx_work); call_rcu(&npinfo->rcu, rcu_cleanup_netpoll_info); } diff --git a/net/core/sock_map.c b/net/core/sock_map.c index 99e3789492a0..c60ba6d292f9 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -1515,6 +1515,17 @@ static int sock_map_prog_link_lookup(struct bpf_map *map, struct bpf_prog ***ppr return 0; } +static int sock_map_prog_attach_check(enum bpf_attach_type attach_type, + struct bpf_prog *prog) +{ + /* A stream parser must not modify the skb, only measure it. */ + if (prog && attach_type == BPF_SK_SKB_STREAM_PARSER && + prog->aux->changes_pkt_data) + return -EINVAL; + + return 0; +} + /* Handle the following four cases: * prog_attach: prog != NULL, old == NULL, link == NULL * prog_detach: prog == NULL, old != NULL, link == NULL @@ -1533,6 +1544,10 @@ static int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog, if (ret) return ret; + ret = sock_map_prog_attach_check(which, prog); + if (ret) + return ret; + /* for prog_attach/prog_detach/link_attach, return error if a bpf_link * exists for that prog. */ @@ -1776,6 +1791,11 @@ static int sock_map_link_update_prog(struct bpf_link *link, ret = -EINVAL; goto out; } + + ret = sock_map_prog_attach_check(link->attach_type, prog); + if (ret) + goto out; + if (!sockmap_link->map) { ret = -ENOLINK; goto out; diff --git a/net/dsa/tag.c b/net/dsa/tag.c index 79ad105902d9..991732d6eae2 100644 --- a/net/dsa/tag.c +++ b/net/dsa/tag.c @@ -79,15 +79,16 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, if (likely(skb->dev)) { dsa_default_offload_fwd_mark(skb); nskb = skb; + } else { + /* Just drop the skb if we can't find the user */ + kfree_skb(skb); } } else { nskb = cpu_dp->rcv(skb, dev); } - if (!nskb) { - kfree_skb(skb); + if (!nskb) return 0; - } skb = nskb; skb_push(skb, ETH_HLEN); @@ -107,11 +108,10 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, if (unlikely(cpu_dp->ds->untag_bridge_pvid || cpu_dp->ds->untag_vlan_aware_bridge_pvid)) { + /* dsa_software_vlan_untag() drops skb on failure */ nskb = dsa_software_vlan_untag(skb); - if (!nskb) { - kfree_skb(skb); + if (!nskb) return 0; - } skb = nskb; } diff --git a/net/dsa/tag_ar9331.c b/net/dsa/tag_ar9331.c index cbb588ca73aa..2e2388143b02 100644 --- a/net/dsa/tag_ar9331.c +++ b/net/dsa/tag_ar9331.c @@ -51,8 +51,10 @@ static struct sk_buff *ar9331_tag_rcv(struct sk_buff *skb, u8 ver, port; u16 hdr; - if (unlikely(!pskb_may_pull(skb, AR9331_HDR_LEN))) + if (unlikely(!pskb_may_pull(skb, AR9331_HDR_LEN))) { + kfree_skb(skb); return NULL; + } hdr = le16_to_cpu(*(__le16 *)skb_mac_header(skb)); @@ -60,12 +62,14 @@ static struct sk_buff *ar9331_tag_rcv(struct sk_buff *skb, if (unlikely(ver != AR9331_HDR_VERSION)) { netdev_warn_once(ndev, "%s:%i wrong header version 0x%2x\n", __func__, __LINE__, hdr); + kfree_skb(skb); return NULL; } if (unlikely(hdr & AR9331_HDR_FROM_CPU)) { netdev_warn_once(ndev, "%s:%i packet should not be from cpu 0x%2x\n", __func__, __LINE__, hdr); + kfree_skb(skb); return NULL; } @@ -75,8 +79,10 @@ static struct sk_buff *ar9331_tag_rcv(struct sk_buff *skb, port = FIELD_GET(AR9331_HDR_PORT_NUM_MASK, hdr); skb->dev = dsa_conduit_find_user(ndev, 0, port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } return skb; } diff --git a/net/dsa/tag_brcm.c b/net/dsa/tag_brcm.c index cf9420439054..411e3b57d16a 100644 --- a/net/dsa/tag_brcm.c +++ b/net/dsa/tag_brcm.c @@ -102,9 +102,9 @@ static struct sk_buff *brcm_tag_xmit_ll(struct sk_buff *skb, * (including FCS and tag) because the length verification is done after * the Broadcom tag is stripped off the ingress packet. * - * Let dsa_user_xmit() free the SKB + * Free the SKB on error. */ - if (__skb_put_padto(skb, ETH_ZLEN + BRCM_TAG_LEN, false)) + if (skb_put_padto(skb, ETH_ZLEN + BRCM_TAG_LEN)) return NULL; skb_push(skb, BRCM_TAG_LEN); @@ -151,27 +151,35 @@ static struct sk_buff *brcm_tag_rcv_ll(struct sk_buff *skb, int source_port; u8 *brcm_tag; - if (unlikely(!pskb_may_pull(skb, BRCM_TAG_LEN))) + if (unlikely(!pskb_may_pull(skb, BRCM_TAG_LEN))) { + kfree_skb(skb); return NULL; + } brcm_tag = skb->data - offset; /* The opcode should never be different than 0b000 */ - if (unlikely((brcm_tag[0] >> BRCM_OPCODE_SHIFT) & BRCM_OPCODE_MASK)) + if (unlikely((brcm_tag[0] >> BRCM_OPCODE_SHIFT) & BRCM_OPCODE_MASK)) { + kfree_skb(skb); return NULL; + } /* We should never see a reserved reason code without knowing how to * handle it */ - if (unlikely(brcm_tag[2] & BRCM_EG_RC_RSVD)) + if (unlikely(brcm_tag[2] & BRCM_EG_RC_RSVD)) { + kfree_skb(skb); return NULL; + } /* Locate which port this is coming from */ source_port = brcm_tag[3] & BRCM_EG_PID_MASK; skb->dev = dsa_conduit_find_user(dev, 0, source_port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } /* Remove Broadcom tag and update checksum */ skb_pull_rcsum(skb, BRCM_TAG_LEN); @@ -228,8 +236,10 @@ static struct sk_buff *brcm_leg_tag_rcv(struct sk_buff *skb, __be16 *proto; u8 *brcm_tag; - if (unlikely(!pskb_may_pull(skb, BRCM_LEG_TAG_LEN + VLAN_HLEN))) + if (unlikely(!pskb_may_pull(skb, BRCM_LEG_TAG_LEN + VLAN_HLEN))) { + kfree_skb(skb); return NULL; + } brcm_tag = dsa_etype_header_pos_rx(skb); proto = (__be16 *)(brcm_tag + BRCM_LEG_TAG_LEN); @@ -237,8 +247,10 @@ static struct sk_buff *brcm_leg_tag_rcv(struct sk_buff *skb, source_port = brcm_tag[5] & BRCM_LEG_PORT_ID; skb->dev = dsa_conduit_find_user(dev, 0, source_port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } /* The internal switch in BCM63XX SoCs always tags on egress on the CPU * port. We use VID 0 internally for untagged traffic, so strip the tag @@ -273,10 +285,8 @@ static struct sk_buff *brcm_leg_tag_xmit(struct sk_buff *skb, * need to make sure that packets are at least 70 bytes * (including FCS and tag) because the length verification is done after * the Broadcom tag is stripped off the ingress packet. - * - * Let dsa_user_xmit() free the SKB */ - if (__skb_put_padto(skb, ETH_ZLEN + BRCM_LEG_TAG_LEN, false)) + if (skb_put_padto(skb, ETH_ZLEN + BRCM_LEG_TAG_LEN)) return NULL; skb_push(skb, BRCM_LEG_TAG_LEN); @@ -325,10 +335,8 @@ static struct sk_buff *brcm_leg_fcs_tag_xmit(struct sk_buff *skb, * need to make sure that packets are at least 70 bytes (including FCS * and tag) because the length verification is done after the Broadcom * tag is stripped off the ingress packet. - * - * Let dsa_user_xmit() free the SKB. */ - if (__skb_put_padto(skb, ETH_ZLEN + BRCM_LEG_TAG_LEN, false)) + if (skb_put_padto(skb, ETH_ZLEN + BRCM_LEG_TAG_LEN)) return NULL; fcs_len = skb->len; @@ -351,8 +359,9 @@ static struct sk_buff *brcm_leg_fcs_tag_xmit(struct sk_buff *skb, brcm_tag[5] = dp->index & BRCM_LEG_PORT_ID; /* Original FCS value */ - if (__skb_pad(skb, ETH_FCS_LEN, false)) + if (skb_pad(skb, ETH_FCS_LEN)) return NULL; + skb_put_data(skb, &fcs_val, ETH_FCS_LEN); return skb; diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c index 2a2c4fb61a65..d5ffee35fbb5 100644 --- a/net/dsa/tag_dsa.c +++ b/net/dsa/tag_dsa.c @@ -224,6 +224,7 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev, /* Remote management is not implemented yet, * drop. */ + kfree_skb(skb); return NULL; case DSA_CODE_ARP_MIRROR: case DSA_CODE_POLICY_MIRROR: @@ -244,12 +245,14 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev, /* Reserved code, this could be anything. Drop * seems like the safest option. */ + kfree_skb(skb); return NULL; } break; default: + kfree_skb(skb); return NULL; } @@ -271,8 +274,10 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev, source_port); } - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } /* When using LAG offload, skb->dev is not a DSA user interface, * so we cannot call dsa_default_offload_fwd_mark and we need to @@ -335,8 +340,10 @@ static struct sk_buff *dsa_xmit(struct sk_buff *skb, struct net_device *dev) static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev) { - if (unlikely(!pskb_may_pull(skb, DSA_HLEN))) + if (unlikely(!pskb_may_pull(skb, DSA_HLEN))) { + kfree_skb(skb); return NULL; + } return dsa_rcv_ll(skb, dev, 0); } @@ -375,8 +382,10 @@ static struct sk_buff *edsa_xmit(struct sk_buff *skb, struct net_device *dev) static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev) { - if (unlikely(!pskb_may_pull(skb, EDSA_HLEN))) + if (unlikely(!pskb_may_pull(skb, EDSA_HLEN))) { + kfree_skb(skb); return NULL; + } skb_pull_rcsum(skb, EDSA_HLEN - DSA_HLEN); diff --git a/net/dsa/tag_gswip.c b/net/dsa/tag_gswip.c index 5fa436121087..5c407d448c9f 100644 --- a/net/dsa/tag_gswip.c +++ b/net/dsa/tag_gswip.c @@ -80,16 +80,20 @@ static struct sk_buff *gswip_tag_rcv(struct sk_buff *skb, int port; u8 *gswip_tag; - if (unlikely(!pskb_may_pull(skb, GSWIP_RX_HEADER_LEN))) + if (unlikely(!pskb_may_pull(skb, GSWIP_RX_HEADER_LEN))) { + kfree_skb(skb); return NULL; + } gswip_tag = skb->data - ETH_HLEN; /* Get source port information */ port = (gswip_tag[7] & GSWIP_RX_SPPID_MASK) >> GSWIP_RX_SPPID_SHIFT; skb->dev = dsa_conduit_find_user(dev, 0, port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } /* remove GSWIP tag */ skb_pull_rcsum(skb, GSWIP_RX_HEADER_LEN); diff --git a/net/dsa/tag_hellcreek.c b/net/dsa/tag_hellcreek.c index 544ab15685a2..dd9f328f3182 100644 --- a/net/dsa/tag_hellcreek.c +++ b/net/dsa/tag_hellcreek.c @@ -27,8 +27,10 @@ static struct sk_buff *hellcreek_xmit(struct sk_buff *skb, * checksums after the switch strips the tag. */ if (skb->ip_summed == CHECKSUM_PARTIAL && - skb_checksum_help(skb)) + skb_checksum_help(skb)) { + kfree_skb(skb); return NULL; + } /* Tag encoding */ tag = skb_put(skb, HELLCREEK_TAG_LEN); @@ -47,11 +49,14 @@ static struct sk_buff *hellcreek_rcv(struct sk_buff *skb, skb->dev = dsa_conduit_find_user(dev, 0, port); if (!skb->dev) { netdev_warn_once(dev, "Failed to get source port: %d\n", port); + kfree_skb(skb); return NULL; } - if (pskb_trim_rcsum(skb, skb->len - HELLCREEK_TAG_LEN)) + if (pskb_trim_rcsum(skb, skb->len - HELLCREEK_TAG_LEN)) { + kfree_skb(skb); return NULL; + } dsa_default_offload_fwd_mark(skb); diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c index d2475c3bbb7d..67fa89f102e0 100644 --- a/net/dsa/tag_ksz.c +++ b/net/dsa/tag_ksz.c @@ -88,11 +88,15 @@ static struct sk_buff *ksz_common_rcv(struct sk_buff *skb, unsigned int port, unsigned int len) { skb->dev = dsa_conduit_find_user(dev, 0, port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } - if (pskb_trim_rcsum(skb, skb->len - len)) + if (pskb_trim_rcsum(skb, skb->len - len)) { + kfree_skb(skb); return NULL; + } dsa_default_offload_fwd_mark(skb); @@ -123,8 +127,10 @@ static struct sk_buff *ksz8795_xmit(struct sk_buff *skb, struct net_device *dev) struct ethhdr *hdr; u8 *tag; - if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) + if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) { + kfree_skb(skb); return NULL; + } /* Tag encoding */ tag = skb_put(skb, KSZ_INGRESS_TAG_LEN); @@ -141,8 +147,10 @@ static struct sk_buff *ksz8795_rcv(struct sk_buff *skb, struct net_device *dev) { u8 *tag; - if (skb_linearize(skb)) + if (skb_linearize(skb)) { + kfree_skb(skb); return NULL; + } tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN; @@ -255,22 +263,24 @@ static struct sk_buff *ksz_defer_xmit(struct dsa_port *dp, struct sk_buff *skb) xmit_work_fn = tagger_data->xmit_work_fn; xmit_worker = priv->xmit_worker; - if (!xmit_work_fn || !xmit_worker) + if (!xmit_work_fn || !xmit_worker) { + kfree_skb(skb); return NULL; + } xmit_work = kzalloc_obj(*xmit_work, GFP_ATOMIC); - if (!xmit_work) + if (!xmit_work) { + kfree_skb(skb); return NULL; + } kthread_init_work(&xmit_work->work, xmit_work_fn); - /* Increase refcount so the kfree_skb in dsa_user_xmit - * won't really free the packet. - */ xmit_work->dp = dp; xmit_work->skb = skb_get(skb); kthread_queue_work(xmit_worker, &xmit_work->work); + kfree_skb(skb); return NULL; } @@ -284,8 +294,10 @@ static struct sk_buff *ksz9477_xmit(struct sk_buff *skb, __be16 *tag; u16 val; - if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) + if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) { + kfree_skb(skb); return NULL; + } /* Tag encoding */ ksz_xmit_timestamp(dp, skb); @@ -310,8 +322,10 @@ static struct sk_buff *ksz9477_rcv(struct sk_buff *skb, struct net_device *dev) unsigned int port; u8 *tag; - if (skb_linearize(skb)) + if (skb_linearize(skb)) { + kfree_skb(skb); return NULL; + } /* Tag decoding */ tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN; @@ -352,8 +366,10 @@ static struct sk_buff *ksz9893_xmit(struct sk_buff *skb, struct ethhdr *hdr; u8 *tag; - if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) + if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) { + kfree_skb(skb); return NULL; + } /* Tag encoding */ ksz_xmit_timestamp(dp, skb); @@ -418,8 +434,10 @@ static struct sk_buff *lan937x_xmit(struct sk_buff *skb, __be16 *tag; u16 val; - if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) + if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) { + kfree_skb(skb); return NULL; + } ksz_xmit_timestamp(dp, skb); diff --git a/net/dsa/tag_lan9303.c b/net/dsa/tag_lan9303.c index 258e5d7dc5ef..d1194696499a 100644 --- a/net/dsa/tag_lan9303.c +++ b/net/dsa/tag_lan9303.c @@ -85,6 +85,7 @@ static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev) if (unlikely(!pskb_may_pull(skb, LAN9303_TAG_LEN))) { dev_warn_ratelimited(&dev->dev, "Dropping packet, cannot pull\n"); + kfree_skb(skb); return NULL; } @@ -102,6 +103,7 @@ static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev) skb->dev = dsa_conduit_find_user(dev, 0, source_port); if (!skb->dev) { dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid source port\n"); + kfree_skb(skb); return NULL; } diff --git a/net/dsa/tag_mtk.c b/net/dsa/tag_mtk.c index dea3eecaf093..c7dc7731675e 100644 --- a/net/dsa/tag_mtk.c +++ b/net/dsa/tag_mtk.c @@ -72,8 +72,10 @@ static struct sk_buff *mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev) int port; __be16 *phdr; - if (unlikely(!pskb_may_pull(skb, MTK_HDR_LEN))) + if (unlikely(!pskb_may_pull(skb, MTK_HDR_LEN))) { + kfree_skb(skb); return NULL; + } phdr = dsa_etype_header_pos_rx(skb); hdr = ntohs(*phdr); @@ -87,8 +89,10 @@ static struct sk_buff *mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev) port = (hdr & MTK_HDR_RECV_SOURCE_PORT_MASK); skb->dev = dsa_conduit_find_user(dev, 0, port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } dsa_default_offload_fwd_mark(skb); diff --git a/net/dsa/tag_mxl-gsw1xx.c b/net/dsa/tag_mxl-gsw1xx.c index 60f7c445e656..4b1b6ef94196 100644 --- a/net/dsa/tag_mxl-gsw1xx.c +++ b/net/dsa/tag_mxl-gsw1xx.c @@ -73,6 +73,7 @@ static struct sk_buff *gsw1xx_tag_rcv(struct sk_buff *skb, if (unlikely(!pskb_may_pull(skb, GSW1XX_HEADER_LEN))) { dev_warn_ratelimited(&dev->dev, "Dropping packet, cannot pull SKB\n"); + kfree_skb(skb); return NULL; } @@ -81,6 +82,7 @@ static struct sk_buff *gsw1xx_tag_rcv(struct sk_buff *skb, if (unlikely(ntohs(gsw1xx_tag[0]) != ETH_P_MXLGSW)) { dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid special tag\n"); dev_warn_ratelimited(&dev->dev, "Tag: %8ph\n", gsw1xx_tag); + kfree_skb(skb); return NULL; } @@ -90,6 +92,7 @@ static struct sk_buff *gsw1xx_tag_rcv(struct sk_buff *skb, if (!skb->dev) { dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid source port\n"); dev_warn_ratelimited(&dev->dev, "Tag: %8ph\n", gsw1xx_tag); + kfree_skb(skb); return NULL; } diff --git a/net/dsa/tag_mxl862xx.c b/net/dsa/tag_mxl862xx.c index 8daefeb8d49d..87b80ddf0946 100644 --- a/net/dsa/tag_mxl862xx.c +++ b/net/dsa/tag_mxl862xx.c @@ -64,6 +64,7 @@ static struct sk_buff *mxl862_tag_rcv(struct sk_buff *skb, if (unlikely(!pskb_may_pull(skb, MXL862_HEADER_LEN))) { dev_warn_ratelimited(&dev->dev, "Cannot pull SKB, packet dropped\n"); + kfree_skb(skb); return NULL; } @@ -73,6 +74,7 @@ static struct sk_buff *mxl862_tag_rcv(struct sk_buff *skb, dev_warn_ratelimited(&dev->dev, "Invalid special tag marker, packet dropped, tag: %8ph\n", mxl862_tag); + kfree_skb(skb); return NULL; } @@ -83,6 +85,7 @@ static struct sk_buff *mxl862_tag_rcv(struct sk_buff *skb, dev_warn_ratelimited(&dev->dev, "Invalid source port, packet dropped, tag: %8ph\n", mxl862_tag); + kfree_skb(skb); return NULL; } diff --git a/net/dsa/tag_netc.c b/net/dsa/tag_netc.c index ccedfe3a80b6..df72a61796ad 100644 --- a/net/dsa/tag_netc.c +++ b/net/dsa/tag_netc.c @@ -131,14 +131,13 @@ static struct sk_buff *netc_rcv(struct sk_buff *skb, int type, subtype; if (unlikely(!pskb_may_pull(skb, NETC_TAG_MAX_LEN))) - return NULL; + goto err_free_skb; tag_cmn = dsa_etype_header_pos_rx(skb); if (ntohs(tag_cmn->tpid) != ETH_P_NXP_NETC) { dev_warn_ratelimited(&ndev->dev, "Unknown TPID 0x%04x\n", ntohs(tag_cmn->tpid)); - - return NULL; + goto err_free_skb; } if (tag_cmn->qos & NETC_TAG_QV) @@ -149,14 +148,13 @@ static struct sk_buff *netc_rcv(struct sk_buff *skb, if (!sw_id) { dev_warn_ratelimited(&ndev->dev, "VEPA switch ID is not supported yet\n"); - - return NULL; + goto err_free_skb; } port = FIELD_GET(NETC_TAG_PORT, tag_cmn->switch_port); skb->dev = dsa_conduit_find_user(ndev, sw_id, port); if (!skb->dev) - return NULL; + goto err_free_skb; type = FIELD_GET(NETC_TAG_TYPE, tag_cmn->type); subtype = FIELD_GET(NETC_TAG_SUBTYPE, tag_cmn->type); @@ -165,11 +163,11 @@ static struct sk_buff *netc_rcv(struct sk_buff *skb, } else if (type == NETC_TAG_TO_HOST) { /* Currently only subtype0 supported */ if (subtype != NETC_TAG_TH_SUBTYPE0) - return NULL; + goto err_free_skb; } else { dev_warn_ratelimited(&ndev->dev, "Unexpected tag type %d\n", type); - return NULL; + goto err_free_skb; } /* Remove Switch tag from the frame */ @@ -178,6 +176,10 @@ static struct sk_buff *netc_rcv(struct sk_buff *skb, dsa_strip_etype_header(skb, tag_len); return skb; + +err_free_skb: + kfree_skb(skb); + return NULL; } static void netc_flow_dissect(const struct sk_buff *skb, __be16 *proto, diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c index 3405def79c2d..d208c7322cd6 100644 --- a/net/dsa/tag_ocelot.c +++ b/net/dsa/tag_ocelot.c @@ -107,14 +107,16 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb, ocelot_xfh_get_rew_val(extraction, &rew_val); skb->dev = dsa_conduit_find_user(netdev, 0, src_port); - if (!skb->dev) + if (!skb->dev) { /* The switch will reflect back some frames sent through * sockets opened on the bare DSA conduit. These will come back * with src_port equal to the index of the CPU port, for which * there is no user registered. So don't print any error * message here (ignore and drop those frames). */ + kfree_skb(skb); return NULL; + } dsa_default_offload_fwd_mark(skb); skb->priority = qos_class; diff --git a/net/dsa/tag_ocelot_8021q.c b/net/dsa/tag_ocelot_8021q.c index e89d9254e90a..f50f1cd83f16 100644 --- a/net/dsa/tag_ocelot_8021q.c +++ b/net/dsa/tag_ocelot_8021q.c @@ -33,30 +33,34 @@ static struct sk_buff *ocelot_defer_xmit(struct dsa_port *dp, xmit_work_fn = data->xmit_work_fn; xmit_worker = priv->xmit_worker; - if (!xmit_work_fn || !xmit_worker) + if (!xmit_work_fn || !xmit_worker) { + kfree_skb(skb); return NULL; + } /* PTP over IP packets need UDP checksumming. We may have inherited * NETIF_F_HW_CSUM from the DSA conduit, but these packets are not sent * through the DSA conduit, so calculate the checksum here. */ - if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) + if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) { + kfree_skb(skb); return NULL; + } xmit_work = kzalloc_obj(*xmit_work, GFP_ATOMIC); - if (!xmit_work) + if (!xmit_work) { + kfree_skb(skb); return NULL; + } /* Calls felix_port_deferred_xmit in felix.c */ kthread_init_work(&xmit_work->work, xmit_work_fn); - /* Increase refcount so the kfree_skb in dsa_user_xmit - * won't really free the packet. - */ xmit_work->dp = dp; xmit_work->skb = skb_get(skb); kthread_queue_work(xmit_worker, &xmit_work->work); + kfree_skb(skb); return NULL; } @@ -84,8 +88,10 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb, dsa_8021q_rcv(skb, &src_port, &switch_id, NULL, NULL); skb->dev = dsa_conduit_find_user(netdev, switch_id, src_port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } dsa_default_offload_fwd_mark(skb); diff --git a/net/dsa/tag_qca.c b/net/dsa/tag_qca.c index 9e3b429e8b36..510792fbfa92 100644 --- a/net/dsa/tag_qca.c +++ b/net/dsa/tag_qca.c @@ -46,16 +46,20 @@ static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev) tagger_data = ds->tagger_data; - if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN))) + if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN))) { + kfree_skb(skb); return NULL; + } phdr = dsa_etype_header_pos_rx(skb); hdr = ntohs(*phdr); /* Make sure the version is correct */ ver = FIELD_GET(QCA_HDR_RECV_VERSION, hdr); - if (unlikely(ver != QCA_HDR_VERSION)) + if (unlikely(ver != QCA_HDR_VERSION)) { + kfree_skb(skb); return NULL; + } /* Get pk type */ pk_type = FIELD_GET(QCA_HDR_RECV_TYPE, hdr); @@ -64,6 +68,7 @@ static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev) if (pk_type == QCA_HDR_RECV_TYPE_RW_REG_ACK) { if (likely(tagger_data->rw_reg_ack_handler)) tagger_data->rw_reg_ack_handler(ds, skb); + kfree_skb(skb); return NULL; } @@ -71,6 +76,7 @@ static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev) if (pk_type == QCA_HDR_RECV_TYPE_MIB) { if (likely(tagger_data->mib_autocast_handler)) tagger_data->mib_autocast_handler(ds, skb); + kfree_skb(skb); return NULL; } @@ -78,8 +84,10 @@ static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev) port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, hdr); skb->dev = dsa_conduit_find_user(dev, 0, port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } /* Remove QCA tag and recalculate checksum */ skb_pull_rcsum(skb, QCA_HDR_LEN); diff --git a/net/dsa/tag_rtl4_a.c b/net/dsa/tag_rtl4_a.c index 3cc63eacfa03..590ea3b921c9 100644 --- a/net/dsa/tag_rtl4_a.c +++ b/net/dsa/tag_rtl4_a.c @@ -41,7 +41,7 @@ static struct sk_buff *rtl4a_tag_xmit(struct sk_buff *skb, u16 out; /* Pad out to at least 60 bytes */ - if (unlikely(__skb_put_padto(skb, ETH_ZLEN, false))) + if (unlikely(eth_skb_pad(skb))) return NULL; netdev_dbg(dev, "add realtek tag to package to port %d\n", @@ -75,8 +75,10 @@ static struct sk_buff *rtl4a_tag_rcv(struct sk_buff *skb, u8 prot; u8 port; - if (unlikely(!pskb_may_pull(skb, RTL4_A_HDR_LEN))) + if (unlikely(!pskb_may_pull(skb, RTL4_A_HDR_LEN))) { + kfree_skb(skb); return NULL; + } tag = dsa_etype_header_pos_rx(skb); p = (__be16 *)tag; @@ -92,6 +94,7 @@ static struct sk_buff *rtl4a_tag_rcv(struct sk_buff *skb, prot = (protport >> RTL4_A_PROTOCOL_SHIFT) & 0x0f; if (prot != RTL4_A_PROTOCOL_RTL8366RB) { netdev_err(dev, "unknown realtek protocol 0x%01x\n", prot); + kfree_skb(skb); return NULL; } port = protport & 0xff; @@ -99,6 +102,7 @@ static struct sk_buff *rtl4a_tag_rcv(struct sk_buff *skb, skb->dev = dsa_conduit_find_user(dev, 0, port); if (!skb->dev) { netdev_dbg(dev, "could not find user for port %d\n", port); + kfree_skb(skb); return NULL; } diff --git a/net/dsa/tag_rtl8_4.c b/net/dsa/tag_rtl8_4.c index 852c6b88079a..4da3beebef75 100644 --- a/net/dsa/tag_rtl8_4.c +++ b/net/dsa/tag_rtl8_4.c @@ -143,8 +143,10 @@ static struct sk_buff *rtl8_4t_tag_xmit(struct sk_buff *skb, /* Calculate the checksum here if not done yet as trailing tags will * break either software or hardware based checksum */ - if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) + if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) { + kfree_skb(skb); return NULL; + } rtl8_4_write_tag(skb, dev, skb_put(skb, RTL8_4_TAG_LEN)); @@ -201,11 +203,15 @@ static int rtl8_4_read_tag(struct sk_buff *skb, struct net_device *dev, static struct sk_buff *rtl8_4_tag_rcv(struct sk_buff *skb, struct net_device *dev) { - if (unlikely(!pskb_may_pull(skb, RTL8_4_TAG_LEN))) + if (unlikely(!pskb_may_pull(skb, RTL8_4_TAG_LEN))) { + kfree_skb(skb); return NULL; + } - if (unlikely(rtl8_4_read_tag(skb, dev, dsa_etype_header_pos_rx(skb)))) + if (unlikely(rtl8_4_read_tag(skb, dev, dsa_etype_header_pos_rx(skb)))) { + kfree_skb(skb); return NULL; + } /* Remove tag and recalculate checksum */ skb_pull_rcsum(skb, RTL8_4_TAG_LEN); @@ -218,14 +224,20 @@ static struct sk_buff *rtl8_4_tag_rcv(struct sk_buff *skb, static struct sk_buff *rtl8_4t_tag_rcv(struct sk_buff *skb, struct net_device *dev) { - if (skb_linearize(skb)) + if (skb_linearize(skb)) { + kfree_skb(skb); return NULL; + } - if (unlikely(rtl8_4_read_tag(skb, dev, skb_tail_pointer(skb) - RTL8_4_TAG_LEN))) + if (unlikely(rtl8_4_read_tag(skb, dev, skb_tail_pointer(skb) - RTL8_4_TAG_LEN))) { + kfree_skb(skb); return NULL; + } - if (pskb_trim_rcsum(skb, skb->len - RTL8_4_TAG_LEN)) + if (pskb_trim_rcsum(skb, skb->len - RTL8_4_TAG_LEN)) { + kfree_skb(skb); return NULL; + } return skb; } diff --git a/net/dsa/tag_rzn1_a5psw.c b/net/dsa/tag_rzn1_a5psw.c index 10994b3470f6..734910156dc3 100644 --- a/net/dsa/tag_rzn1_a5psw.c +++ b/net/dsa/tag_rzn1_a5psw.c @@ -48,7 +48,7 @@ static struct sk_buff *a5psw_tag_xmit(struct sk_buff *skb, struct net_device *de * least 60 bytes otherwise they will be discarded when they enter the * switch port logic. */ - if (__skb_put_padto(skb, ETH_ZLEN, false)) + if (eth_skb_pad(skb)) return NULL; /* provide 'A5PSW_TAG_LEN' bytes additional space */ @@ -77,6 +77,7 @@ static struct sk_buff *a5psw_tag_rcv(struct sk_buff *skb, if (unlikely(!pskb_may_pull(skb, A5PSW_TAG_LEN))) { dev_warn_ratelimited(&dev->dev, "Dropping packet, cannot pull\n"); + kfree_skb(skb); return NULL; } @@ -84,14 +85,17 @@ static struct sk_buff *a5psw_tag_rcv(struct sk_buff *skb, if (tag->ctrl_tag != htons(ETH_P_DSA_A5PSW)) { dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid TAG marker\n"); + kfree_skb(skb); return NULL; } port = FIELD_GET(A5PSW_CTRL_DATA_PORT, ntohs(tag->ctrl_data)); skb->dev = dsa_conduit_find_user(dev, 0, port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } skb_pull_rcsum(skb, A5PSW_TAG_LEN); dsa_strip_etype_header(skb, A5PSW_TAG_LEN); diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c index de6d4ce8668b..bfe1f746f55b 100644 --- a/net/dsa/tag_sja1105.c +++ b/net/dsa/tag_sja1105.c @@ -149,19 +149,20 @@ static struct sk_buff *sja1105_defer_xmit(struct dsa_port *dp, xmit_work_fn = tagger_data->xmit_work_fn; xmit_worker = priv->xmit_worker; - if (!xmit_work_fn || !xmit_worker) + if (!xmit_work_fn || !xmit_worker) { + kfree_skb(skb); return NULL; + } xmit_work = kzalloc_obj(*xmit_work, GFP_ATOMIC); - if (!xmit_work) + if (!xmit_work) { + kfree_skb(skb); return NULL; + } kthread_init_work(&xmit_work->work, xmit_work_fn); - /* Increase refcount so the kfree_skb in dsa_user_xmit - * won't really free the packet. - */ xmit_work->dp = dp; - xmit_work->skb = skb_get(skb); + xmit_work->skb = skb; kthread_queue_work(xmit_worker, &xmit_work->work); @@ -401,10 +402,7 @@ static struct sk_buff kfree_skb(priv->stampable_skb); } - /* Hold a reference to avoid dsa_switch_rcv - * from freeing the skb. - */ - priv->stampable_skb = skb_get(skb); + priv->stampable_skb = skb; spin_unlock(&priv->meta_lock); /* Tell DSA we got nothing */ @@ -436,6 +434,7 @@ static struct sk_buff dev_err_ratelimited(ds->dev, "Unexpected meta frame\n"); spin_unlock(&priv->meta_lock); + kfree_skb(skb); return NULL; } @@ -443,6 +442,7 @@ static struct sk_buff dev_err_ratelimited(ds->dev, "Meta frame on wrong port\n"); spin_unlock(&priv->meta_lock); + kfree_skb(skb); return NULL; } @@ -501,18 +501,21 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, /* Normal data plane traffic and link-local frames are tagged with * a tag_8021q VLAN which we have to strip */ - if (sja1105_skb_has_tag_8021q(skb)) + if (sja1105_skb_has_tag_8021q(skb)) { dsa_8021q_rcv(skb, &source_port, &switch_id, &vbid, &vid); - else if (source_port == -1 && switch_id == -1) + } else if (source_port == -1 && switch_id == -1) { /* Packets with no source information have no chance of * getting accepted, drop them straight away. */ + kfree_skb(skb); return NULL; + } skb->dev = dsa_tag_8021q_find_user(netdev, source_port, switch_id, vid, vbid); if (!skb->dev) { netdev_warn(netdev, "Couldn't decode source port\n"); + kfree_skb(skb); return NULL; } @@ -539,12 +542,15 @@ static struct sk_buff *sja1110_rcv_meta(struct sk_buff *skb, u16 rx_header) if (!ds) { net_err_ratelimited("%s: cannot find switch id %d\n", conduit->name, switch_id); + kfree_skb(skb); return NULL; } tagger_data = sja1105_tagger_data(ds); - if (!tagger_data->meta_tstamp_handler) + if (!tagger_data->meta_tstamp_handler) { + kfree_skb(skb); return NULL; + } for (i = 0; i <= n_ts; i++) { u8 ts_id, source_port, dir; @@ -562,6 +568,7 @@ static struct sk_buff *sja1110_rcv_meta(struct sk_buff *skb, u16 rx_header) } /* Discard the meta frame, we've consumed the timestamps it contained */ + kfree_skb(skb); return NULL; } @@ -572,8 +579,10 @@ static struct sk_buff *sja1110_rcv_inband_control_extension(struct sk_buff *skb, { u16 rx_header; - if (unlikely(!pskb_may_pull(skb, SJA1110_HEADER_LEN))) + if (unlikely(!pskb_may_pull(skb, SJA1110_HEADER_LEN))) { + kfree_skb(skb); return NULL; + } /* skb->data points to skb_mac_header(skb) + ETH_HLEN, which is exactly * what we need because the caller has checked the EtherType (which is @@ -609,8 +618,10 @@ static struct sk_buff *sja1110_rcv_inband_control_extension(struct sk_buff *skb, * padding and trailer we need to account for the fact that * skb->data points to skb_mac_header(skb) + ETH_HLEN. */ - if (pskb_trim_rcsum(skb, start_of_padding - ETH_HLEN)) + if (pskb_trim_rcsum(skb, start_of_padding - ETH_HLEN)) { + kfree_skb(skb); return NULL; + } /* Trap-to-host frame, no timestamp trailer */ } else { *source_port = SJA1110_RX_HEADER_SRC_PORT(rx_header); @@ -653,6 +664,7 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb, if (!skb->dev) { netdev_warn(netdev, "Couldn't decode source port\n"); + kfree_skb(skb); return NULL; } diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c index 4dce24cfe6a7..49c802c10ca6 100644 --- a/net/dsa/tag_trailer.c +++ b/net/dsa/tag_trailer.c @@ -30,22 +30,30 @@ static struct sk_buff *trailer_rcv(struct sk_buff *skb, struct net_device *dev) u8 *trailer; int source_port; - if (skb_linearize(skb)) + if (skb_linearize(skb)) { + kfree_skb(skb); return NULL; + } trailer = skb_tail_pointer(skb) - 4; if (trailer[0] != 0x80 || (trailer[1] & 0xf8) != 0x00 || - (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00) + (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00) { + kfree_skb(skb); return NULL; + } source_port = trailer[1] & 7; skb->dev = dsa_conduit_find_user(dev, 0, source_port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } - if (pskb_trim_rcsum(skb, skb->len - 4)) + if (pskb_trim_rcsum(skb, skb->len - 4)) { + kfree_skb(skb); return NULL; + } return skb; } diff --git a/net/dsa/tag_vsc73xx_8021q.c b/net/dsa/tag_vsc73xx_8021q.c index af121a9aff7f..f4736a1a7a0f 100644 --- a/net/dsa/tag_vsc73xx_8021q.c +++ b/net/dsa/tag_vsc73xx_8021q.c @@ -44,6 +44,7 @@ vsc73xx_rcv(struct sk_buff *skb, struct net_device *netdev) if (!skb->dev) { dev_warn_ratelimited(&netdev->dev, "Couldn't decode source port\n"); + kfree_skb(skb); return NULL; } diff --git a/net/dsa/tag_xrs700x.c b/net/dsa/tag_xrs700x.c index a05219f702c6..bb268020ee86 100644 --- a/net/dsa/tag_xrs700x.c +++ b/net/dsa/tag_xrs700x.c @@ -30,15 +30,21 @@ static struct sk_buff *xrs700x_rcv(struct sk_buff *skb, struct net_device *dev) source_port = ffs((int)trailer[0]) - 1; - if (source_port < 0) + if (source_port < 0) { + kfree_skb(skb); return NULL; + } skb->dev = dsa_conduit_find_user(dev, 0, source_port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } - if (pskb_trim_rcsum(skb, skb->len - 1)) + if (pskb_trim_rcsum(skb, skb->len - 1)) { + kfree_skb(skb); return NULL; + } /* Frame is forwarded by hardware, don't forward in software. */ dsa_default_offload_fwd_mark(skb); diff --git a/net/dsa/tag_yt921x.c b/net/dsa/tag_yt921x.c index f3ced99b1c85..294784ab6694 100644 --- a/net/dsa/tag_yt921x.c +++ b/net/dsa/tag_yt921x.c @@ -87,8 +87,10 @@ yt921x_tag_rcv(struct sk_buff *skb, struct net_device *netdev) __be16 *tag; u16 rx; - if (unlikely(!pskb_may_pull(skb, YT921X_TAG_LEN))) + if (unlikely(!pskb_may_pull(skb, YT921X_TAG_LEN))) { + kfree_skb(skb); return NULL; + } tag = dsa_etype_header_pos_rx(skb); @@ -96,6 +98,7 @@ yt921x_tag_rcv(struct sk_buff *skb, struct net_device *netdev) dev_warn_ratelimited(&netdev->dev, "Unexpected EtherType 0x%04x\n", ntohs(tag[0])); + kfree_skb(skb); return NULL; } @@ -104,6 +107,7 @@ yt921x_tag_rcv(struct sk_buff *skb, struct net_device *netdev) if (unlikely((rx & YT921X_TAG_PORT_EN) == 0)) { dev_warn_ratelimited(&netdev->dev, "Unexpected rx tag 0x%04x\n", rx); + kfree_skb(skb); return NULL; } @@ -112,6 +116,7 @@ yt921x_tag_rcv(struct sk_buff *skb, struct net_device *netdev) if (unlikely(!skb->dev)) { dev_warn_ratelimited(&netdev->dev, "Couldn't decode source port %u\n", port); + kfree_skb(skb); return NULL; } diff --git a/net/dsa/user.c b/net/dsa/user.c index 8704c1a3a5b7..072fa76972cc 100644 --- a/net/dsa/user.c +++ b/net/dsa/user.c @@ -935,13 +935,12 @@ static netdev_tx_t dsa_user_xmit(struct sk_buff *skb, struct net_device *dev) eth_skb_pad(skb); /* Transmit function may have to reallocate the original SKB, - * in which case it must have freed it. Only free it here on error. + * in which case it must have freed it. Taggers will drop the + * passed skb on error. */ nskb = p->xmit(skb, dev); - if (!nskb) { - kfree_skb(skb); + if (!nskb) return NETDEV_TX_OK; - } return dsa_enqueue_skb(nskb, dev); } diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index a56bb79e15e0..e4ec60a33496 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -371,8 +371,9 @@ static void tcp_ao_key_free_rcu(struct rcu_head *head) kfree_sensitive(key); } -static void tcp_ao_info_free(struct tcp_ao_info *ao) +static void tcp_ao_info_free_rcu(struct rcu_head *head) { + struct tcp_ao_info *ao = container_of(head, struct tcp_ao_info, rcu); struct tcp_ao_key *key; struct hlist_node *n; @@ -411,7 +412,7 @@ void tcp_ao_destroy_sock(struct sock *sk, bool twsk) if (!twsk) tcp_ao_sk_omem_free(sk, ao); - tcp_ao_info_free(ao); + call_rcu(&ao->rcu, tcp_ao_info_free_rcu); } void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index ec09f97cc9e6..209ef7522508 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1467,9 +1467,9 @@ void tcp_clear_md5_list(struct sock *sk) md5sig = rcu_dereference_protected(tp->md5sig_info, 1); hlist_for_each_entry_safe(key, n, &md5sig->head, node) { - hlist_del(&key->node); + hlist_del_rcu(&key->node); atomic_sub(sizeof(*key), &sk->sk_omem_alloc); - kfree(key); + kfree_rcu(key, rcu); } } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 00ec4b5900f2..d7c1444b5e30 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -4329,9 +4329,13 @@ int tcp_connect(struct sock *sk) if (needs_md5) { tcp_ao_destroy_sock(sk, false); } else if (needs_ao) { + struct tcp_md5sig_info *md5sig; + tcp_clear_md5_list(sk); - kfree(rcu_replace_pointer(tp->md5sig_info, NULL, - lockdep_sock_is_held(sk))); + md5sig = rcu_replace_pointer(tp->md5sig_info, NULL, + lockdep_sock_is_held(sk)); + kfree_rcu(md5sig, rcu); + static_branch_slow_dec_deferred(&tcp_md5_needed); } } #endif diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index fc95738ded76..a130cdfaebfb 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -636,12 +636,12 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) }; const struct nlmsghdr *nlh = cb->nlh; struct net *net = sock_net(skb->sk); - unsigned int e = 0, s_e; struct hlist_head *head; struct fib6_walker *w; struct fib6_table *tb; unsigned int h, s_h; int err = 0; + u32 s_id; rcu_read_lock(); if (cb->strict_check) { @@ -701,23 +701,22 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) } s_h = cb->args[0]; - s_e = cb->args[1]; + s_id = cb->args[1]; - for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) { - e = 0; + for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_id = 0) { head = &net->ipv6.fib_table_hash[h]; hlist_for_each_entry_rcu(tb, head, tb6_hlist) { - if (e < s_e) - goto next; + if (s_id && tb->tb6_id != s_id) + continue; + + s_id = 0; + cb->args[1] = tb->tb6_id; err = fib6_dump_table(tb, skb, cb); if (err != 0) goto out; -next: - e++; } } out: - cb->args[1] = e; cb->args[0] = h; unlock: diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c index 1c3ad25700c4..62a7eb779202 100644 --- a/net/ipv6/seg6.c +++ b/net/ipv6/seg6.c @@ -29,6 +29,9 @@ bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len, bool reduced) int max_last_entry; int trailing; + if (len < sizeof(*srh)) + return false; + if (srh->type != IPV6_SRCRT_TYPE_4) return false; diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index a531b654b8d9..6cfad152d7d1 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1480,7 +1480,11 @@ ip_set_dump_done(struct netlink_callback *cb) struct ip_set_net *inst = (struct ip_set_net *)cb->args[IPSET_CB_NET]; ip_set_id_t index = (ip_set_id_t)cb->args[IPSET_CB_INDEX]; - struct ip_set *set = ip_set_ref_netlink(inst, index); + struct ip_set *set; + + rcu_read_lock(); + set = ip_set_ref_netlink(inst, index); + rcu_read_unlock(); if (set->variant->uref) set->variant->uref(set, cb, false); @@ -1686,7 +1690,9 @@ next_set: release_refcount: /* If there was an error or set is done, release set */ if (ret || !cb->args[IPSET_CB_ARG0]) { + rcu_read_lock(); set = ip_set_ref_netlink(inst, index); + rcu_read_unlock(); if (set->variant->uref) set->variant->uref(set, cb, false); pr_debug("release set %s\n", set->name); diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 38630c5e006f..7ae68d60586a 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -306,7 +306,7 @@ struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me) { struct nf_conntrack_expect *new; - new = kmem_cache_alloc(nf_ct_expect_cachep, GFP_ATOMIC); + new = kmem_cache_zalloc(nf_ct_expect_cachep, GFP_ATOMIC); if (!new) return NULL; @@ -391,6 +391,7 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class, #if IS_ENABLED(CONFIG_NF_NAT) memset(&exp->saved_addr, 0, sizeof(exp->saved_addr)); memset(&exp->saved_proto, 0, sizeof(exp->saved_proto)); + exp->dir = 0; #endif } EXPORT_SYMBOL_GPL(nf_ct_expect_init); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 4217715d42dc..31cbb1b55b9e 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -3549,8 +3549,6 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, if (cda[CTA_EXPECT_FLAGS]) { exp->flags = ntohl(nla_get_be32(cda[CTA_EXPECT_FLAGS])); exp->flags &= ~NF_CT_EXPECT_USERSPACE; - } else { - exp->flags = 0; } if (cda[CTA_EXPECT_FN]) { const char *name = nla_data(cda[CTA_EXPECT_FN]); @@ -3562,8 +3560,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, goto err_out; } exp->expectfn = expfn->expectfn; - } else - exp->expectfn = NULL; + } exp->class = class; exp->master = ct; @@ -3583,12 +3580,6 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, exp, nf_ct_l3num(ct)); if (err < 0) goto err_out; -#if IS_ENABLED(CONFIG_NF_NAT) - } else { - memset(&exp->saved_addr, 0, sizeof(exp->saved_addr)); - memset(&exp->saved_proto, 0, sizeof(exp->saved_proto)); - exp->dir = 0; -#endif } return exp; err_out: diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 5ec3a4a4bbd7..f3f90a866338 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -956,7 +956,6 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, return NF_ACCEPT; saddr = &ct->tuplehash[!dir].tuple.src.u3; } else if (sip_external_media) { - struct net_device *dev = skb_dst(skb)->dev; struct dst_entry *dst = NULL; struct flowi fl; @@ -978,7 +977,11 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, * through the same interface as the signalling peer. */ if (dst) { - bool external_media = (dst->dev == dev); + const struct dst_entry *this_dst = skb_dst(skb); + bool external_media = false; + + if (this_dst && dst->dev == this_dst->dev) + external_media = true; dst_release(dst); if (external_media) diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index f1460b683d7a..2cbcca9110db 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -163,6 +163,8 @@ nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy, tb[NFCTH_POLICY_NAME], NF_CT_HELPER_NAME_LEN); expect_policy->max_expected = ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX])); + if (!expect_policy->max_expected) + expect_policy->max_expected = NF_CT_EXPECT_MAX_CNT; if (expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT) return -EINVAL; diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 80ca077b81bd..35d4c6c628ff 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -1184,6 +1184,173 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) return err; } +static bool nfqnl_validate_ipopts(const struct iphdr *iph_new, + const struct nf_queue_entry *e) +{ + const struct iphdr *iph_orig = ip_hdr(e->skb); + unsigned int ihl = iph_new->ihl * 4; + + if (iph_new->ihl != iph_orig->ihl) + return false; + if (ihl == sizeof(*iph_orig)) + return true; + + return memcmp(iph_new + 1, ip_hdr(e->skb) + 1, ihl - sizeof(*iph_orig)) == 0; +} + +static bool nfqnl_validate_ip4(const struct iphdr *iph, unsigned int data_len, + const struct nf_queue_entry *e) +{ + unsigned int ihl; + + if (data_len < sizeof(*iph)) + return false; + + ihl = iph->ihl * 4u; + if (ihl < sizeof(*iph) || data_len < ihl) + return false; + + if (iph->version != 4 || + ((iph->frag_off ^ ip_hdr(e->skb)->frag_off) & ~htons(IP_DF)) != 0) + return false; + + /* BIG TCP won't work; netlink attr len is u16 */ + if (ntohs(iph->tot_len) != data_len) + return false; + + /* support for ipopts mangling would require + * recompile + skb transport header update. + */ + return nfqnl_validate_ipopts(iph, e); +} + +static bool nfqnl_validate_one_exthdr(const u8 *data, + unsigned int data_len, + const struct nf_queue_entry *e, + int start, int hdrlen) +{ + u16 octets; + + if (data_len < hdrlen || hdrlen < 2) + return false; + + while (hdrlen > 0) { + if (data_len < sizeof(octets)) + return false; + data_len -= sizeof(octets); + + if (skb_copy_bits(e->skb, start, &octets, sizeof(octets))) + return false; + + if (hdrlen < sizeof(octets)) + return false; + + hdrlen -= sizeof(octets); + if (memcmp(data, &octets, sizeof(octets))) + return false; + + start += sizeof(octets); + data += sizeof(octets); + } + + return true; +} + +static bool nfqnl_validate_exthdr(const struct ipv6hdr *ip6_new, + unsigned int data_len, + const struct nf_queue_entry *e) +{ + const struct ipv6hdr *ip6_orig = ipv6_hdr(e->skb); + int exthdr_cnt = 0, start = sizeof(*ip6_orig); + const u8 *data = (const u8 *)ip6_new; + u8 orig_nexthdr = ip6_orig->nexthdr; + u8 new_nexthdr = ip6_new->nexthdr; + + if (new_nexthdr != orig_nexthdr) + return false; + + data += sizeof(*ip6_new); + data_len -= sizeof(*ip6_new); + + while (ipv6_ext_hdr(orig_nexthdr)) { + const struct ipv6_opt_hdr *hp; + struct ipv6_opt_hdr _hdr; + int hdrlen; + + if (orig_nexthdr == NEXTHDR_NONE) + return true; + + if (unlikely(exthdr_cnt++ >= IP6_MAX_EXT_HDRS_CNT)) + return false; + + hp = skb_header_pointer(e->skb, start, sizeof(_hdr), &_hdr); + if (!hp) + return false; + + switch (orig_nexthdr) { + case NEXTHDR_FRAGMENT: + hdrlen = sizeof(struct frag_hdr); + break; + case NEXTHDR_AUTH: + hdrlen = ipv6_authlen(hp); + break; + default: + hdrlen = ipv6_optlen(hp); + break; + } + + if (!nfqnl_validate_one_exthdr(data, data_len, e, + start, hdrlen)) + return false; + + orig_nexthdr = hp->nexthdr; + hp = (const void *)data; + new_nexthdr = hp->nexthdr; + + if (new_nexthdr != orig_nexthdr) + return false; + + data_len -= hdrlen; + start += hdrlen; + data += hdrlen; + } + + return true; +} + +static bool nfqnl_validate_ip6(const struct ipv6hdr *ip6, unsigned int data_len, + const struct nf_queue_entry *e) +{ + if (data_len < sizeof(*ip6)) + return false; + + /* BIG TCP/jumbograms won't work; netlink attr len is u16 */ + if (ntohs(ip6->payload_len) != data_len - sizeof(*ip6)) + return false; + + if (ip6->version != 6) + return false; + + return nfqnl_validate_exthdr(ip6, data_len, e); +} + +static bool nfqnl_validate_write(const void *data, unsigned int data_len, + const struct nf_queue_entry *e) +{ + switch (e->state.pf) { + case NFPROTO_IPV4: + return nfqnl_validate_ip4(data, data_len, e); + case NFPROTO_IPV6: + return nfqnl_validate_ip6(data, data_len, e) && + !(IP6CB(e->skb)->flags & IP6SKB_JUMBOGRAM); + case NFPROTO_BRIDGE: + /* No write support. Bridge is dubious: userspace doesn't even see L2 header */ + return false; + } + + return false; +} + static int nfqnl_mangle(void *data, unsigned int data_len, struct nf_queue_entry *e, int diff) { @@ -1192,6 +1359,9 @@ nfqnl_mangle(void *data, unsigned int data_len, struct nf_queue_entry *e, int di if (e->state.net->user_ns != &init_user_ns) return -EPERM; + if (!nfqnl_validate_write(data, data_len, e)) + return -EINVAL; + if (diff < 0) { unsigned int min_len = skb_transport_offset(e->skb); diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c index e048f05694cd..89555380f1c5 100644 --- a/net/netfilter/nft_fib.c +++ b/net/netfilter/nft_fib.c @@ -31,6 +31,15 @@ int nft_fib_validate(const struct nft_ctx *ctx, const struct nft_expr *expr) const struct nft_fib *priv = nft_expr_priv(expr); unsigned int hooks; + switch (ctx->family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + case NFPROTO_INET: + break; + default: + return -EOPNOTSUPP; + } + switch (priv->result) { case NFT_FIB_RESULT_OIF: case NFT_FIB_RESULT_OIFNAME: diff --git a/net/netfilter/nft_fib_netdev.c b/net/netfilter/nft_fib_netdev.c index 3f3478abd845..5774a7544027 100644 --- a/net/netfilter/nft_fib_netdev.c +++ b/net/netfilter/nft_fib_netdev.c @@ -50,6 +50,33 @@ static void nft_fib_netdev_eval(const struct nft_expr *expr, regs->verdict.code = NFT_BREAK; } +static int nft_fib_netdev_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + const struct nft_fib *priv = nft_expr_priv(expr); + unsigned int hooks; + + switch (priv->result) { + case NFT_FIB_RESULT_OIF: + case NFT_FIB_RESULT_OIFNAME: + hooks = (1 << NF_NETDEV_INGRESS); + break; + case NFT_FIB_RESULT_ADDRTYPE: + if (priv->flags & NFTA_FIB_F_IIF) + hooks = (1 << NF_NETDEV_INGRESS); + else if (priv->flags & NFTA_FIB_F_OIF) + hooks = (1 << NF_NETDEV_EGRESS); + else + hooks = (1 << NF_NETDEV_INGRESS) | + (1 << NF_NETDEV_EGRESS); + break; + default: + return -EINVAL; + } + + return nft_chain_validate_hooks(ctx->chain, hooks); +} + static struct nft_expr_type nft_fib_netdev_type; static const struct nft_expr_ops nft_fib_netdev_ops = { .type = &nft_fib_netdev_type, @@ -57,7 +84,7 @@ static const struct nft_expr_ops nft_fib_netdev_ops = { .eval = nft_fib_netdev_eval, .init = nft_fib_init, .dump = nft_fib_dump, - .validate = nft_fib_validate, + .validate = nft_fib_netdev_validate, }; static struct nft_expr_type nft_fib_netdev_type __read_mostly = { diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 345eff140d56..391539a1ceaa 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -834,6 +834,249 @@ nft_payload_set_vlan(const u32 *src, struct sk_buff *skb, u16 offset, u8 len, return true; } +/* Ingress is very early, before l3 protocol handlers. + * There should be no in-tree code that trusts l3/l4 headers + * between ingress and NF_INET_PRE_ROUTING hooks. + */ +static bool nft_in_ingress(const struct nf_hook_state *s) +{ + return s->pf == NFPROTO_NETDEV && s->hook == NF_NETDEV_INGRESS; +} + +static bool nft_nh_write_ok_ip4(const struct nft_pktinfo *pkt, + const struct nft_payload_set *priv, + const u32 *src) +{ + unsigned int offset = priv->offset + skb_network_offset(pkt->skb); + const u8 *new_octets = (const u8 *)src; + u8 old_octet; + + switch (priv->offset) { + case 0: /* csum fixups does expand dscp/tos store to 2 bytes. + * make sure ihl/version remain unchanged. + */ + if (skb_copy_bits(pkt->skb, offset, &old_octet, sizeof(old_octet))) + return false; + + return priv->len == 2 && + *new_octets == old_octet; + case offsetof(struct iphdr, tos): + return priv->len == 1; + case offsetof(struct iphdr, id): + return priv->len == 2; + case offsetof(struct iphdr, ttl): + if (priv->len == 1) + return true; + + if (priv->len != 2) + return false; + + /* same, csum fixup does expand ttl store to two bytes. + * check protocol is not altered. + */ + if (skb_copy_bits(pkt->skb, offset + 1, &old_octet, sizeof(old_octet))) + return false; + + return new_octets[1] == old_octet; + case offsetof(struct iphdr, check): + return priv->len <= 2 + 4 + 4; + case offsetof(struct iphdr, saddr): + return priv->len <= 4 + 4; + case offsetof(struct iphdr, daddr): + return priv->len <= 4; + } + + return false; +} + +static bool nft_nh_write_ok_ip6(const struct nft_pktinfo *pkt, + const struct nft_payload_set *priv, + const u32 *src) +{ + const struct ipv6hdr *ih = (const void *)src; + + switch (priv->offset) { + case 0: /* store to dscp must not alter ip6 version */ + return priv->len <= 4 && ih->version == 6; + case 2: + return priv->len <= 2; + case offsetof(struct ipv6hdr, hop_limit): + return priv->len == 1; + case offsetof(struct ipv6hdr, saddr): + return priv->len <= 16 + 16; + case offsetof(struct ipv6hdr, daddr): + return priv->len <= 16; + } + + return false; +} + +static bool nft_nh_write_ok_arp(const struct nft_payload_set *priv) +{ + /* Variable size for standard ethernet arp */ + const unsigned int eth_ip = 2 * (ETH_ALEN + 4); + unsigned int offset = priv->offset; + + switch (offset) { + case offsetof(struct arphdr, ar_op): + return priv->len == 2; + default: + break; + } + + /* permit writes post fixed arp header size. offset + len are + * checked vs skb size via skb_ensure_writable. + */ + return offset >= sizeof(struct arphdr) && priv->len <= eth_ip; +} + +static bool nft_nh_write_ok_netdev(const struct nft_pktinfo *pkt, + const struct nft_payload_set *priv, + const u32 *src) +{ +#ifdef CONFIG_NF_TABLES_NETDEV + switch (pkt->skb->protocol) { + case htons(ETH_P_ARP): + return nft_nh_write_ok_arp(priv); + case htons(ETH_P_IP): + return nft_nh_write_ok_ip4(pkt, priv, src); + case htons(ETH_P_IPV6): + return nft_nh_write_ok_ip6(pkt, priv, src); + } +#endif + /* default to false for now, relax later in case we have + * use-cases that need inner header manipulation for + * encapsulated traffic like vlan or PPPoE. + */ + return false; +} + +static bool nft_nh_write_ok_bridge(const struct nft_pktinfo *pkt, + const struct nft_payload_set *priv, + const u32 *src) +{ +#if IS_ENABLED(CONFIG_NF_TABLES_BRIDGE) + switch (pkt->ethertype) { + case htons(ETH_P_ARP): + return nft_nh_write_ok_arp(priv); + case htons(ETH_P_IP): + return nft_nh_write_ok_ip4(pkt, priv, src); + case htons(ETH_P_IPV6): + return nft_nh_write_ok_ip6(pkt, priv, src); + } +#endif + /* see nft_nh_write_ok_netdev: default to false */ + return false; +} + +static bool nft_nh_write_ok(const struct nft_pktinfo *pkt, + const struct nft_payload_set *priv, + const u32 *src) +{ + switch (pkt->state->pf) { + case NFPROTO_ARP: + return nft_nh_write_ok_arp(priv); + case NFPROTO_BRIDGE: + return nft_nh_write_ok_bridge(pkt, priv, src); + case NFPROTO_IPV4: + return nft_nh_write_ok_ip4(pkt, priv, src); + case NFPROTO_IPV6: + return nft_nh_write_ok_ip6(pkt, priv, src); + case NFPROTO_NETDEV: + if (pkt->state->hook == NF_NETDEV_INGRESS) + return true; + return nft_nh_write_ok_netdev(pkt, priv, src); + } + + return false; +} + +/* check linklayer modifications don't spill into network header. */ +static bool nft_ll_write_ok(const struct nft_pktinfo *pkt, int offset) +{ + if (nft_in_ingress(pkt->state)) + return true; + + return offset <= skb_network_offset(pkt->skb); +} + +static bool nft_payload_validate_inet_csum_offset(const struct nft_ctx *ctx, + const struct nft_payload_set *priv) +{ + switch (priv->base) { + case NFT_PAYLOAD_LL_HEADER: + break; + case NFT_PAYLOAD_NETWORK_HEADER: + if (ctx->family == NFPROTO_IPV4) { + if (offsetof(struct iphdr, check) == priv->csum_offset) + return true; + + return false; + } + return true; /* run time validation required */ + case NFT_PAYLOAD_TRANSPORT_HEADER: + if (priv->csum_flags) /* makes no sense, asks for "re-update" of L4 checksum */ + return false; + + /* no further check here; offset can't be negative so bogus + * offsets can corrupt L4 or payload but not l3 headers. + * We already allow arbitrary l4/inner payload writes. + */ + return true; + case NFT_PAYLOAD_INNER_HEADER: + return true; + case NFT_PAYLOAD_TUN_HEADER: + break; + } + + return false; +} + +/* do not allow arbitrary network header mangling via bogus csum_off. + * We only support ipv4. Only NFPROTO_IPV4 can be checked from control + * plane. + */ +static bool nft_payload_csum_nh_write_ok(const struct nft_payload_set *priv, + const struct nft_pktinfo *pkt) +{ + switch (pkt->state->pf) { + case NFPROTO_IPV4: + /* Warning: NFPROTO_INET was not checked; we can't return true here. */ + return priv->csum_offset == offsetof(struct iphdr, check); + case NFPROTO_IPV6: + return false; + case NFPROTO_BRIDGE: + return pkt->ethertype == htons(ETH_P_IP) && + priv->csum_offset == offsetof(struct iphdr, check); + case NFPROTO_NETDEV: + return pkt->skb->protocol == htons(ETH_P_IP) && + priv->csum_offset == offsetof(struct iphdr, check); + } + + return false; +} + +static bool nft_payload_csum_write_ok(const struct nft_pktinfo *pkt, + const struct nft_payload_set *priv) +{ + switch (priv->base) { + case NFT_PAYLOAD_LL_HEADER: + break; + case NFT_PAYLOAD_NETWORK_HEADER: + return nft_payload_csum_nh_write_ok(priv, pkt); + case NFT_PAYLOAD_TRANSPORT_HEADER: + case NFT_PAYLOAD_INNER_HEADER: + /* neither offsets are validated, offsets cannot be + * negative so real l3 headers cannot be mangled. + */ + return true; + case NFT_PAYLOAD_TUN_HEADER: + break; + } + + return false; +} + static void nft_payload_set_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -861,8 +1104,12 @@ static void nft_payload_set_eval(const struct nft_expr *expr, } offset = skb_mac_header(skb) - skb->data - vlan_hlen; + if (!nft_ll_write_ok(pkt, priv->len + priv->offset + offset)) + goto err; break; case NFT_PAYLOAD_NETWORK_HEADER: + if (!nft_nh_write_ok(pkt, priv, src)) + goto err; offset = skb_network_offset(skb); break; case NFT_PAYLOAD_TRANSPORT_HEADER: @@ -894,6 +1141,7 @@ static void nft_payload_set_eval(const struct nft_expr *expr, tsum = csum_partial(src, priv->len, 0); if (priv->csum_type == NFT_PAYLOAD_CSUM_INET && + nft_payload_csum_write_ok(pkt, priv) && nft_payload_csum_inet(skb, src, fsum, tsum, csum_offset)) goto err; @@ -960,7 +1208,26 @@ static int nft_payload_set_init(const struct nft_ctx *ctx, switch (csum_type) { case NFT_PAYLOAD_CSUM_NONE: + if (priv->csum_offset) /* nonsensical */ + return -EINVAL; + + if (priv->csum_flags == 0) + break; + + /* Userspace requests L4 checksum update, e.g.: + * - IPv6 stateless NAT (no l3 csum) + * - transport header mangling + * - inner data mangling + */ + if (priv->base == NFT_PAYLOAD_NETWORK_HEADER || + priv->base == NFT_PAYLOAD_TRANSPORT_HEADER || + priv->base == NFT_PAYLOAD_INNER_HEADER) + break; + + return -EINVAL; case NFT_PAYLOAD_CSUM_INET: + if (!nft_payload_validate_inet_csum_offset(ctx, priv)) + return -EINVAL; break; case NFT_PAYLOAD_CSUM_SCTP: if (priv->base != NFT_PAYLOAD_TRANSPORT_HEADER) @@ -968,6 +1235,9 @@ static int nft_payload_set_init(const struct nft_ctx *ctx, if (priv->csum_offset != offsetof(struct sctphdr, checksum)) return -EINVAL; + + if (priv->csum_flags) + return -EINVAL; break; default: return -EOPNOTSUPP; diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c index 706c78853f24..978bb0c01106 100644 --- a/net/netfilter/nft_set_pipapo.c +++ b/net/netfilter/nft_set_pipapo.c @@ -342,6 +342,8 @@ #include "nft_set_pipapo_avx2.h" #include "nft_set_pipapo.h" +static void nft_pipapo_abort(const struct nft_set *set); + /** * pipapo_refill() - For each set bit, set bits from selected mapping table item * @map: Bitmap to be scanned for set bits @@ -1296,7 +1298,7 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set, const u8 *start_p, *end_p; int i, bsize_max, err = 0; - if (!m) + if (!m || m->state == NFT_PIPAPO_CLONE_ERR) return -ENOMEM; if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END)) @@ -1367,8 +1369,10 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set, else ret = pipapo_expand(f, start, end, f->groups * f->bb); - if (ret < 0) - return ret; + if (ret < 0) { + err = ret; + goto abort; + } if (f->bsize > bsize_max) bsize_max = f->bsize; @@ -1384,7 +1388,7 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set, err = pipapo_realloc_scratch(m, bsize_max); if (err) - return err; + goto abort; m->bsize_max = bsize_max; } else { @@ -1396,7 +1400,26 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set, pipapo_map(m, rulemap, e); + m->state = NFT_PIPAPO_CLONE_MOD; return 0; +abort: + DEBUG_NET_WARN_ON_ONCE(m->state == NFT_PIPAPO_CLONE_ERR); + + /* Two rollback cases: + * 1) no previous changes. nft_pipapo_abort is not + * guaranteed to be invoked (there might be no further + * add/delete requests coming after this). + * + * 2) we had previous changes: there are transaction + * records pointing to this set. Leave the rollback to + * the transaction handling. + */ + if (m->state == NFT_PIPAPO_CLONE_NEW) + nft_pipapo_abort(set); /* releases m */ + else + m->state = NFT_PIPAPO_CLONE_ERR; + + return err; } /** @@ -1473,6 +1496,7 @@ static struct nft_pipapo_match *pipapo_clone(struct nft_pipapo_match *old) dst++; } + new->state = NFT_PIPAPO_CLONE_NEW; return new; out_mt: @@ -1896,7 +1920,7 @@ nft_pipapo_deactivate(const struct net *net, const struct nft_set *set, /* removal must occur on priv->clone, if we are low on memory * we have no choice and must fail the removal request. */ - if (!m) + if (!m || m->state == NFT_PIPAPO_CLONE_ERR) return NULL; e = pipapo_get(m, (const u8 *)elem->key.val.data, diff --git a/net/netfilter/nft_set_pipapo.h b/net/netfilter/nft_set_pipapo.h index b82abb03576e..a19e980d06ef 100644 --- a/net/netfilter/nft_set_pipapo.h +++ b/net/netfilter/nft_set_pipapo.h @@ -131,9 +131,16 @@ struct nft_pipapo_scratch { unsigned long __map[]; }; +enum nft_pipapo_clone_state { + NFT_PIPAPO_CLONE_NEW, + NFT_PIPAPO_CLONE_MOD, + NFT_PIPAPO_CLONE_ERR, +}; + /** * struct nft_pipapo_match - Data used for lookup and matching * @field_count: Amount of fields in set + * @state: add/delete state; used from control plane * @bsize_max: Maximum lookup table bucket size of all fields, in longs * @scratch: Preallocated per-CPU maps for partial matching results * @rcu: Matching data is swapped on commits @@ -141,6 +148,7 @@ struct nft_pipapo_scratch { */ struct nft_pipapo_match { u8 field_count; + enum nft_pipapo_clone_state state:8; unsigned int bsize_max; struct nft_pipapo_scratch * __percpu *scratch; struct rcu_head rcu; diff --git a/net/qrtr/mhi.c b/net/qrtr/mhi.c index 80e341d2f8a4..3990da1a65dc 100644 --- a/net/qrtr/mhi.c +++ b/net/qrtr/mhi.c @@ -4,7 +4,6 @@ */ #include <linux/mhi.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/skbuff.h> #include <net/sock.h> diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c index cf2dcec6ce5a..9c5b695dba7c 100644 --- a/net/rfkill/rfkill-gpio.c +++ b/net/rfkill/rfkill-gpio.c @@ -7,7 +7,6 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/rfkill.h> #include <linux/of.h> #include <linux/platform_device.h> diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index 58a074651176..09d46e195e33 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c @@ -44,7 +44,7 @@ TC_INDIRECT_SCOPE int tcf_bpf_act(struct sk_buff *skb, tcf_lastuse_update(&prog->tcf_tm); bstats_update(this_cpu_ptr(prog->common.cpu_bstats), skb); - filter = rcu_dereference(prog->filter); + filter = rcu_dereference_bh(prog->filter); if (at_ingress) { __skb_push(skb, skb->mac_len); filter_res = bpf_prog_run_data_pointers(filter, skb); diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index 5434df6ca8ef..27088760eff4 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -346,6 +346,8 @@ static int dualpi2_skb_classify(struct dualpi2_sched_data *q, struct tcf_proto *fl; int result; + cb->classified = DUALPI2_C_CLASSIC; + dualpi2_read_ect(skb); if (cb->ect & q->ecn_mask) { cb->classified = DUALPI2_C_L4S; @@ -359,10 +361,8 @@ static int dualpi2_skb_classify(struct dualpi2_sched_data *q, } fl = rcu_dereference_bh(q->tcf_filters); - if (!fl) { - cb->classified = DUALPI2_C_CLASSIC; + if (!fl) return NET_XMIT_SUCCESS; - } result = tcf_classify(skb, NULL, fl, &res, false); if (result >= 0) { diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c index 1e25b75daae2..d85cb0263b67 100644 --- a/net/sched/sch_hhf.c +++ b/net/sched/sch_hhf.c @@ -462,12 +462,39 @@ begin: return skb; } +static void hhf_reset_classifier(struct hhf_sched_data *q) +{ + int i; + + if (!q->hh_flows) + return; + + for (i = 0; i < HH_FLOWS_CNT; i++) { + struct hh_flow_state *flow, *next; + struct list_head *head = &q->hh_flows[i]; + + list_for_each_entry_safe(flow, next, head, flowchain) { + list_del(&flow->flowchain); + kfree(flow); + } + } + WRITE_ONCE(q->hh_flows_current_cnt, 0); + + for (i = 0; i < HHF_ARRAYS_CNT; i++) { + if (q->hhf_valid_bits[i]) + bitmap_zero(q->hhf_valid_bits[i], HHF_ARRAYS_LEN); + } + q->hhf_arrays_reset_timestamp = hhf_time_stamp(); +} + static void hhf_reset(struct Qdisc *sch) { + struct hhf_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; while ((skb = hhf_dequeue(sch)) != NULL) rtnl_kfree_skbs(skb, skb); + hhf_reset_classifier(q); } static void hhf_destroy(struct Qdisc *sch) diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c index 4e465d11e3d7..a467dd122369 100644 --- a/net/sched/sch_multiq.c +++ b/net/sched/sch_multiq.c @@ -103,7 +103,7 @@ static struct sk_buff *multiq_dequeue(struct Qdisc *sch) if (!netif_xmit_stopped( netdev_get_tx_queue(qdisc_dev(sch), q->curband))) { qdisc = q->queues[q->curband]; - skb = qdisc->dequeue(qdisc); + skb = qdisc_dequeue_peeked(qdisc); if (skb) { qdisc_bstats_update(sch, skb); qdisc_qlen_dec(sch); diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c index 558987d9b977..299234a5f0fe 100644 --- a/net/sched/sch_taprio.c +++ b/net/sched/sch_taprio.c @@ -749,7 +749,7 @@ static struct sk_buff *taprio_dequeue_from_txq(struct Qdisc *sch, int txq, return NULL; skip_peek_checks: - skb = child->ops->dequeue(child); + skb = qdisc_dequeue_peeked(child); if (unlikely(!skb)) return NULL; diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index e7bbc9e5174d..24ba31f8c828 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -52,7 +52,8 @@ struct teql_master { struct Qdisc_ops qops; struct net_device *dev; - struct Qdisc *slaves; + struct Qdisc __rcu *slaves; + spinlock_t slaves_lock; /* serializes writes to ->slaves */ struct list_head master_list; unsigned long tx_bytes; unsigned long tx_packets; @@ -61,7 +62,7 @@ struct teql_master { }; struct teql_sched_data { - struct Qdisc *next; + struct Qdisc __rcu *next; struct teql_master *m; struct sk_buff_head q; }; @@ -101,7 +102,9 @@ teql_dequeue(struct Qdisc *sch) if (skb == NULL) { struct net_device *m = qdisc_dev(q); if (m) { - dat->m->slaves = sch; + spin_lock_bh(&dat->m->slaves_lock); + rcu_assign_pointer(dat->m->slaves, sch); + spin_unlock_bh(&dat->m->slaves_lock); netif_wake_queue(m); } } else { @@ -132,34 +135,49 @@ teql_destroy(struct Qdisc *sch) struct Qdisc *q, *prev; struct teql_sched_data *dat = qdisc_priv(sch); struct teql_master *master = dat->m; + struct netdev_queue *txq = NULL; + bool reset_master_queue = false; if (!master) return; - prev = master->slaves; + spin_lock_bh(&master->slaves_lock); + prev = rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock)); if (prev) { do { - q = NEXT_SLAVE(prev); - if (q == sch) { - NEXT_SLAVE(prev) = NEXT_SLAVE(q); - if (q == master->slaves) { - master->slaves = NEXT_SLAVE(q); - if (q == master->slaves) { - struct netdev_queue *txq; - - txq = netdev_get_tx_queue(master->dev, 0); - master->slaves = NULL; - - dev_reset_queue(master->dev, - txq, NULL); - } - } - skb_queue_purge(&dat->q); - break; + struct Qdisc *head, *next; + + q = rcu_dereference_protected(NEXT_SLAVE(prev), + lockdep_is_held(&master->slaves_lock)); + if (q != sch) { + prev = q; + continue; } - } while ((prev = q) != master->slaves); + next = rcu_dereference_protected(NEXT_SLAVE(q), + lockdep_is_held(&master->slaves_lock)); + rcu_assign_pointer(NEXT_SLAVE(prev), next); + + head = rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock)); + if (q == head) { + rcu_assign_pointer(master->slaves, next); + if (q == next) { + txq = netdev_get_tx_queue(master->dev, 0); + rcu_assign_pointer(master->slaves, NULL); + reset_master_queue = true; + } + } + skb_queue_purge(&dat->q); + break; + } while (prev != rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock))); } + spin_unlock_bh(&master->slaves_lock); + + if (reset_master_queue) + dev_reset_queue(master->dev, txq, NULL); } static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, @@ -168,6 +186,7 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, struct net_device *dev = qdisc_dev(sch); struct teql_master *m = (struct teql_master *)sch->ops; struct teql_sched_data *q = qdisc_priv(sch); + struct Qdisc *first; if (dev->hard_header_len > m->dev->hard_header_len) return -EINVAL; @@ -184,7 +203,9 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, skb_queue_head_init(&q->q); - if (m->slaves) { + spin_lock_bh(&m->slaves_lock); + first = rcu_dereference_protected(m->slaves, lockdep_is_held(&m->slaves_lock)); + if (first) { if (m->dev->flags & IFF_UP) { if ((m->dev->flags & IFF_POINTOPOINT && !(dev->flags & IFF_POINTOPOINT)) || @@ -192,8 +213,10 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, !(dev->flags & IFF_BROADCAST)) || (m->dev->flags & IFF_MULTICAST && !(dev->flags & IFF_MULTICAST)) || - dev->mtu < m->dev->mtu) + dev->mtu < m->dev->mtu) { + spin_unlock_bh(&m->slaves_lock); return -EINVAL; + } } else { if (!(dev->flags&IFF_POINTOPOINT)) m->dev->flags &= ~IFF_POINTOPOINT; @@ -204,14 +227,17 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, if (dev->mtu < m->dev->mtu) m->dev->mtu = dev->mtu; } - q->next = NEXT_SLAVE(m->slaves); - NEXT_SLAVE(m->slaves) = sch; + rcu_assign_pointer(q->next, + rcu_dereference_protected(NEXT_SLAVE(first), + lockdep_is_held(&m->slaves_lock))); + rcu_assign_pointer(NEXT_SLAVE(first), sch); } else { - q->next = sch; - m->slaves = sch; + rcu_assign_pointer(q->next, sch); + rcu_assign_pointer(m->slaves, sch); m->dev->mtu = dev->mtu; m->dev->flags = (m->dev->flags&~FMASK)|(dev->flags&FMASK); } + spin_unlock_bh(&m->slaves_lock); return 0; } @@ -285,7 +311,9 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) int subq = skb_get_queue_mapping(skb); struct sk_buff *skb_res = NULL; - start = master->slaves; + rcu_read_lock_bh(); + + start = rcu_dereference_bh(master->slaves); restart: nores = 0; @@ -317,10 +345,17 @@ restart: netdev_start_xmit(skb, slave, slave_txq, false) == NETDEV_TX_OK) { __netif_tx_unlock(slave_txq); - master->slaves = NEXT_SLAVE(q); + spin_lock_bh(&master->slaves_lock); + if (rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock)) == q) + rcu_assign_pointer(master->slaves, + rcu_dereference_protected(NEXT_SLAVE(q), + lockdep_is_held(&master->slaves_lock))); + spin_unlock_bh(&master->slaves_lock); netif_wake_queue(dev); master->tx_packets++; master->tx_bytes += length; + rcu_read_unlock_bh(); return NETDEV_TX_OK; } __netif_tx_unlock(slave_txq); @@ -329,14 +364,21 @@ restart: busy = 1; break; case 1: - master->slaves = NEXT_SLAVE(q); + spin_lock_bh(&master->slaves_lock); + if (rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock)) == q) + rcu_assign_pointer(master->slaves, + rcu_dereference_protected(NEXT_SLAVE(q), + lockdep_is_held(&master->slaves_lock))); + spin_unlock_bh(&master->slaves_lock); + rcu_read_unlock_bh(); return NETDEV_TX_OK; default: nores = 1; break; } __skb_pull(skb, skb_network_offset(skb)); - } while ((q = NEXT_SLAVE(q)) != start); + } while ((q = rcu_dereference_bh(NEXT_SLAVE(q))) != start); if (nores && skb_res == NULL) { skb_res = skb; @@ -345,29 +387,32 @@ restart: if (busy) { netif_stop_queue(dev); + rcu_read_unlock_bh(); return NETDEV_TX_BUSY; } master->tx_errors++; drop: master->tx_dropped++; + rcu_read_unlock_bh(); dev_kfree_skb(skb); return NETDEV_TX_OK; } static int teql_master_open(struct net_device *dev) { - struct Qdisc *q; + struct Qdisc *q, *first; struct teql_master *m = netdev_priv(dev); int mtu = 0xFFFE; unsigned int flags = IFF_NOARP | IFF_MULTICAST; - if (m->slaves == NULL) + first = rtnl_dereference(m->slaves); + if (!first) return -EUNATCH; flags = FMASK; - q = m->slaves; + q = first; do { struct net_device *slave = qdisc_dev(q); @@ -389,7 +434,7 @@ static int teql_master_open(struct net_device *dev) flags &= ~IFF_BROADCAST; if (!(slave->flags&IFF_MULTICAST)) flags &= ~IFF_MULTICAST; - } while ((q = NEXT_SLAVE(q)) != m->slaves); + } while ((q = rtnl_dereference(NEXT_SLAVE(q))) != first); m->dev->mtu = mtu; m->dev->flags = (m->dev->flags&~FMASK) | flags; @@ -417,14 +462,15 @@ static void teql_master_stats64(struct net_device *dev, static int teql_master_mtu(struct net_device *dev, int new_mtu) { struct teql_master *m = netdev_priv(dev); - struct Qdisc *q; + struct Qdisc *q, *first; - q = m->slaves; + first = rtnl_dereference(m->slaves); + q = first; if (q) { do { if (new_mtu > qdisc_dev(q)->mtu) return -EINVAL; - } while ((q = NEXT_SLAVE(q)) != m->slaves); + } while ((q = rtnl_dereference(NEXT_SLAVE(q))) != first); } WRITE_ONCE(dev->mtu, new_mtu); @@ -444,6 +490,7 @@ static __init void teql_master_setup(struct net_device *dev) struct teql_master *master = netdev_priv(dev); struct Qdisc_ops *ops = &master->qops; + spin_lock_init(&master->slaves_lock); master->dev = dev; ops->priv_size = sizeof(struct teql_sched_data); diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 587b0017a67d..cf335494bffe 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -663,8 +663,9 @@ static void sctp_free_addr_wq(struct net *net) struct sctp_sockaddr_entry *addrw; struct sctp_sockaddr_entry *temp; + timer_shutdown_sync(&net->sctp.addr_wq_timer); + spin_lock_bh(&net->sctp.addr_wq_lock); - timer_delete(&net->sctp.addr_wq_timer); list_for_each_entry_safe(addrw, temp, &net->sctp.addr_waitq, list) { list_del(&addrw->list); kfree(addrw); diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 41958b8e59fd..8adac9e0cd66 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1761,6 +1761,8 @@ struct sctp_association *sctp_unpack_cookie( bear_cookie = &cookie->c; ch = (struct sctp_chunkhdr *)(bear_cookie + 1); + if (ch->type != SCTP_CID_INIT) + goto malformed; chlen = ntohs(ch->length); if (chlen < sizeof(struct sctp_init_chunk)) goto malformed; @@ -2298,7 +2300,8 @@ int sctp_verify_init(struct net *net, const struct sctp_endpoint *ep, * VIOLATION error. We build the ERROR chunk here and let the normal * error handling code build and send the packet. */ - if (param.v != (void *)chunk->chunk_end) + if (param.v != (void *)peer_init + + SCTP_PAD4(ntohs(peer_init->chunk_hdr.length))) return sctp_process_inv_paramlength(asoc, param.p, chunk, errp); /* The only missing mandatory param possible today is diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 8e920cef0858..d23d935e128e 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -707,11 +707,12 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, struct sctp_cmd_seq *commands) { struct sctp_ulpevent *ev, *ai_ev = NULL, *auth_ev = NULL; + struct sctp_chunk *err_chk_p = NULL; struct sctp_association *new_asoc; struct sctp_init_chunk *peer_init; struct sctp_chunk *chunk = arg; - struct sctp_chunk *err_chk_p; struct sctp_chunk *repl; + enum sctp_cid cid; struct sock *sk; int error = 0; @@ -785,6 +786,19 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, } } + peer_init = (struct sctp_init_chunk *)(chunk->subh.cookie_hdr + 1); + cid = peer_init->chunk_hdr.type; + if (!sctp_sk(sk)->cookie_auth_enable && + !sctp_verify_init(net, ep, asoc, cid, peer_init, chunk, + &err_chk_p)) { + sctp_association_free(new_asoc); + if (err_chk_p) + sctp_chunk_free(err_chk_p); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); + } + if (err_chk_p) + sctp_chunk_free(err_chk_p); + if (security_sctp_assoc_request(new_asoc, chunk->head_skb ?: chunk->skb)) { sctp_association_free(new_asoc); return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); @@ -798,7 +812,6 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, /* This is a brand-new association, so these are not yet side * effects--it is safe to run them here. */ - peer_init = (struct sctp_init_chunk *)(chunk->subh.cookie_hdr + 1); if (!sctp_process_init(new_asoc, chunk, &chunk->subh.cookie_hdr->c.peer_addr, peer_init, GFP_ATOMIC)) @@ -2215,10 +2228,12 @@ enum sctp_disposition sctp_sf_do_5_2_4_dupcook( void *arg, struct sctp_cmd_seq *commands) { + struct sctp_chunk *err_chk_p = NULL; struct sctp_association *new_asoc; + struct sctp_init_chunk *peer_init; struct sctp_chunk *chunk = arg; enum sctp_disposition retval; - struct sctp_chunk *err_chk_p; + enum sctp_cid cid; int error = 0; char action; @@ -2287,6 +2302,21 @@ enum sctp_disposition sctp_sf_do_5_2_4_dupcook( switch (action) { case 'A': /* Association restart. */ case 'B': /* Collision case B. */ + peer_init = (struct sctp_init_chunk *) + (chunk->subh.cookie_hdr + 1); + cid = peer_init->chunk_hdr.type; + if (!sctp_sk(ep->base.sk)->cookie_auth_enable && + !sctp_verify_init(net, ep, asoc, cid, peer_init, chunk, + &err_chk_p)) { + sctp_association_free(new_asoc); + if (err_chk_p) + sctp_chunk_free(err_chk_p); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, + commands); + } + if (err_chk_p) + sctp_chunk_free(err_chk_p); + fallthrough; case 'D': /* Collision case D. */ /* Update socket peer label if first association. */ if (security_sctp_assoc_request((struct sctp_association *)asoc, diff --git a/net/sctp/socket.c b/net/sctp/socket.c index c8481461f7d8..c7b9e325ec1c 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4111,8 +4111,9 @@ static int sctp_setsockopt_reset_streams(struct sock *sk, if (optlen < sizeof(*params)) return -EINVAL; /* srs_number_streams is u16, so optlen can't be bigger than this. */ - optlen = min_t(unsigned int, optlen, USHRT_MAX + - sizeof(__u16) * sizeof(*params)); + optlen = min_t(unsigned int, optlen, + struct_size_t(struct sctp_reset_streams, srs_stream_list, + USHRT_MAX)); if (params->srs_number_streams * sizeof(__u16) > optlen - sizeof(*params)) @@ -4598,8 +4599,8 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, if (optlen > 0) { /* Trim it to the biggest size sctp sockopt may need if necessary */ optlen = min_t(unsigned int, optlen, - PAGE_ALIGN(USHRT_MAX + - sizeof(__u16) * sizeof(struct sctp_reset_streams))); + PAGE_ALIGN(struct_size_t(struct sctp_reset_streams, + srs_stream_list, USHRT_MAX))); kopt = memdup_sockptr(optval, optlen); if (IS_ERR(kopt)) return PTR_ERR(kopt); diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index 76a1585d3f6b..10d1ec593084 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -497,12 +497,13 @@ void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l, */ int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, struct tipc_msg *hdr, - struct sk_buff_head *retrq) + struct sk_buff_head *retrq, bool *valid) { struct sk_buff_head *inputq = &tipc_bc_base(net)->inputq; struct tipc_gap_ack_blks *ga; struct sk_buff_head xmitq; int rc = 0; + u16 glen; __skb_queue_head_init(&xmitq); @@ -510,13 +511,18 @@ int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, if (msg_type(hdr) != STATE_MSG) { tipc_link_bc_init_rcv(l, hdr); } else if (!msg_bc_ack_invalid(hdr)) { - tipc_get_gap_ack_blks(&ga, l, hdr, false); - if (!sysctl_tipc_bc_retruni) - retrq = &xmitq; - rc = tipc_link_bc_ack_rcv(l, msg_bcast_ack(hdr), - msg_bc_gap(hdr), ga, &xmitq, - retrq); - rc |= tipc_link_bc_sync_rcv(l, hdr, &xmitq); + glen = tipc_get_gap_ack_blks(&ga, l, hdr, false); + if (glen > msg_data_sz(hdr)) { + /* Malformed Gap ACK blocks; caller drops the msg */ + *valid = false; + } else { + if (!sysctl_tipc_bc_retruni) + retrq = &xmitq; + rc = tipc_link_bc_ack_rcv(l, msg_bcast_ack(hdr), + msg_bc_gap(hdr), ga, &xmitq, + retrq); + rc |= tipc_link_bc_sync_rcv(l, hdr, &xmitq); + } } tipc_bcast_unlock(net); diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h index 2d9352dc7b0e..55d17b5413e1 100644 --- a/net/tipc/bcast.h +++ b/net/tipc/bcast.h @@ -97,7 +97,7 @@ void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l, struct tipc_msg *hdr); int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, struct tipc_msg *hdr, - struct sk_buff_head *retrq); + struct sk_buff_head *retrq, bool *valid); int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg, struct tipc_link *bcl); int tipc_nl_bc_link_set(struct net *net, struct nlattr *attrs[]); diff --git a/net/tipc/node.c b/net/tipc/node.c index 97aa970a0d83..8e4ef2630ae4 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -1831,12 +1831,15 @@ static void tipc_node_mcast_rcv(struct tipc_node *n) } static void tipc_node_bc_sync_rcv(struct tipc_node *n, struct tipc_msg *hdr, - int bearer_id, struct sk_buff_head *xmitq) + int bearer_id, struct sk_buff_head *xmitq, + bool *valid) { struct tipc_link *ucl; int rc; - rc = tipc_bcast_sync_rcv(n->net, n->bc_entry.link, hdr, xmitq); + rc = tipc_bcast_sync_rcv(n->net, n->bc_entry.link, hdr, xmitq, valid); + if (!*valid) + return; if (rc & TIPC_LINK_DOWN_EVT) { tipc_node_reset_links(n); @@ -2140,12 +2143,18 @@ rcv: /* Ensure broadcast reception is in synch with peer's send state */ if (unlikely(usr == LINK_PROTOCOL)) { + bool valid = true; + if (unlikely(skb_linearize(skb))) { tipc_node_put(n); goto discard; } hdr = buf_msg(skb); - tipc_node_bc_sync_rcv(n, hdr, bearer_id, &xmitq); + tipc_node_bc_sync_rcv(n, hdr, bearer_id, &xmitq, &valid); + if (!valid) { + tipc_node_put(n); + goto discard; + } } else if (unlikely(tipc_link_acked(n->bc_entry.link) != bc_ack)) { tipc_bcast_ack_rcv(net, n->bc_entry.link, hdr); } |
