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

Commit 9d8d2f45 authored by Jack Pham's avatar Jack Pham
Browse files

usb: gadget: qti: Move miscdevice to context structure



The misc device class uses the dev's drvdata field to store a
pointer to the instance of struct miscdevice. However, the USB
QTI driver is overwriting it with its RMNET-specific context
pointer, which leads to invalid access by namely misc_devnode()
when it retrieves an unexpected pointer.

This unexpected pointer was somewhat benign on 32-bit kernels
since the miscdevice.nodename pointer accessed in misc_devnode()
happens to align exactly with the rmnet_ctrl_qti_port.line_state
member which is initially set to 0. However on 64-bit kernels,
pointers occupy 8 bytes instead of 4, such that the nodename
member now overlaps a raw_spinlock magic field and causes the
code to proceed past the NULL pointer check and crash.

Clean up the USB QTI driver by moving the miscdevice and name
instances into the rmnet_ctrl_qti_port structure itself. This
allows the use container_of to retrieve the context in the file
operation callbacks instead of relying on improperly used
dev_set/get_drvdata.

Change-Id: I9351c3d83565c37eb03f160a9003278a51a66c83
Signed-off-by: default avatarJack Pham <jackp@codeaurora.org>
parent ce80330c
Loading
Loading
Loading
Loading
+36 −31
Original line number Diff line number Diff line
@@ -18,8 +18,12 @@

#include "u_rmnet.h"

#define RMNET_CTRL_QTI_NAME "rmnet_ctrl"

struct rmnet_ctrl_qti_port {
	struct grmnet	*port_usb;
	char		name[sizeof(RMNET_CTRL_QTI_NAME) + 2];
	struct miscdevice rmnet_device;

	bool		is_open;
	int index;
@@ -259,21 +263,18 @@ void gqti_ctrl_update_ipa_pipes(struct grmnet *gr, u8 port_num, u32 ipa_prod,
static int rmnet_ctrl_open(struct inode *ip, struct file *fp)
{
	unsigned long		flags;
	struct miscdevice *rmnet_device = fp->private_data;
	struct rmnet_ctrl_qti_port *port;

	port = dev_get_drvdata(rmnet_device->this_device);
	struct rmnet_ctrl_qti_port *port = container_of(fp->private_data,
						struct rmnet_ctrl_qti_port,
						rmnet_device);

	pr_debug("Open rmnet_ctrl_qti device file name=%s(index=%d)\n",
		rmnet_device->name, port->index);
		port->name, port->index);

	if (rmnet_ctrl_lock(&port->open_excl)) {
		pr_err("Already opened\n");
		return -EBUSY;
	}

	fp->private_data = port;

	spin_lock_irqsave(&port->lock, flags);
	port->is_open = true;
	spin_unlock_irqrestore(&port->lock, flags);
@@ -284,7 +285,9 @@ static int rmnet_ctrl_open(struct inode *ip, struct file *fp)
static int rmnet_ctrl_release(struct inode *ip, struct file *fp)
{
	unsigned long		flags;
	struct rmnet_ctrl_qti_port *port = fp->private_data;
	struct rmnet_ctrl_qti_port *port = container_of(fp->private_data,
						struct rmnet_ctrl_qti_port,
						rmnet_device);

	pr_debug("Close rmnet control file");

@@ -300,7 +303,9 @@ static int rmnet_ctrl_release(struct inode *ip, struct file *fp)
static ssize_t
rmnet_ctrl_read(struct file *fp, char __user *buf, size_t count, loff_t *pos)
{
	struct rmnet_ctrl_qti_port *port = fp->private_data;
	struct rmnet_ctrl_qti_port *port = container_of(fp->private_data,
						struct rmnet_ctrl_qti_port,
						rmnet_device);
	struct rmnet_ctrl_pkt *cpkt = NULL;
	unsigned long flags;
	int ret = 0;
@@ -371,7 +376,9 @@ static ssize_t
rmnet_ctrl_write(struct file *fp, const char __user *buf, size_t count,
		   loff_t *pos)
{
	struct rmnet_ctrl_qti_port *port = fp->private_data;
	struct rmnet_ctrl_qti_port *port = container_of(fp->private_data,
						struct rmnet_ctrl_qti_port,
						rmnet_device);
	void *kbuf;
	unsigned long flags;
	int ret = 0;
@@ -445,7 +452,9 @@ rmnet_ctrl_write(struct file *fp, const char __user *buf, size_t count,

static long rmnet_ctrl_ioctl(struct file *fp, unsigned cmd, unsigned long arg)
{
	struct rmnet_ctrl_qti_port *port = fp->private_data;
	struct rmnet_ctrl_qti_port *port = container_of(fp->private_data,
						struct rmnet_ctrl_qti_port,
						rmnet_device);
	struct ep_info info;
	int val, ret = 0;

@@ -504,7 +513,9 @@ static long rmnet_ctrl_ioctl(struct file *fp, unsigned cmd, unsigned long arg)

static unsigned int rmnet_ctrl_poll(struct file *file, poll_table *wait)
{
	struct rmnet_ctrl_qti_port *port = file->private_data;
	struct rmnet_ctrl_qti_port *port = container_of(file->private_data,
						struct rmnet_ctrl_qti_port,
						rmnet_device);
	unsigned long flags;
	unsigned int mask = 0;

@@ -536,26 +547,11 @@ static const struct file_operations rmnet_ctrl_fops = {
	.poll = rmnet_ctrl_poll,
};

static struct miscdevice rmnet_device[NR_QTI_PORTS];
#define RMNET_CTRL_QTI_NAME "rmnet_ctrl"
static char name[NR_QTI_PORTS][sizeof(RMNET_CTRL_QTI_NAME) + 2];

static int __init gqti_ctrl_init(void)
{
	int ret, i, sz = sizeof(RMNET_CTRL_QTI_NAME)+2;
	struct rmnet_ctrl_qti_port *port = NULL;

	for (i = 0; i < NR_QTI_PORTS; i++) {
		if (i == 0)
			strlcat(name[i], RMNET_CTRL_QTI_NAME, sz);
		else
			snprintf(name[i], sz, "%s%d", RMNET_CTRL_QTI_NAME, i);

		rmnet_device[i].name = name[i];
		rmnet_device[i].fops = &rmnet_ctrl_fops;
		rmnet_device[i].minor = MISC_DYNAMIC_MINOR;
	}

	for (i = 0; i < NR_QTI_PORTS; i++) {
		port = kzalloc(sizeof(struct rmnet_ctrl_qti_port), GFP_KERNEL);
		if (!port) {
@@ -581,18 +577,28 @@ static int __init gqti_ctrl_init(void)
		port->ipa_prod_idx = -1;
		port->ipa_cons_idx = -1;

		ret = misc_register(&rmnet_device[i]);
		if (i == 0)
			strlcat(port->name, RMNET_CTRL_QTI_NAME, sz);
		else
			snprintf(port->name, sz, "%s%d",
					RMNET_CTRL_QTI_NAME, i);

		port->rmnet_device.name = port->name;
		port->rmnet_device.fops = &rmnet_ctrl_fops;
		port->rmnet_device.minor = MISC_DYNAMIC_MINOR;

		ret = misc_register(&port->rmnet_device);
		if (ret) {
			pr_err("rmnet control driver failed to register");
			goto fail_init;
		}
		dev_set_drvdata(rmnet_device[i].this_device, port);
	}

	return ret;

fail_init:
	for (i--; i >= 0; i--) {
		misc_deregister(&ctrl_port[i]->rmnet_device);
		kfree(ctrl_port[i]);
		ctrl_port[i] = NULL;
	}
@@ -605,8 +611,7 @@ static void __exit gqti_ctrl_cleanup(void)
	int i;

	for (i = 0; i < NR_QTI_PORTS; i++) {
		misc_deregister(&rmnet_device[i]);

		misc_deregister(&ctrl_port[i]->rmnet_device);
		kfree(ctrl_port[i]);
		ctrl_port[i] = NULL;
	}