Subject: [PATCH 9/12]: spidernet: fix racy double-free of skb From: Linas Vepstas It appears that under certain circumstances, a race will result in a double-free of an skb. This patch null's out the skb pointer upon the skb free, avoiding the inadvertent deref of bogus data. The next patch fixes the actual race. Signed-off-by: Linas Vepstas Cc: Jens Osterkamp Cc: Kou Ishizaki Signed-off-by: Arnd Bergmann --- Sent on 9 Feb 2007 Fix an unlikely race in the transmit stuff, per Jim on 10 January 2007 and bugzilla report. drivers/net/spider_net.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) Index: linux-2.6/drivers/net/spider_net.c =================================================================== --- linux-2.6.orig/drivers/net/spider_net.c +++ linux-2.6/drivers/net/spider_net.c @@ -395,10 +395,11 @@ spider_net_free_rx_chain_contents(struct descr = card->rx_chain.head; do { if (descr->skb) { - dev_kfree_skb(descr->skb); pci_unmap_single(card->pdev, descr->hwdescr->buf_addr, SPIDER_NET_MAX_FRAME, PCI_DMA_BIDIRECTIONAL); + dev_kfree_skb(descr->skb); + descr->skb = NULL; } descr = descr->next; } while (descr != card->rx_chain.head); @@ -452,6 +453,7 @@ spider_net_prepare_rx_descr(struct spide SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE); if (pci_dma_mapping_error(buf)) { dev_kfree_skb_any(descr->skb); + descr->skb = NULL; if (netif_msg_rx_err(card) && net_ratelimit()) pr_err("Could not iommu-map rx buffer\n"); card->spider_stats.rx_iommu_map_error++; @@ -681,6 +683,7 @@ static int spider_net_prepare_tx_descr(struct spider_net_card *card, struct sk_buff *skb) { + struct spider_net_descr_chain *chain = &card->tx_chain; struct spider_net_descr *descr; struct spider_net_hw_descr *hwdescr; dma_addr_t buf; @@ -695,10 +698,15 @@ spider_net_prepare_tx_descr(struct spide return -ENOMEM; } - spin_lock_irqsave(&card->tx_chain.lock, flags); + spin_lock_irqsave(&chain->lock, flags); descr = card->tx_chain.head; + if (descr->next == chain->tail->prev) { + spin_unlock_irqrestore(&chain->lock, flags); + pci_unmap_single(card->pdev, buf, skb->len, PCI_DMA_TODEVICE); + return -ENOMEM; + } hwdescr = descr->hwdescr; - card->tx_chain.head = descr->next; + chain->head = descr->next; descr->skb = skb; hwdescr->buf_addr = buf; @@ -708,7 +716,7 @@ spider_net_prepare_tx_descr(struct spide hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_CARDOWNED | SPIDER_NET_DMAC_NOCS; - spin_unlock_irqrestore(&card->tx_chain.lock, flags); + spin_unlock_irqrestore(&chain->lock, flags); if (skb->protocol == htons(ETH_P_IP)) switch (skb->nh.iph->protocol) { @@ -837,6 +845,7 @@ spider_net_release_tx_chain(struct spide chain->tail = descr->next; hwdescr->dmac_cmd_status |= SPIDER_NET_DESCR_NOT_IN_USE; skb = descr->skb; + descr->skb = NULL; buf_addr = hwdescr->buf_addr; spin_unlock_irqrestore(&chain->lock, flags); @@ -902,13 +911,10 @@ spider_net_xmit(struct sk_buff *skb, str { int cnt; struct spider_net_card *card = netdev_priv(netdev); - struct spider_net_descr_chain *chain = &card->tx_chain; spider_net_release_tx_chain(card, 0); - if ((chain->head->next == chain->tail->prev) || - (spider_net_prepare_tx_descr(card, skb) != 0)) { - + if (spider_net_prepare_tx_descr(card, skb) != 0) { card->netdev_stats.tx_dropped++; netif_stop_queue(netdev); return NETDEV_TX_BUSY; @@ -1126,6 +1132,7 @@ spider_net_decode_one_descr(struct spide bad_desc: dev_kfree_skb_irq(descr->skb); + descr->skb = NULL; hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; return 0; }