Commit ba46ce30 authored by Oleg Nesterov's avatar Oleg Nesterov Committed by Greg Kroah-Hartman
Browse files

staging: usbip: fix the usage of kthread_stop()



stub_shutdown_connection() and vhci_shutdown_connection() use
task_is_dead() before kthread_stop(). This buys nothing and wrong.
kthread_stop() is fine even if this thread is dead. However, if it
is dead nothing protects this task_struct, we shouldn't touch this
memory.

Change the code to do the necessary get_task_struct/put_task_struct.

This patch assumes that

	- xxx_shutdown_connection() is always called, so we can't
	  leak the task_struct.

	- kthread_stop_put() can't be called twice on the same task.

Signed-off-by: default avatarOleg Nesterov <oleg@redhat.com>
Cc: Tobias Klauser <tklauser@distanz.ch>
Cc: Matt Mooney <mfm@muteddisk.com>,
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 96ddcd43
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -113,8 +113,8 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,

		spin_unlock(&sdev->ud.lock);

		sdev->ud.tcp_rx = kthread_run(stub_rx_loop, &sdev->ud, "stub_rx");
		sdev->ud.tcp_tx = kthread_run(stub_tx_loop, &sdev->ud, "stub_tx");
		sdev->ud.tcp_rx = kthread_get_run(stub_rx_loop, &sdev->ud, "stub_rx");
		sdev->ud.tcp_tx = kthread_get_run(stub_tx_loop, &sdev->ud, "stub_tx");

		spin_lock(&sdev->ud.lock);
		sdev->ud.status = SDEV_ST_USED;
@@ -187,10 +187,10 @@ static void stub_shutdown_connection(struct usbip_device *ud)
	}

	/* 1. stop threads */
	if (ud->tcp_rx && !task_is_dead(ud->tcp_rx))
		kthread_stop(ud->tcp_rx);
	if (ud->tcp_tx && !task_is_dead(ud->tcp_tx))
		kthread_stop(ud->tcp_tx);
	if (ud->tcp_rx)
		kthread_stop_put(ud->tcp_rx);
	if (ud->tcp_tx)
		kthread_stop_put(ud->tcp_tx);

	/*
	 * 2. close the socket
+17 −0
Original line number Diff line number Diff line
@@ -292,6 +292,23 @@ struct usbip_device {
	} eh_ops;
};

#define kthread_get_run(threadfn, data, namefmt, ...)			   \
({									   \
	struct task_struct *__k						   \
		= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
	if (!IS_ERR(__k)) {						   \
		get_task_struct(__k);					   \
		wake_up_process(__k);					   \
	}								   \
	__k;								   \
})

#define kthread_stop_put(k)		\
	do {				\
		kthread_stop(k);	\
		put_task_struct(k);	\
	} while (0)

/* usbip_common.c */
void usbip_dump_urb(struct urb *purb);
void usbip_dump_header(struct usbip_header *pdu);
+4 −4
Original line number Diff line number Diff line
@@ -821,10 +821,10 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
	}

	/* kill threads related to this sdev, if v.c. exists */
	if (vdev->ud.tcp_rx && !task_is_dead(vdev->ud.tcp_rx))
		kthread_stop(vdev->ud.tcp_rx);
	if (vdev->ud.tcp_tx && !task_is_dead(vdev->ud.tcp_tx))
		kthread_stop(vdev->ud.tcp_tx);
	if (vdev->ud.tcp_rx)
		kthread_stop_put(vdev->ud.tcp_rx);
	if (vdev->ud.tcp_tx)
		kthread_stop_put(vdev->ud.tcp_tx);

	pr_info("stop threads\n");

+2 −2
Original line number Diff line number Diff line
@@ -222,8 +222,8 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
	spin_unlock(&the_controller->lock);
	/* end the lock */

	vdev->ud.tcp_rx = kthread_run(vhci_rx_loop, &vdev->ud, "vhci_rx");
	vdev->ud.tcp_tx = kthread_run(vhci_tx_loop, &vdev->ud, "vhci_tx");
	vdev->ud.tcp_rx = kthread_get_run(vhci_rx_loop, &vdev->ud, "vhci_rx");
	vdev->ud.tcp_tx = kthread_get_run(vhci_tx_loop, &vdev->ud, "vhci_tx");

	rh_port_connect(rhport, speed);