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

Commit 2c35c274 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

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

parents c630ca7a 156486f5
Loading
Loading
Loading
Loading
+79 −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);
@@ -383,10 +386,31 @@ static struct sk_buff *rndis_add_header(struct gether *port,
					struct sk_buff *skb)
{
	struct sk_buff *skb2;
	struct rndis_packet_msg_type *header = NULL;
	struct f_rndis *rndis = func_to_rndis(&port->func);

	if (!skb)
		return NULL;

	if (rndis->port.multi_pkt_xfer) {
		if (port->header) {
			header = port->header;
			header->MessageType = cpu_to_le32(RNDIS_MSG_PACKET);
			header->MessageLength = cpu_to_le32(skb->len +
							sizeof(*header));
			header->DataOffset = cpu_to_le32(36);
			header->DataLength = cpu_to_le32(skb->len);
			pr_debug("MessageLength:%d DataLength:%d\n",
						header->MessageLength,
						header->DataLength);
			return skb;
		}

		dev_kfree_skb(skb);
		pr_err("RNDIS header is NULL.\n");
		return NULL;
	}

	skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type));
	rndis_add_hdr(skb2);

@@ -422,10 +446,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 +468,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 +476,54 @@ 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 +546,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 +698,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);
@@ -992,6 +1054,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;
	INIT_LIST_HEAD(&opts->rndis_os_desc.ext_prop);

@@ -1022,11 +1085,15 @@ static void rndis_free(struct usb_function *f)
{
	struct f_rndis *rndis;
	struct f_rndis_opts *opts;
	unsigned long int 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);
@@ -1059,6 +1126,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++;
+78 −36
Original line number Diff line number Diff line
@@ -246,6 +246,7 @@ static void defer_kevent(struct eth_dev *dev, int flag)
}

static void rx_complete(struct usb_ep *ep, struct usb_request *req);
static void tx_complete(struct usb_ep *ep, struct usb_request *req);

static int
rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
@@ -312,7 +313,6 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)

	req->buf = skb->data;
	req->length = size;
	req->complete = rx_complete;
	req->context = skb;

	retval = usb_ep_queue(out, req, gfp_flags);
@@ -415,6 +415,7 @@ static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
{
	unsigned		i;
	struct usb_request	*req;
	bool			usb_in;

	if (!n)
		return -ENOMEM;
@@ -425,10 +426,22 @@ static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
		if (i-- == 0)
			goto extra;
	}

	if (ep->desc->bEndpointAddress & USB_DIR_IN)
		usb_in = true;
	else
		usb_in = false;

	while (i--) {
		req = usb_ep_alloc_request(ep, GFP_ATOMIC);
		if (!req)
			return list_empty(list) ? -ENOMEM : 0;
		/* update completion handler */
		if (usb_in)
			req->complete = tx_complete;
		else
			req->complete = rx_complete;

		list_add(&req->list, list);
	}
	return 0;
@@ -580,7 +593,7 @@ static void eth_work(struct work_struct *work)

static void tx_complete(struct usb_ep *ep, struct usb_request *req)
{
	struct sk_buff	*skb = req->context;
	struct sk_buff	*skb;
	struct eth_dev	*dev = ep->driver_data;
	struct net_device *net = dev->net;
	struct usb_request *new_req;
@@ -693,6 +706,7 @@ static void tx_complete(struct usb_ep *ep, struct usb_request *req)
			spin_unlock(&dev->req_lock);
		}
	} else {
		skb = req->context;
		/* Is aggregation already enabled and buffers allocated ? */
		if (dev->port_usb->multi_pkt_xfer && dev->tx_req_bufsize) {
			req->buf = kzalloc(dev->tx_req_bufsize
@@ -735,7 +749,7 @@ static int alloc_tx_buffer(struct eth_dev *dev)
	list_for_each(act, &dev->tx_reqs) {
		req = container_of(act, struct usb_request, list);
		if (!req->buf)
			req->buf = kmalloc(dev->tx_req_bufsize
			req->buf = kzalloc(dev->tx_req_bufsize
				+ dev->gadget->extra_buf_alloc, GFP_ATOMIC);

		if (!req->buf)
@@ -771,11 +785,15 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
	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);
@@ -786,7 +804,8 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
	}

	/* apply outgoing CDC or RNDIS filters */
	if (skb && !is_promisc(cdc_filter)) {
	if (!test_bit(RMNET_MODE_LLP_IP, &dev->flags) &&
			!is_promisc(cdc_filter)) {
		u8		*dest = skb->data;

		if (is_multicast_ether_addr(dest)) {
@@ -800,6 +819,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
			else
				type = USB_CDC_PACKET_TYPE_ALL_MULTICAST;
			if (!(cdc_filter & type)) {
				dev->net->stats.tx_dropped++;
				dev_kfree_skb_any(skb);
				return NETDEV_TX_OK;
			}
@@ -808,29 +828,6 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
	}

	dev->tx_pkts_rcvd++;
	/*
	 * no buffer copies needed, unless the network stack did it
	 * or the hardware can't use skb buffers.
	 * or there's not enough space for extra headers we need
	 */
	spin_lock_irqsave(&dev->lock, flags);
	if (dev->wrap && dev->port_usb)
		skb = dev->wrap(dev->port_usb, skb);
	spin_unlock_irqrestore(&dev->lock, flags);

	if (!skb) {
		if (dev->port_usb && dev->port_usb->supports_multi_frame) {
			/*
			 * Multi frame CDC protocols may store the frame for
			 * later which is not a dropped frame.
			 */
		} else {
			dev->net->stats.tx_dropped++;
		}

		/* no error code for dropped packets */
		return NETDEV_TX_OK;
	}

	/* Allocate memory for tx_reqs to support multi packet transfer */
	spin_lock_irqsave(&dev->req_lock, flags);
@@ -865,13 +862,45 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
		dev->tx_throttle++;
		netif_stop_queue(net);
	}
	spin_unlock_irqrestore(&dev->req_lock, flags);

	/* no buffer copies needed, unless the network stack did it
	 * or the hardware can't use skb buffers.
	 * or there's not enough space for extra headers we need
	 */
	spin_lock_irqsave(&dev->lock, flags);
	if (dev->wrap) {
		if (dev->port_usb)
			skb = dev->wrap(dev->port_usb, skb);
		if (!skb) {
			spin_unlock_irqrestore(&dev->lock, flags);
			/* Multi frame CDC protocols may store the frame for
			 * later which is not a dropped frame.
			 */
			if (dev->port_usb &&
					dev->port_usb->supports_multi_frame)
				goto multiframe;
			goto drop;
		}
	}

	dev->tx_skb_hold_count++;
	spin_unlock_irqrestore(&dev->req_lock, flags);
	spin_unlock_irqrestore(&dev->lock, flags);

	if (multi_pkt_xfer) {
		pr_debug("req->length:%d header_len:%u\n"
				"skb->len:%d skb->data_len:%d\n",
				req->length, dev->header_len,
				skb->len, skb->data_len);
		/* Add RNDIS Header */
		memcpy(req->buf + req->length, dev->port_usb->header,
						dev->header_len);
		/* Increment req length by header size */
		req->length += dev->header_len;
		/* Copy received IP data from SKB */
		memcpy(req->buf + req->length, skb->data, skb->len);
		req->length = req->length + skb->len;
		/* Increment req length by skb data length */
		req->length += skb->len;
		length = req->length;
		dev_kfree_skb_any(skb);

@@ -931,12 +960,8 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
		req->context = 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
@@ -983,11 +1008,13 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
			dev_kfree_skb_any(skb);
		else
			req->length = 0;
drop:
		dev->net->stats.tx_dropped++;
multiframe:
		spin_lock_irqsave(&dev->req_lock, flags);
		if (list_empty(&dev->tx_reqs))
			netif_start_queue(net);
		list_add(&req->list, &dev->tx_reqs);
		list_add_tail(&req->list, &dev->tx_reqs);
		spin_unlock_irqrestore(&dev->req_lock, flags);
	}
success:
@@ -1446,6 +1473,8 @@ int gether_register_netdev(struct net_device *net)
	else
		INFO(dev, "MAC %pM\n", dev->dev_mac);

	uether_debugfs_init(dev);

	return status;
}
EXPORT_SYMBOL_GPL(gether_register_netdev);
@@ -1584,6 +1613,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);
@@ -1612,6 +1642,11 @@ struct net_device *gether_connect(struct gether *link)
	if (!dev)
		return ERR_PTR(-EINVAL);

	/* size of rndis_packet_msg_type is 44 */
	link->header = kzalloc(44, GFP_ATOMIC);
	if (!link->header)
		return ERR_PTR(-ENOMEM);

	if (link->in_ep) {
		link->in_ep->driver_data = dev;
		result = usb_ep_enable(link->in_ep);
@@ -1675,8 +1710,12 @@ struct net_device *gether_connect(struct gether *link)
	}
fail0:
	/* caller is responsible for cleanup on error */
	if (result < 0)
	if (result < 0) {
		kfree(link->header);

		return ERR_PTR(result);
	}

	return dev->net;
}
EXPORT_SYMBOL_GPL(gether_connect);
@@ -1729,6 +1768,9 @@ void gether_disconnect(struct gether *link)
			spin_lock(&dev->req_lock);
		}
		spin_unlock(&dev->req_lock);
		/* Free rndis header buffer memory */
		kfree(link->header);
		link->header = NULL;
		link->in_ep->desc = NULL;
	}

+1 −0
Original line number Diff line number Diff line
@@ -86,6 +86,7 @@ struct gether {
	/* called on network open/close */
	void				(*open)(struct gether *);
	void				(*close)(struct gether *);
	struct rndis_packet_msg_type	*header;
};

#define	DEFAULT_FILTER	(USB_CDC_PACKET_TYPE_BROADCAST \