Hans,

When investigating Mike Krufky's report of module reload problems, I ran
across problems with the management of the cx18_cards[] array.  They're
corner cases and not likely to be the cause of Mike problems though.

Upon error conditions in cx18_probe(), the code at the 'err:' label
could leak cx18_cards[] entries.  Not a big problem since there are 32
of them, but they could have caused a NULL pointer de-reference in
cx18_v4l2_open().

The attached patch fixes these and reworks the management of the
cx18_cards[] entries.  The cx18_active_cards variable is replaced with
cx18_highest_cards_index (because that's essentially what
cx18_active_cards_was doing +1), and cleanup of entries happens a little
more pedantically (obtaining the lock, and removing each entry on a pci
remove, instead of waiting until module unload).

The attached patch was made against the latest v4l-dvb hg repository.

Comments welcome.

Regards,
Andy
# HG changeset patch
# User Andy Walls <[EMAIL PROTECTED]>
# Date 1209781272 14400
# Node ID cb8788a7039efb818fd8299d1fcbe8c8f20d3093
# Parent  4c4fd6b8755cc9918255876ff1010bc77374a310
Fix cx18_cards[] entry leak, and possible NULL dereference.


From: Andy Walls <[EMAIL PROTECTED]>

The code at the err: label of cx18_probe() could leak cx18_card[] entries.
In cx18_v4l2_open() the leaked entries could cause NULL pointer derefernces.
Fixed these two problems and modified managment of the cx18_cards[] entries.
In the process, replaced the misnamed cx18_cards_active variable with a
cx18_highest_card_index variable, which was essentially the function of
cx18_cards_active anyway.


Signed-off-by: Andy Walls <[EMAIL PROTECTED]>

diff -r 4c4fd6b8755c -r cb8788a7039e linux/drivers/media/video/cx18/cx18-driver.c
--- a/linux/drivers/media/video/cx18/cx18-driver.c	Fri May 02 07:51:27 2008 -0300
+++ b/linux/drivers/media/video/cx18/cx18-driver.c	Fri May 02 22:21:12 2008 -0400
@@ -38,9 +38,6 @@
 #include <media/tveeprom.h>
 
 
-/* var to keep track of the number of array elements in use */
-int cx18_cards_active;
-
 /* If you have already X v4l cards, then set this to X. This way
    the device numbers stay matched. Example: you have a WinTV card
    without radio and a Compro H900 with. Normally this would give a
@@ -51,7 +48,10 @@ int cx18_first_minor;
 /* Master variable for all cx18 info */
 struct cx18 *cx18_cards[CX18_MAX_CARDS];
 
-/* Protects cx18_cards_active */
+/* Highest index used so far in cx18_cards[] */
+int cx18_highest_card_index = -1;
+
+/* Protects cx18_cards[] */
 DEFINE_SPINLOCK(cx18_cards_lock);
 
 /* add your revision and whatnot here */
@@ -594,20 +594,40 @@ static void cx18_load_and_init_modules(s
 	hw = cx->hw_flags;
 }
 
+static void cx18_cards_free_entry(struct cx18 *cx)
+{
+	int i = cx->num;
+
+	spin_lock(&cx18_cards_lock);
+
+	kfree(cx18_cards[i]); /* the passed in cx pointer is now invalid! */
+	cx18_cards[i] = NULL;
+
+	cx18_highest_card_index = -1;
+	for (i = 0; i < CX18_MAX_CARDS; i++)
+		if (cx18_cards[i] != NULL)
+			cx18_highest_card_index = i;
+
+	spin_unlock(&cx18_cards_lock);
+}
+
 static int __devinit cx18_probe(struct pci_dev *dev,
 				const struct pci_device_id *pci_id)
 {
 	int retval = 0;
 	int vbi_buf_size;
+	int i;
 	u32 devtype;
 	struct cx18 *cx;
 
 	spin_lock(&cx18_cards_lock);
 
 	/* Make sure we've got a place for this card */
-	if (cx18_cards_active == CX18_MAX_CARDS) {
-		printk(KERN_ERR "cx18:  Maximum number of cards detected (%d).\n",
-			      cx18_cards_active);
+	for (i = 0; i < CX18_MAX_CARDS && cx18_cards[i] != NULL; i++) {}
+
+	if (i == CX18_MAX_CARDS) {
+		printk(KERN_ERR
+		       "cx18:  Maximum number of cards detected (%d).\n", i);
 		spin_unlock(&cx18_cards_lock);
 		return -ENOMEM;
 	}
@@ -617,11 +637,15 @@ static int __devinit cx18_probe(struct p
 		spin_unlock(&cx18_cards_lock);
 		return -ENOMEM;
 	}
-	cx18_cards[cx18_cards_active] = cx;
+	cx18_cards[i] = cx;
 	cx->dev = dev;
-	cx->num = cx18_cards_active++;
+	cx->num = i;
 	snprintf(cx->name, sizeof(cx->name) - 1, "cx18-%d", cx->num);
 	CX18_INFO("Initializing card #%d\n", cx->num);
+
+	for (; i < CX18_MAX_CARDS; i++)
+		if (cx18_cards[i] != NULL)
+			cx18_highest_card_index = i;
 
 	spin_unlock(&cx18_cards_lock);
 
@@ -687,8 +711,6 @@ static int __devinit cx18_probe(struct p
 		CX18_ERR("Could not initialize i2c\n");
 		goto free_map;
 	}
-
-	CX18_DEBUG_INFO("Active card count: %d.\n", cx18_cards_active);
 
 	if (cx->card->hw_all & CX18_HW_TVEEPROM) {
 		/* Based on the model number the cardtype may be changed.
@@ -816,8 +838,7 @@ err:
 		retval = -ENODEV;
 	CX18_ERR("Error %d on initialization\n", retval);
 
-	kfree(cx18_cards[cx18_cards_active]);
-	cx18_cards[cx18_cards_active] = NULL;
+	cx18_cards_free_entry(cx);
 	return retval;
 }
 
@@ -918,6 +939,9 @@ static void cx18_remove(struct pci_dev *
 	pci_disable_device(cx->dev);
 
 	CX18_INFO("Removed %s, card #%d\n", cx->card_name, cx->num);
+
+	pci_set_drvdata(cx->dev, NULL);
+	cx18_cards_free_entry(cx);
 }
 
 /* define a pci_driver for card detection */
@@ -960,11 +984,15 @@ static void module_cleanup(void)
 
 	pci_unregister_driver(&cx18_pci_driver);
 
-	for (i = 0; i < cx18_cards_active; i++) {
+	spin_lock(&cx18_cards_lock);
+	for (i = 0; i <= cx18_highest_card_index; i++) {
 		if (cx18_cards[i] == NULL)
 			continue;
 		kfree(cx18_cards[i]);
-	}
+		cx18_cards[i] = NULL;
+	}
+	cx18_highest_card_index = -1;
+	spin_unlock(&cx18_cards_lock);
 }
 
 module_init(module_start);
diff -r 4c4fd6b8755c -r cb8788a7039e linux/drivers/media/video/cx18/cx18-driver.h
--- a/linux/drivers/media/video/cx18/cx18-driver.h	Fri May 02 07:51:27 2008 -0300
+++ b/linux/drivers/media/video/cx18/cx18-driver.h	Fri May 02 22:21:12 2008 -0400
@@ -449,7 +449,7 @@ struct cx18 {
 
 /* Globals */
 extern struct cx18 *cx18_cards[];
-extern int cx18_cards_active;
+extern int cx18_highest_card_index;
 extern int cx18_first_minor;
 extern spinlock_t cx18_cards_lock;
 
diff -r 4c4fd6b8755c -r cb8788a7039e linux/drivers/media/video/cx18/cx18-fileops.c
--- a/linux/drivers/media/video/cx18/cx18-fileops.c	Fri May 02 07:51:27 2008 -0300
+++ b/linux/drivers/media/video/cx18/cx18-fileops.c	Fri May 02 22:21:12 2008 -0400
@@ -694,7 +694,9 @@ int cx18_v4l2_open(struct inode *inode, 
 
 	/* Find which card this open was on */
 	spin_lock(&cx18_cards_lock);
-	for (x = 0; cx == NULL && x < cx18_cards_active; x++) {
+	for (x = 0; cx == NULL && x <= cx18_highest_card_index; x++) {
+		if (cx18_cards[x] == NULL)
+			continue;
 		/* find out which stream this open was on */
 		for (y = 0; y < CX18_MAX_STREAMS; y++) {
 			s = &cx18_cards[x]->streams[y];
_______________________________________________
ivtv-devel mailing list
[email protected]
http://ivtvdriver.org/mailman/listinfo/ivtv-devel

Reply via email to