Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 2981

kernel-2.6.18-238.el5.src.rpm

From: Neil Horman <nhorman@redhat.com>
Date: Wed, 11 Aug 2010 19:58:08 -0400
Subject: [net] qla3xxx: fix oops on too-long netdev priv structure
Message-id: <20100811195808.GH1573@hmsreliant.think-freely.org>
Patchwork-id: 27518
O-Subject: Re: [RHEL 5.6 PATCH] fix oops in qla3xxx on too-long netdev priv
	structure (bz 620508)
Bugzilla: 620508
RH-Acked-by: Jiri Pirko <jpirko@redhat.com>
RH-Acked-by: Jerome Marchand <jmarchan@redhat.com>
RH-Acked-by: Andy Gospodarek <gospo@redhat.com>
RH-Acked-by: David S. Miller <davem@redhat.com>

Hey all-
        This got reported a few days ago.  I didn't think it would happen, but
apparently the qla3xxx driver allocates a net_device with more than 65Kb worth
of private data.  Upstream / RHEL6 doesn't have a problem with this, but RHEL5
this net_device_extended structure that lives at the end of the private area
to work around abi issues.  Its records the private length in a u16, which means
we can only have 65kb of private data per net_device.  qla3xxx violates that and
winds up corrupting memory.  We could increase the priv_len field in net_device,
but I think doing so would break abi if any third party vendors reference that
variable.  So instead, we've done this, which puts the qla3xxx driver on a priv
diet of sorts, and adds a check to the netdev_alloc function to ensure that we
don't silently violate the length field again.  Tested and confirmed to solve bz
620508.

Signed-off-by: Jarod Wilson <jarod@redhat.com>

diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c
index 891b8c4..feda8a0 100644
--- a/drivers/net/qla3xxx.c
+++ b/drivers/net/qla3xxx.c
@@ -3989,6 +3989,7 @@ static int __devinit ql3xxx_probe(struct pci_dev *pdev,
 {
 	struct net_device *ndev = NULL;
 	struct ql3_adapter *qdev = NULL;
+	struct ql_tx_buf_cb *tx_buf = NULL;
 	static int cards_found = 0;
 	int pci_using_dac, err;
 
@@ -4022,12 +4023,20 @@ static int __devinit ql3xxx_probe(struct pci_dev *pdev,
 		goto err_out_free_regions;
 	}
 
+	tx_buf = kzalloc(sizeof(struct ql_tx_buf_cb)*NUM_REQ_Q_ENTRIES, GFP_KERNEL);
+	if (!tx_buf) {
+		printk(KERN_ERR PFX "%s could not allocate tx_buf_cb\n",
+		       pci_name(pdev));
+		err = -ENOMEM;
+		goto err_out_free_regions;
+	}
+
 	ndev = alloc_etherdev(sizeof(struct ql3_adapter));
 	if (!ndev) {
 		printk(KERN_ERR PFX "%s could not alloc etherdev\n",
 		       pci_name(pdev));
 		err = -ENOMEM;
-		goto err_out_free_regions;
+		goto err_out_free_txb;
 	}
 
 	SET_MODULE_OWNER(ndev);
@@ -4036,6 +4045,7 @@ static int __devinit ql3xxx_probe(struct pci_dev *pdev,
 	pci_set_drvdata(pdev, ndev);
 
 	qdev = netdev_priv(ndev);
+	qdev->tx_buf = tx_buf;
 	qdev->index = cards_found;
 	qdev->ndev = ndev;
 	qdev->pdev = pdev;
@@ -4157,6 +4167,8 @@ err_out_iounmap:
 	iounmap(qdev->mem_map_registers);
 err_out_free_ndev:
 	free_netdev(ndev);
+err_out_free_txb:
+	kfree(tx_buf);
 err_out_free_regions:
 	pci_release_regions(pdev);
 err_out_disable_pdev:
@@ -4186,6 +4198,7 @@ static void __devexit ql3xxx_remove(struct pci_dev *pdev)
 	iounmap(qdev->mem_map_registers);
 	pci_release_regions(pdev);
 	pci_set_drvdata(pdev, NULL);
+	kfree(qdev->tx_buf);
 	free_netdev(ndev);
 }
 
diff --git a/drivers/net/qla3xxx.h b/drivers/net/qla3xxx.h
index 8f0c6eb..c0982a5 100644
--- a/drivers/net/qla3xxx.h
+++ b/drivers/net/qla3xxx.h
@@ -1211,7 +1211,7 @@ struct ql3_adapter {
 	u32 req_consumer_index_phy_addr_high;
 	u32 req_consumer_index_phy_addr_low;
 	atomic_t tx_count;
-	struct ql_tx_buf_cb tx_buf[NUM_REQ_Q_ENTRIES];
+	struct ql_tx_buf_cb *tx_buf;
 
 	/* Net Response Queue */
 	u32 rsp_q_size;
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index bb56266..dbc7553 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -586,6 +586,10 @@ struct net_device
 	/* space for optional statistics and wireless sysfs groups */
 	struct attribute_group  *sysfs_groups[3];
 #ifndef __GENKSYMS__
+	/*
+	 * Private data size is limited to 64kB
+	 */
+#define NETDEV_PRIV_LEN_MAX	0X0000FFFF
 	unsigned short priv_len;
 #endif
 };
diff --git a/net/core/dev.c b/net/core/dev.c
index de00e24..1968d30 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3803,6 +3803,11 @@ struct net_device *alloc_netdev(int sizeof_priv, const char *name,
 	struct net_device *dev;
 	int alloc_size;
 
+	if (sizeof_priv > NETDEV_PRIV_LEN_MAX) {
+		printk(KERN_ERR "alloc_dev: Private data too big.\n");
+		return NULL;
+	}
+
 	/* ensure 32-byte alignment of both the device and private area */
 	alloc_size = (sizeof(*dev) + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST;
 	alloc_size += (sizeof_priv + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST;