Commit message: net: hns: Fix a skb used after free bug

skb maybe freed in hns_nic_net_xmit_hw() and return NETDEV_TX_OK,
which cause hns_nic_net_xmit to use a freed skb.

BUG: KASAN: use-after-free in hns_nic_net_xmit_hw+0x62c/0x940...
	[17659.112635]      alloc_debug_processing+0x18c/0x1a0
	[17659.117208]      __slab_alloc+0x52c/0x560
	[17659.120909]      kmem_cache_alloc_node+0xac/0x2c0
	[17659.125309]      __alloc_skb+0x6c/0x260
	[17659.128837]      tcp_send_ack+0x8c/0x280
	[17659.132449]      __tcp_ack_snd_check+0x9c/0xf0
	[17659.136587]      tcp_rcv_established+0x5a4/0xa70
	[17659.140899]      tcp_v4_do_rcv+0x27c/0x620
	[17659.144687]      tcp_prequeue_process+0x108/0x170
	[17659.149085]      tcp_recvmsg+0x940/0x1020
	[17659.152787]      inet_recvmsg+0x124/0x180
	[17659.156488]      sock_recvmsg+0x64/0x80
	[17659.160012]      SyS_recvfrom+0xd8/0x180
	[17659.163626]      __sys_trace_return+0x0/0x4
	[17659.167506] INFO: Freed in kfree_skbmem+0xa0/0xb0 age=23 cpu=1 pid=13
	[17659.174000]      free_debug_processing+0x1d4/0x2c0
	[17659.178486]      __slab_free+0x240/0x390
	[17659.182100]      kmem_cache_free+0x24c/0x270
	[17659.186062]      kfree_skbmem+0xa0/0xb0
	[17659.189587]      __kfree_skb+0x28/0x40
	[17659.193025]      napi_gro_receive+0x168/0x1c0
	[17659.197074]      hns_nic_rx_up_pro+0x58/0x90
	[17659.201038]      hns_nic_rx_poll_one+0x518/0xbc0
	[17659.205352]      hns_nic_common_poll+0x94/0x140
	[17659.209576]      net_rx_action+0x458/0x5e0
	[17659.213363]      __do_softirq+0x1b8/0x480
	[17659.217062]      run_ksoftirqd+0x64/0x80
	[17659.220679]      smpboot_thread_fn+0x224/0x310
	[17659.224821]      kthread+0x150/0x170
	[17659.228084]      ret_from_fork+0x10/0x40

	BUG: KASAN: use-after-free in hns_nic_net_xmit+0x8c/0xc0...
	[17751.080490]      __slab_alloc+0x52c/0x560
	[17751.084188]      kmem_cache_alloc+0x244/0x280
	[17751.088238]      __build_skb+0x40/0x150
	[17751.091764]      build_skb+0x28/0x100
	[17751.095115]      __alloc_rx_skb+0x94/0x150
	[17751.098900]      __napi_alloc_skb+0x34/0x90
	[17751.102776]      hns_nic_rx_poll_one+0x180/0xbc0
	[17751.107097]      hns_nic_common_poll+0x94/0x140
	[17751.111333]      net_rx_action+0x458/0x5e0
	[17751.115123]      __do_softirq+0x1b8/0x480
	[17751.118823]      run_ksoftirqd+0x64/0x80
	[17751.122437]      smpboot_thread_fn+0x224/0x310
	[17751.126575]      kthread+0x150/0x170
	[17751.129838]      ret_from_fork+0x10/0x40
	[17751.133454] INFO: Freed in kfree_skbmem+0xa0/0xb0 age=19 cpu=7 pid=43
	[17751.139951]      free_debug_processing+0x1d4/0x2c0
	[17751.144436]      __slab_free+0x240/0x390
	[17751.148051]      kmem_cache_free+0x24c/0x270
	[17751.152014]      kfree_skbmem+0xa0/0xb0
	[17751.155543]      __kfree_skb+0x28/0x40
	[17751.159022]      napi_gro_receive+0x168/0x1c0
	[17751.163074]      hns_nic_rx_up_pro+0x58/0x90
	[17751.167041]      hns_nic_rx_poll_one+0x518/0xbc0
	[17751.171358]      hns_nic_common_poll+0x94/0x140
	[17751.175585]      net_rx_action+0x458/0x5e0
	[17751.179373]      __do_softirq+0x1b8/0x480
	[17751.183076]      run_ksoftirqd+0x64/0x80
	[17751.186691]      smpboot_thread_fn+0x224/0x310
	[17751.190826]      kthread+0x150/0x170
	[17751.194093]      ret_from_fork+0x10/0x40

Fixes: 13ac695e7ea1 ("net:hns: Add support of Hip06 SoC to the Hislicon Network Subsystem")
Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
Signed-off-by: lipeng <lipeng321@huawei.com>
Reported-by: Jun He <hjat2005@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
CVE: CVE-2017-18218
CWE: CWE-416
Vulnerability description: In drivers/net/ethernet/hisilicon/hns/hns_enet.c in the Linux kernel before 4.13, local users can cause a denial of service (use-after-free and BUG) or possibly have unspecified other impact by leveraging differences in skb handling between hns_nic_net_xmit_hw and hns_nic_net_xmit.

Project: linux
File: hns_enet.c

static void fill_desc(struct hnae_ring *ring, void *priv,
		      int size, dma_addr_t dma, int frag_end,
		      int buf_num, enum hns_desc_type type, int mtu)
{
	struct hnae_desc *desc = &ring->desc[ring->next_to_use];
	struct hnae_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use];
	struct sk_buff *skb;
	__be16 protocol;
	u32 ip_offset;
	u32 asid_bufnum_pid = 0;
	u32 flag_ipoffset = 0;

	desc_cb->priv = priv;
	desc_cb->length = size;
	desc_cb->dma = dma;
	desc_cb->type = type;

	desc->addr = cpu_to_le64(dma);
	desc->tx.send_size = cpu_to_le16((u16)size);

	/*config bd buffer end */
	flag_ipoffset |= 1 << HNS_TXD_VLD_B;

	asid_bufnum_pid |= buf_num << HNS_TXD_BUFNUM_S;

	if (type == DESC_TYPE_SKB) {
		skb = (struct sk_buff *)priv;

		if (skb->ip_summed == CHECKSUM_PARTIAL) {
			protocol = skb->protocol;
			ip_offset = ETH_HLEN;

			/*if it is a SW VLAN check the next protocol*/
			if (protocol == htons(ETH_P_8021Q)) {
				ip_offset += VLAN_HLEN;
				protocol = vlan_get_protocol(skb);
				skb->protocol = protocol;
			}

			if (skb->protocol == htons(ETH_P_IP)) {
				flag_ipoffset |= 1 << HNS_TXD_L3CS_B;
				/* check for tcp/udp header */
				flag_ipoffset |= 1 << HNS_TXD_L4CS_B;

			} else if (skb->protocol == htons(ETH_P_IPV6)) {
				/* ipv6 has not l3 cs, check for L4 header */
				flag_ipoffset |= 1 << HNS_TXD_L4CS_B;
			}

			flag_ipoffset |= ip_offset << HNS_TXD_IPOFFSET_S;
		}
	}

	flag_ipoffset |= frag_end << HNS_TXD_FE_B;

	desc->tx.asid_bufnum_pid = cpu_to_le16(asid_bufnum_pid);
	desc->tx.flag_ipoffset = cpu_to_le32(flag_ipoffset);

	ring_ptr_move_fw(ring, next_to_use);
}

Project: linux
File: hns_enet.c

static int hns_nic_common_poll(struct napi_struct *napi, int budget)
{
	int clean_complete = 0;
	struct hns_nic_ring_data *ring_data =
		container_of(napi, struct hns_nic_ring_data, napi);
	struct hnae_ring *ring = ring_data->ring;

try_again:
	clean_complete += ring_data->poll_one(
				ring_data, budget - clean_complete,
				ring_data->ex_process);

	if (clean_complete < budget) {
		if (ring_data->fini_process(ring_data)) {
			napi_complete(napi);
			ring->q->handle->dev->ops->toggle_ring_irq(ring, 0);
		} else {
			goto try_again;
		}
	}

	return clean_complete;
}

Project: linux
File: hns_enet.c

static unsigned int hns_nic_get_headlen(unsigned char *data, u32 flag,
					unsigned int max_size)
{
	unsigned char *network;
	u8 hlen;

	/* this should never happen, but better safe than sorry */
	if (max_size < ETH_HLEN)
		return max_size;

	/* initialize network frame pointer */
	network = data;

	/* set first protocol and move network header forward */
	network += ETH_HLEN;

	/* handle any vlan tag if present */
	if (hnae_get_field(flag, HNS_RXD_VLAN_M, HNS_RXD_VLAN_S)
		== HNS_RX_FLAG_VLAN_PRESENT) {
		if ((typeof(max_size))(network - data) > (max_size - VLAN_HLEN))
			return max_size;

		network += VLAN_HLEN;
	}

	/* handle L3 protocols */
	if (hnae_get_field(flag, HNS_RXD_L3ID_M, HNS_RXD_L3ID_S)
		== HNS_RX_FLAG_L3ID_IPV4) {
		if ((typeof(max_size))(network - data) >
		    (max_size - sizeof(struct iphdr)))
			return max_size;

		/* access ihl as a u8 to avoid unaligned access on ia64 */
		hlen = (network[0] & 0x0F) << 2;

		/* verify hlen meets minimum size requirements */
		if (hlen < sizeof(struct iphdr))
			return network - data;

		/* record next protocol if header is present */
	} else if (hnae_get_field(flag, HNS_RXD_L3ID_M, HNS_RXD_L3ID_S)
		== HNS_RX_FLAG_L3ID_IPV6) {
		if ((typeof(max_size))(network - data) >
		    (max_size - sizeof(struct ipv6hdr)))
			return max_size;

		/* record next protocol */
		hlen = sizeof(struct ipv6hdr);
	} else {
		return network - data;
	}

	/* relocate pointer to start of L4 header */
	network += hlen;

	/* finally sort out TCP/UDP */
	if (hnae_get_field(flag, HNS_RXD_L4ID_M, HNS_RXD_L4ID_S)
		== HNS_RX_FLAG_L4ID_TCP) {
		if ((typeof(max_size))(network - data) >
		    (max_size - sizeof(struct tcphdr)))
			return max_size;

		/* access doff as a u8 to avoid unaligned access on ia64 */
		hlen = (network[12] & 0xF0) >> 2;

		/* verify hlen meets minimum size requirements */
		if (hlen < sizeof(struct tcphdr))
			return network - data;

		network += hlen;
	} else if (hnae_get_field(flag, HNS_RXD_L4ID_M, HNS_RXD_L4ID_S)
		== HNS_RX_FLAG_L4ID_UDP) {
		if ((typeof(max_size))(network - data) >
		    (max_size - sizeof(struct udphdr)))
			return max_size;

		network += sizeof(struct udphdr);
	}

	/* If everything has gone correctly network should be the
	 * data section of the packet and will be the end of the header.
	 * If not then it probably represents the end of the last recognized
	 * header.
	 */
	if ((typeof(max_size))(network - data) < max_size)
		return network - data;
	else
		return max_size;
}

Project: linux
File: hns_enet.c

static int hns_nic_init_ring_data(struct hns_nic_priv *priv)
{
	struct hnae_handle *h = priv->ae_handle;
	struct hns_nic_ring_data *rd;
	bool is_ver1 = AE_IS_VER1(priv->enet_ver);
	int i;

	if (h->q_num > NIC_MAX_Q_PER_VF) {
		netdev_err(priv->netdev, "too much queue (%d)\n", h->q_num);
		return -EINVAL;
	}

	priv->ring_data = kzalloc(h->q_num * sizeof(*priv->ring_data) * 2,
				  GFP_KERNEL);
	if (!priv->ring_data)
		return -ENOMEM;

	for (i = 0; i < h->q_num; i++) {
		rd = &priv->ring_data[i];
		rd->queue_index = i;
		rd->ring = &h->qs[i]->tx_ring;
		rd->poll_one = hns_nic_tx_poll_one;
		rd->fini_process = is_ver1 ? hns_nic_tx_fini_pro :
			hns_nic_tx_fini_pro_v2;

		netif_napi_add(priv->netdev, &rd->napi,
			       hns_nic_common_poll, NIC_TX_CLEAN_MAX_NUM);
		rd->ring->irq_init_flag = RCB_IRQ_NOT_INITED;
	}
	for (i = h->q_num; i < h->q_num * 2; i++) {
		rd = &priv->ring_data[i];
		rd->queue_index = i - h->q_num;
		rd->ring = &h->qs[i - h->q_num]->rx_ring;
		rd->poll_one = hns_nic_rx_poll_one;
		rd->ex_process = hns_nic_rx_up_pro;
		rd->fini_process = is_ver1 ? hns_nic_rx_fini_pro :
			hns_nic_rx_fini_pro_v2;

		netif_napi_add(priv->netdev, &rd->napi,
			       hns_nic_common_poll, NIC_RX_CLEAN_MAX_NUM);
		rd->ring->irq_init_flag = RCB_IRQ_NOT_INITED;
	}

	return 0;
}

Project: linux
File: hns_enet.c

static void hns_nic_reuse_page(struct sk_buff *skb, int i,
			       struct hnae_ring *ring, int pull_len,
			       struct hnae_desc_cb *desc_cb)
{
	struct hnae_desc *desc;
	int truesize, size;
	int last_offset;
	bool twobufs;

	twobufs = ((PAGE_SIZE < 8192) &&
		hnae_buf_size(ring) == HNS_BUFFER_SIZE_2048);

	desc = &ring->desc[ring->next_to_clean];
	size = le16_to_cpu(desc->rx.size);

	if (twobufs) {
		truesize = hnae_buf_size(ring);
	} else {
		truesize = ALIGN(size, L1_CACHE_BYTES);
		last_offset = hnae_page_size(ring) - hnae_buf_size(ring);
	}

	skb_add_rx_frag(skb, i, desc_cb->priv, desc_cb->page_offset + pull_len,
			size - pull_len, truesize - pull_len);

	 /* avoid re-using remote pages,flag default unreuse */
	if (unlikely(page_to_nid(desc_cb->priv) != numa_node_id()))
		return;

	if (twobufs) {
		/* if we are only owner of page we can reuse it */
		if (likely(page_count(desc_cb->priv) == 1)) {
			/* flip page offset to other buffer */
			desc_cb->page_offset ^= truesize;

			desc_cb->reuse_flag = 1;
			/* bump ref count on page before it is given*/
			get_page(desc_cb->priv);
		}
		return;
	}

	/* move offset up to the next cache line */
	desc_cb->page_offset += truesize;

	if (desc_cb->page_offset <= last_offset) {
		desc_cb->reuse_flag = 1;
		/* bump ref count on page before it is given*/
		get_page(desc_cb->priv);
	}
}

Project: linux
File: hns_enet.c

static int hns_nic_ring_open(struct net_device *netdev, int idx)
{
	struct hns_nic_priv *priv = netdev_priv(netdev);
	struct hnae_handle *h = priv->ae_handle;

	napi_enable(&priv->ring_data[idx].napi);

	enable_irq(priv->ring_data[idx].ring->irq);
	h->dev->ops->toggle_ring_irq(priv->ring_data[idx].ring, 0);

	return 0;
}

Project: linux
File: hns_enet.c

static void unfill_desc(struct hnae_ring *ring)
{
	ring_ptr_move_bw(ring, next_to_use);
}