[PATCH] USB:serial:release port object after releasing serial

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Ming Lei <tom.leiming@xxxxxxxxx>

This patch releases the usb_serial_port object after releasing
usb_serial object, because .shutdown of usb_serial_driver often
references port object and it is called in destroy_serial(),
when it is touched, port object has been released.

This bug is introduced by Alan's patch:
	usb-serial-fix-lifetime-and-locking-problems.patch.

The bug is reproduced very easily by the following steps:
1, plug a usb serial port and unplug it;
or
2,plug a use serial port, open it ,disconnect it, and close it.

In each case, the oops will happen:
[  157.595138] BUG: unable to handle kernel NULL pointer dereference at (null)
[  157.595146] IP: [<ffffffffa03c1608>] generic_cleanup+0x11/0x6d [usbserial]
[  157.595157] PGD 0
[  157.595160] Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC
[  157.595167] last sysfs file: /sys/devices/pci0000:00/0000:00:1d.7/usb2/2-2/2-2.1/2-2.1:1.0/bInterfaceClass
[  157.595170] CPU 0
[  157.595173] Modules linked in: ch341 usbserial vfat fat fuse sunrpc ipv6 acpi_cpufreq snd_hda_codec_idt snd_hda_intel snd_hda_codec snd_hwdep snd_seq_device snd_pcm_oss snd_mixer_oss iwl3945 snd_pcm iwlcore mac80211 usbhid ohci1394 snd_timer snd i2c_i801 ieee1394 cfg80211 i2c_core tg3 sg libphy processor snd_page_alloc battery ac button thermal sr_mod cdrom evdev dcdbas ata_generic ata_piix libata sd_mod scsi_mod ext3 jbd mbcache uhci_hcd ohci_hcd ehci_hcd usbcore [last unloaded: microcode]
[  157.595237] Pid: 6326, comm: lockdev Not tainted 2.6.30-rc3-gkh-00001-g7168b05 #96 Latitude D630
[  157.595240] RIP: 0010:[<ffffffffa03c1608>]  [<ffffffffa03c1608>] generic_cleanup+0x11/0x6d [usbserial]
[  157.595248] RSP: 0018:ffff880010d43be8  EFLAGS: 00010246
[  157.595251] RAX: 0000000000000001 RBX: 0000000000000000 RCX: 0000000000000000
[  157.595260] RDX: 0000000000000000 RSI: ffffffffa03c0faf RDI: 0000000000000000
[  157.595263] RBP: ffff880010d43bf8 R08: 0000000000000001 R09: ffff880010d43bd8
[  157.595266] R10: 0000000000000002 R11: 0000000087654321 R12: ffff88003ddaddf8
[  157.595269] R13: ffff88003ddaddf0 R14: ffff88003ddaddf0 R15: ffff88003350b9e0
[  157.595272] FS:  0000000000000000(0000) GS:ffff880002400000(0000) knlGS:0000000000000000
[  157.595276] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  157.595279] CR2: 0000000000000000 CR3: 0000000000201000 CR4: 00000000000006e0
[  157.595282] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[  157.595285] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[  157.595289] Process lockdev (pid: 6326, threadinfo ffff880010d42000, task ffff880010c09640)
[  157.595291] Stack:
[  157.595293]  0000000000000001 ffff88003ddaddf8 ffff880010d43c28 ffffffffa03c16ac
[  157.595300]  0000000000000246 ffff88003ddade50 ffff88003ddade50 ffff88003ddaddf0
[  157.595307]  ffff880010d43c58 ffffffffa03c0ffb ffff88003ddade50 ffffffffa03c0faf
[  157.595315] Call Trace:
[  157.595317]  [<ffffffffa03c16ac>] usb_serial_generic_shutdown+0x48/0x5d [usbserial]
[  157.595325]  [<ffffffffa03c0ffb>] destroy_serial+0x4c/0xef [usbserial]
[  157.595332]  [<ffffffffa03c0faf>] ? destroy_serial+0x0/0xef [usbserial]
[  157.595340]  [<ffffffff80355855>] kref_put+0x4b/0x57
[  157.595347]  [<ffffffffa03c0d2c>] usb_serial_put+0x2d/0x3d [usbserial]
[  157.595354]  [<ffffffffa03c0e81>] serial_close+0x145/0x15e [usbserial]
[  157.595361]  [<ffffffff803aae0a>] tty_release_dev+0x1bf/0x4e8
[  157.595368]  [<ffffffff803ab144>] ? tty_release+0x11/0x24
[  157.595372]  [<ffffffff8048b20e>] ? lock_kernel+0x2b/0xbe
[  157.595378]  [<ffffffff8035e09d>] ? _raw_spin_trylock+0x11/0x37
[  157.595384]  [<ffffffff803ab14c>] tty_release+0x19/0x24
[  157.595388]  [<ffffffff802c8049>] __fput+0xeb/0x1a7
[  157.595394]  [<ffffffff802c811d>] fput+0x18/0x1a
[  157.595399]  [<ffffffff802c53c8>] filp_close+0x67/0x72
[  157.595404]  [<ffffffff80241391>] put_files_struct+0x6b/0xc2
[  157.595409]  [<ffffffff8024142f>] exit_files+0x47/0x50
[  157.595414]  [<ffffffff80242bb1>] do_exit+0x1ff/0x6a6
[  157.595418]  [<ffffffff8048a31c>] ? trace_hardirqs_off_thunk+0x3a/0x3c
[  157.595424]  [<ffffffff8020bbf9>] ? retint_swapgs+0x13/0x1b
[  157.595431]  [<ffffffff802430d7>] do_group_exit+0x7f/0xaf
[  157.595435]  [<ffffffff80243119>] sys_exit_group+0x12/0x16
[  157.595440]  [<ffffffff8020b142>] system_call_fastpath+0x16/0x1b
[  157.595445] Code: c6 de 33 3c a0 48 c7 c7 10 34 3c a0 31 c0 e8 80 58 0c e0 89 d8 5b 41 5c c9 c3 55 83 3d b1 5c 00 00 00 48 89 e5 41 54 53 48 89 fb <4c> 8b 27 74 23 0f b6 8f 88 02 00 00 48 c7 c2 b0 27 3c a0 48 c7
[  157.595517] RIP  [<ffffffffa03c1608>] generic_cleanup+0x11/0x6d [usbserial]
[  157.595524]  RSP <ffff880010d43be8>
[  157.595526] CR2: 0000000000000000
[  157.595530] ---[ end trace c18d5f6118ef6990 ]---

Signed-off-by: Ming Lei <tom.leiming@xxxxxxxxx>
---
 drivers/usb/serial/bus.c        |    9 ++++++---
 drivers/usb/serial/usb-serial.c |   25 +++++++++++++++++++++----
 2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c
index 83bbb5b..45b837b 100644
--- a/drivers/usb/serial/bus.c
+++ b/drivers/usb/serial/bus.c
@@ -87,9 +87,11 @@ exit:
 	return retval;
 }
 
+#define to_usb_serial_driver(d) container_of(d, struct usb_serial_driver, \
+						driver)
 static int usb_serial_device_remove(struct device *dev)
 {
-	struct usb_serial_driver *driver;
+	struct usb_serial_driver *driver = NULL;
 	struct usb_serial_port *port;
 	int retval = 0;
 	int minor;
@@ -100,8 +102,9 @@ static int usb_serial_device_remove(struct device *dev)
 
 	device_remove_file(&port->dev, &dev_attr_port_number);
 
-	driver = port->serial->type;
-	if (driver->port_remove) {
+	if (dev->driver)
+		driver = to_usb_serial_driver(dev->driver);
+	if (driver && driver->port_remove) {
 		if (!try_module_get(driver->driver.owner)) {
 			dev_err(dev, "module get failed, exiting\n");
 			retval = -EIO;
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 2c09006..892ac97 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -305,7 +305,6 @@ static void serial_close(struct tty_struct *tty, struct file *filp)
 	--port->port.count;
 	count = port->port.count;
 	mutex_unlock(&port->mutex);
-	put_device(&port->dev);
 
 	/* Mustn't dereference port any more */
 	if (count == 0) {
@@ -316,6 +315,10 @@ static void serial_close(struct tty_struct *tty, struct file *filp)
 	}
 	usb_serial_put(serial);
 
+	/*Must release port after serial, for .shutdown
+	* need to reference port.*/
+	put_device(&port->dev);
+
 	/* Mustn't dereference serial any more */
 	if (count == 0)
 		module_put(owner);
@@ -1053,6 +1056,8 @@ void usb_serial_disconnect(struct usb_interface *interface)
 	struct usb_serial *serial = usb_get_intfdata(interface);
 	struct device *dev = &interface->dev;
 	struct usb_serial_port *port;
+	struct usb_serial_port **port_save;
+	unsigned char ports_num;
 
 	usb_serial_console_disconnect(serial);
 	dbg("%s", __func__);
@@ -1063,7 +1068,15 @@ void usb_serial_disconnect(struct usb_interface *interface)
 	serial->disconnected = 1;
 	mutex_unlock(&serial->disc_mutex);
 
-	for (i = 0; i < serial->num_ports; ++i) {
+	ports_num = serial->num_ports;
+	port_save = kmalloc(ports_num*sizeof(struct usb_serial_port *), \
+			GFP_KERNEL);
+	if (!port_save) {
+		dev_err(&interface->dev, "%s- failed\n", __func__);
+		return;
+	}
+
+	for (i = 0; i < ports_num; ++i) {
 		port = serial->port[i];
 		if (port) {
 			struct tty_struct *tty = tty_port_tty_get(&port->port);
@@ -1073,13 +1086,17 @@ void usb_serial_disconnect(struct usb_interface *interface)
 			}
 			kill_traffic(port);
 			cancel_work_sync(&port->work);
-			device_unregister(&port->dev);
-			serial->port[i] = NULL;
+			port_save[i] = port;
 		}
 	}
 	/* let the last holder of this object
 	 * cause it to be cleaned up */
 	usb_serial_put(serial);
+
+	for (i = 0; i < ports_num; ++i)
+		device_unregister(&port_save[i]->dev);
+	kfree(port_save);
+
 	dev_info(dev, "device disconnected\n");
 }
 EXPORT_SYMBOL_GPL(usb_serial_disconnect);
-- 
1.6.0.GIT

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux