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

Commit 2814c340 authored by Danny Segal's avatar Danny Segal
Browse files

usb: gadget: Fix switching out from multi configuration compositions



Fix a crash issue in the USB stack when there is a dynamic composition
switch from a multi configuration composition, e.g., (ECM:RNDIS:MBIM) to
another composition. The crash occurs because this composition has three
configuration, which are all using BAM to BAM connections. Therefore,
when we switch out from this composition, the same bam2bam_data_ports[]
array entry is free 3 times - once for each configuration during its
unbind function. When the MBIM configuration is unbound, it tries to
access this array entry which has already been freed, and a crash occurs.
The solutions used to fix this issue is to use a reference count for this
array entry, and free it only when the reference count gets to zero.
Additionally, a NULL pointer check is used before accessing this array.

Change-Id: I94b3af367da8cd344bacb6b84e84aed0f5f66655
CRs-Fixed: 545448
Signed-off-by: default avatarDanny Segal <dsegal@codeaurora.org>
parent fd3fd9c7
Loading
Loading
Loading
Loading
+21 −3
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ struct bam_data_ch_info {

struct bam_data_port {
	unsigned			port_num;
	unsigned int                    ref_count;
	struct data_port		*port_usb;
	struct bam_data_ch_info		data_ch;

@@ -337,20 +338,31 @@ static void bam2bam_data_connect_work(struct work_struct *w)

static void bam2bam_data_port_free(int portno)
{
	if (bam2bam_data_ports[portno] == NULL)
		return;

	if (--bam2bam_data_ports[portno]->ref_count == 0) {
		kfree(bam2bam_data_ports[portno]);
		bam2bam_data_ports[portno] = NULL;
	}
}

static int bam2bam_data_port_alloc(int portno)
{
	struct bam_data_port	*port = NULL;
	struct bam_data_ch_info	*d = NULL;

	if (bam2bam_data_ports[portno] != NULL) {
		bam2bam_data_ports[portno]->ref_count++;
		goto done;
	}

	port = kzalloc(sizeof(struct bam_data_port), GFP_KERNEL);
	if (!port)
		return -ENOMEM;

	port->port_num  = portno;
	port->ref_count = 1;

	INIT_WORK(&port->connect_w, bam2bam_data_connect_work);
	INIT_WORK(&port->disconnect_w, bam2bam_data_disconnect_work);
@@ -364,6 +376,7 @@ static int bam2bam_data_port_alloc(int portno)
	d->ipa_params.src_client = IPA_CLIENT_USB_PROD;
	d->ipa_params.dst_client = IPA_CLIENT_USB_CONS;

done:
	pr_debug("port:%p portno:%d\n", port, portno);

	return 0;
@@ -388,6 +401,11 @@ void bam_data_disconnect(struct data_port *gr, u8 port_num)

	port = bam2bam_data_ports[port_num];

	if (!port) {
		pr_err("port %u is NULL", port_num);
		return;
	}

	if (port->port_usb && port->port_usb->in &&
	  port->port_usb->in->driver_data) {
		/* disable endpoints */