Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1f70ba3d authored by Vijayavardhan Vennapusa's avatar Vijayavardhan Vennapusa
Browse files

USB: gadget: f_rndis: Fix NULL pointer dereference during disconnect



There is a chance that unbind() and rndis_command_complete() might
race other and is results in crash. Hence add spinlock protection to
avoid this.

Change-Id: I1ea0c3da6586c3b08a0ed85f7d0d58e8dc0ebf1b
Signed-off-by: default avatarVijayavardhan Vennapusa <vvreddy@codeaurora.org>
parent 5a94aeea
Loading
Loading
Loading
Loading
+49 −15
Original line number Diff line number Diff line
@@ -98,6 +98,7 @@ struct f_rndis {
};

static struct f_rndis *__rndis;
static spinlock_t _rndis_lock;

int
rndis_rx_trigger(bool write)
@@ -473,13 +474,22 @@ static void rndis_response_available(void *_rndis)

static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req)
{
	struct f_rndis			*rndis = req->context;
	struct f_rndis			*rndis = __rndis;
	struct usb_composite_dev	*cdev;
	int				status = req->status;
	struct usb_ep *notify_ep;

	if (!rndis->port.func.config || !rndis->port.func.config->cdev)
	spin_lock(&_rndis_lock);
	if (!rndis || !rndis->notify || !rndis->notify->driver_data) {
		spin_unlock(&_rndis_lock);
		return;
	else
	}

	if (!rndis->port.func.config || !rndis->port.func.config->cdev) {
		spin_unlock(&_rndis_lock);
		return;
	}

	cdev = rndis->port.func.config->cdev;

	/* after TX:
@@ -491,7 +501,7 @@ static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req)
	case -ESHUTDOWN:
		/* connection gone */
		atomic_set(&rndis->notify_count, 0);
		break;
		goto out;
	default:
		DBG(cdev, "RNDIS %s response error %d, %d/%d\n",
			ep->name, status,
@@ -499,35 +509,50 @@ static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req)
		/* FALLTHROUGH */
	case 0:
		if (ep != rndis->notify)
			break;
			goto out;

		/* handle multiple pending RNDIS_RESPONSE_AVAILABLE
		 * notifications by resending until we're done
		 */
		if (atomic_dec_and_test(&rndis->notify_count))
			break;
		status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
			goto out;
		notify_ep = rndis->notify;
		spin_unlock(&_rndis_lock);
		status = usb_ep_queue(notify_ep, req, GFP_ATOMIC);
		if (status) {
			atomic_dec(&rndis->notify_count);
			spin_lock(&_rndis_lock);
			if (!__rndis)
				goto out;
			atomic_dec(&__rndis->notify_count);
			DBG(cdev, "notify/1 --> %d\n", status);
			spin_unlock(&_rndis_lock);
		}
		break;
	}

	return;

out:
	spin_unlock(&_rndis_lock);
}

static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
{
	struct f_rndis			*rndis = req->context;
	struct f_rndis			*rndis = __rndis;
	struct usb_composite_dev	*cdev;
	int				status;
	rndis_init_msg_type		*buf;

	if (!rndis || !rndis->notify || !rndis->notify->driver_data)
	spin_lock(&_rndis_lock);
	if (!rndis || !rndis->notify || !rndis->notify->driver_data) {
		spin_unlock(&_rndis_lock);
		return;
	}

	if (!rndis->port.func.config || !rndis->port.func.config->cdev)
	if (!rndis->port.func.config || !rndis->port.func.config->cdev) {
		spin_unlock(&_rndis_lock);
		return;
	else
	}

	cdev = rndis->port.func.config->cdev;

	/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
@@ -554,6 +579,7 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
			gether_update_dl_max_pkts_per_xfer(&rndis->port,
					 rndis->port.dl_max_pkts_per_xfer);

			spin_unlock(&_rndis_lock);
			return;
		}

@@ -569,6 +595,7 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
			rndis->port.multi_pkt_xfer = 0;
	}
//	spin_unlock(&dev->lock);
	spin_unlock(&_rndis_lock);
}

static int
@@ -725,13 +752,16 @@ static void rndis_disable(struct usb_function *f)
{
	struct f_rndis		*rndis = func_to_rndis(f);
	struct usb_composite_dev *cdev = f->config->cdev;
	unsigned long flags;

	if (!rndis->notify->driver_data)
		return;

	DBG(cdev, "rndis deactivated\n");

	spin_lock_irqsave(&_rndis_lock, flags);
	rndis_uninit(rndis->config);
	spin_unlock_irqrestore(&_rndis_lock, flags);
	gether_disconnect(&rndis->port);

	usb_ep_disable(rndis->notify);
@@ -962,6 +992,7 @@ static void
rndis_old_unbind(struct usb_configuration *c, struct usb_function *f)
{
	struct f_rndis	*rndis = func_to_rndis(f);
	unsigned long flags;

	rndis_deregister(rndis->config);

@@ -970,8 +1001,10 @@ rndis_old_unbind(struct usb_configuration *c, struct usb_function *f)
	kfree(rndis->notify_req->buf);
	usb_ep_free_request(rndis->notify, rndis->notify_req);

	spin_lock_irqsave(&_rndis_lock, flags);
	kfree(rndis);
	__rndis = NULL;
	spin_unlock_irqrestore(&_rndis_lock, flags);
}

int
@@ -1209,6 +1242,7 @@ static int __init rndis_mod_init(void)
	if (ret)
		return ret;

	spin_lock_init(&_rndis_lock);
	return usb_function_register(&rndisusb_func);
}
module_init(rndis_mod_init);