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

Commit 6f090cae authored by Rohit Vaswani's avatar Rohit Vaswani
Browse files

misc: qfpfuse: Fix overflow condition



Fix potential integer overflow for ioctl command to avoid incorrect
buffer allocation. Use the stack for data buffering to avoid the
small buffer allocation for improved performance. Increase the fuse
blow timeout in the driver to cover all QFPROM implementations.

CRs-Fixed: 550574,550575
Change-Id: Ie48cc2545a41ab422a9765124e2d6fa6e419858f
Acked-by: default avatarKaushik Sikdar <ksikdar@qti.qualcomm.com>
Signed-off-by: default avatarRohit Vaswani <rvaswani@codeaurora.org>
parent dbf6ecc2
Loading
Loading
Loading
Loading
+49 −34
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@
/*
 * Time QFPROM requires to reliably burn a fuse.
 */
#define QFPROM_BLOW_TIMEOUT_US      10
#define QFPROM_BLOW_TIMEOUT_US      20
#define QFPROM_BLOW_TIMER_OFFSET    0x2038
/*
 * Denotes number of cycles required to blow the fuse.
@@ -42,6 +42,9 @@

#define QFP_FUSE_READY              0x01
#define QFP_FUSE_OFF                0x00

#define QFP_FUSE_BUF_SIZE           64

static const char *blow_supply = "vdd-blow";

struct qfp_priv_t {
@@ -63,6 +66,20 @@ struct qfp_resource {
/* We need only one instance of this for the driver */
static struct qfp_priv_t *qfp_priv;

static inline bool is_usr_req_valid(const struct qfp_fuse_req *req)
{
	uint32_t size = qfp_priv->end - qfp_priv->base;
	uint32_t req_size = req->size * sizeof(uint32_t);

	if ((req_size == 0) || (req_size > size))
		return false;
	if (req->offset >= size)
		return false;
	if ((req->offset + req_size) > size)
		return false;

	return true;
}

static int qfp_fuse_open(struct inode *inode, struct file *filp)
{
@@ -187,7 +204,9 @@ qfp_fuse_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int err = 0;
	struct qfp_fuse_req req;
	u32 *buf = NULL;
	u32 fuse_buf[QFP_FUSE_BUF_SIZE];
	u32 *buf = fuse_buf;
	u32 *ptr = NULL;
	int i;

	/* Verify user arguments. */
@@ -209,26 +228,22 @@ qfp_fuse_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
		}

		/* Check for limits */
		if (!req.size) {
			pr_err("Request size zero.\n");
			err = -EFAULT;
			break;
		}

		if (qfp_priv->base + req.offset + (req.size - 1) * 4 >
				qfp_priv->end) {
			pr_err("Req size exceeds QFPROM addr space\n");
			err = -EFAULT;
		if (is_usr_req_valid(&req) == false) {
			pr_err("Invalid request\n");
			err = -EINVAL;
			break;
		}

		if (req.size > QFP_FUSE_BUF_SIZE) {
			/* Allocate memory for buffer */
		buf = kzalloc(req.size * 4, GFP_KERNEL);
		if (buf == NULL) {
			ptr = kzalloc(req.size * 4, GFP_KERNEL);
			if (ptr == NULL) {
				pr_alert("No memory for data\n");
				err = -ENOMEM;
				break;
			}
			buf = ptr;
		}

		if (mutex_lock_interruptible(&qfp_priv->lock)) {
			err = -ERESTARTSYS;
@@ -260,26 +275,24 @@ qfp_fuse_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
			err = -EFAULT;
			break;
		}

		/* Check for limits */
		if (!req.size) {
			pr_err("Request size zero.\n");
			err = -EFAULT;
			break;
		}
		if (qfp_priv->base + req.offset + (req.size - 1) * 4 >
				qfp_priv->end) {
			pr_err("Req size exceeds QFPROM space\n");
			err = -EFAULT;
		if (is_usr_req_valid(&req) == false) {
			pr_err("Invalid request\n");
			err = -EINVAL;
			break;
		}

		if (req.size > QFP_FUSE_BUF_SIZE) {
			/* Allocate memory for buffer */
		buf = kzalloc(4 * (req.size), GFP_KERNEL);
		if (buf == NULL) {
			ptr = kzalloc(req.size * 4, GFP_KERNEL);
			if (ptr == NULL) {
				pr_alert("No memory for data\n");
				err = -ENOMEM;
				break;
			}
			buf = ptr;
		}

		/* Copy user data to local buffer */
		if (copy_from_user(buf, (void __user *)req.data,
@@ -306,7 +319,9 @@ qfp_fuse_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
		pr_err("Invalid ioctl command.\n");
		return -ENOTTY;
	}
	kfree(buf);

	kfree(ptr);

	return err;
}