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

Commit 46778034 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: f_rndis: Fix rndis message parsing of erroneous requests"

parents 8cf323e9 e1cc61dc
Loading
Loading
Loading
Loading
+57 −10
Original line number Diff line number Diff line
@@ -93,6 +93,9 @@ struct f_rndis {
	atomic_t			notify_count;
};

static struct f_rndis *__rndis;
static spinlock_t _rndis_lock;

static inline struct f_rndis *func_to_rndis(struct usb_function *f)
{
	return container_of(f, struct f_rndis, port.func);
@@ -422,10 +425,19 @@ 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 usb_composite_dev	*cdev = rndis->port.func.config->cdev;
	struct f_rndis			*rndis;
	struct usb_composite_dev	*cdev;
	int				status = req->status;
	struct usb_ep *notify_ep;

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

	cdev = rndis->port.func.config->cdev;
	/* after TX:
	 *  - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
	 *  - RNDIS_RESPONSE_AVAILABLE (status/irq)
@@ -435,7 +447,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,
@@ -443,29 +455,53 @@ 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 usb_composite_dev	*cdev = rndis->port.func.config->cdev;
	struct f_rndis			*rndis;
	struct usb_composite_dev	*cdev;
	int				status;
	rndis_init_msg_type		*buf;

	if (req->status != 0) {
		pr_err("%s: RNDIS command completion error:%d\n",
				__func__, req->status);
		return;
	}

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

	cdev = rndis->port.func.config->cdev;
	/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
//	spin_lock(&dev->lock);
	status = rndis_msg_parser(rndis->params, (u8 *) req->buf);
@@ -488,6 +524,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
@@ -639,13 +676,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->enabled)
		return;

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

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

	usb_ep_disable(rndis->notify);
@@ -938,6 +978,7 @@ static struct usb_function_instance *rndis_alloc_inst(void)
	opts->rndis_os_desc.ext_compat_id = opts->rndis_ext_compat_id;

	mutex_init(&opts->lock);
	spin_lock_init(&_rndis_lock);
	opts->func_inst.free_func_inst = rndis_free_inst;
	opts->net = gether_setup_default();
	if (IS_ERR(opts->net)) {
@@ -967,11 +1008,15 @@ static void rndis_free(struct usb_function *f)
{
	struct f_rndis *rndis;
	struct f_rndis_opts *opts;
	unsigned long flags;

	rndis = func_to_rndis(f);
	rndis_deregister(rndis->params);
	opts = container_of(f->fi, struct f_rndis_opts, func_inst);
	spin_lock_irqsave(&_rndis_lock, flags);
	kfree(rndis);
	__rndis = NULL;
	spin_unlock_irqrestore(&_rndis_lock, flags);
	mutex_lock(&opts->lock);
	opts->refcnt--;
	mutex_unlock(&opts->lock);
@@ -1000,6 +1045,8 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
	if (!rndis)
		return ERR_PTR(-ENOMEM);

	__rndis = rndis;

	opts = container_of(fi, struct f_rndis_opts, func_inst);
	mutex_lock(&opts->lock);
	opts->refcnt++;
+46 −4
Original line number Diff line number Diff line
@@ -59,6 +59,9 @@

static struct workqueue_struct	*uether_wq;

/* Extra buffer size to allocate for tx */
#define EXTRA_ALLOCATION_SIZE_U_ETH	128

struct eth_dev {
	/* lock is held while accessing port_usb
	 */
@@ -104,6 +107,7 @@ struct eth_dev {
	unsigned long		tx_throttle;
	unsigned long		rx_throttle;
	unsigned int		tx_pkts_rcvd;
	unsigned long		skb_expand_cnt;
	struct dentry		*uether_dent;
	struct dentry		*uether_dfile;
};
@@ -125,7 +129,7 @@ static void uether_debugfs_exit(struct eth_dev *dev);
 * of interconnect, data can be very bursty. tx_qmult is the
 * additional multipler on qmult.
 */
static unsigned int tx_qmult = 1;
static unsigned int tx_qmult = 2;
module_param(tx_qmult, uint, 0644);
MODULE_PARM_DESC(tx_qmult, "Additional queue length multiplier for tx");

@@ -748,17 +752,24 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
{
	struct eth_dev		*dev = netdev_priv(net);
	int			length = 0;
	int			tail_room = 0;
	int			extra_alloc = 0;
	int			retval;
	struct usb_request	*req = NULL;
	struct sk_buff		*new_skb;
	unsigned long		flags;
	struct usb_ep		*in = NULL;
	u16			cdc_filter = 0;
	bool			multi_pkt_xfer = false;
	u32			fixed_in_len = 0;
	bool			is_fixed = false;

	spin_lock_irqsave(&dev->lock, flags);
	if (dev->port_usb) {
		in = dev->port_usb->in_ep;
		cdc_filter = dev->port_usb->cdc_filter;
		is_fixed = dev->port_usb->is_fixed;
		fixed_in_len = dev->port_usb->fixed_in_len;
		multi_pkt_xfer = dev->port_usb->multi_pkt_xfer;
	}
	spin_unlock_irqrestore(&dev->lock, flags);
@@ -880,6 +891,35 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
		dev->tx_skb_hold_count = 0;
		spin_unlock_irqrestore(&dev->lock, flags);
	} else {
		bool do_align = false;

		/* Check if TX buffer should be aligned before queuing to hw */
		if (dev->gadget->is_chipidea &&
		    !IS_ALIGNED((size_t)skb->data, 4))
			do_align = true;

		/*
		 * Some UDC requires allocation of some extra bytes for
		 * TX buffer due to hardware requirement. Check if extra
		 * bytes are already there, otherwise allocate new buffer
		 * with extra bytes and do memcpy to align skb as well.
		 */
		if (dev->gadget->extra_buf_alloc)
			extra_alloc = EXTRA_ALLOCATION_SIZE_U_ETH;
		tail_room = skb_tailroom(skb);
		if (do_align || tail_room < extra_alloc) {
			pr_debug("%s:align skb and update tail_room %d to %d\n",
					__func__, tail_room, extra_alloc);
			tail_room = extra_alloc;
			new_skb = skb_copy_expand(skb, 0, tail_room,
						  GFP_ATOMIC);
			if (!new_skb)
				return -ENOMEM;
			dev_kfree_skb_any(skb);
			skb = new_skb;
			dev->skb_expand_cnt++;
		}

		length = skb->len;
		req->buf = skb->data;
		req->context = skb;
@@ -888,9 +928,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
	req->complete = tx_complete;

	/* NCM requires no zlp if transfer is dwNtbInMaxSize */
	if (dev->port_usb &&
	    dev->port_usb->is_fixed &&
	    length == dev->port_usb->fixed_in_len &&
	if (is_fixed && length == fixed_in_len &&
	    (length % in->maxpacket) == 0)
		req->zero = 0;
	else
@@ -1525,6 +1563,7 @@ void gether_cleanup(struct eth_dev *dev)
	uether_debugfs_exit(dev);
	unregister_netdev(dev->net);
	flush_work(&dev->work);
	cancel_work_sync(&dev->rx_work);
	free_netdev(dev->net);
}
EXPORT_SYMBOL_GPL(gether_cleanup);
@@ -1721,6 +1760,8 @@ static int uether_stat_show(struct seq_file *s, void *unused)
		seq_printf(s, "tx_throttle = %lu\n", dev->tx_throttle);
		seq_printf(s, "tx_pkts_rcvd=%u\n", dev->tx_pkts_rcvd);
		seq_printf(s, "rx_throttle = %lu\n", dev->rx_throttle);
		seq_printf(s, "skb_expand_cnt = %lu\n",
					dev->skb_expand_cnt);
	}
	return ret;
}
@@ -1741,6 +1782,7 @@ static ssize_t uether_stat_reset(struct file *file,
	/* Reset tx_throttle */
	dev->tx_throttle = 0;
	dev->rx_throttle = 0;
	dev->skb_expand_cnt = 0;
	spin_unlock_irqrestore(&dev->lock, flags);
	return count;
}