From f53bcf29a6e7a66b3d935b8d562fa00829261f05 Mon Sep 17 00:00:00 2001
From: Bingzhe Cai <bingzhec@codeaurora.org>
Date: Tue, 24 Sep 2013 01:42:12 +0800
Subject: input: touchpanel: fix security issues in GT915 driver

There are multiple buffer overflow and input validation issues
in Goodix gt915 driver, fix these issues by adding data length
check and change file system node mode.

CRs-Fixed: 526101
Change-Id: I5173fc1ca021fd45c939c7c8a4f460651330de5b
Signed-off-by: Bingzhe Cai <bingzhec@codeaurora.org>
---
 drivers/input/touchscreen/gt9xx/goodix_tool.c | 110 +++++++++++++++++++-------
 1 file changed, 83 insertions(+), 27 deletions(-)

diff --git a/drivers/input/touchscreen/gt9xx/goodix_tool.c b/drivers/input/touchscreen/gt9xx/goodix_tool.c
index bdac3fd..aa8159f 100644
--- a/drivers/input/touchscreen/gt9xx/goodix_tool.c
+++ b/drivers/input/touchscreen/gt9xx/goodix_tool.c
@@ -22,6 +22,7 @@
  */
 
 #include "gt9xx.h"
+#include <linux/mutex.h>
 
 #define DATA_LENGTH_UINT    512
 #define CMD_HEAD_LENGTH     (sizeof(st_cmd_head) - sizeof(u8 *))
@@ -53,6 +54,8 @@ static struct i2c_client *gt_client;
 
 static struct proc_dir_entry *goodix_proc_entry;
 
+static struct mutex lock;
+
 static s32 goodix_tool_write(struct file *filp, const char __user *buff,
 						unsigned long len, void *data);
 static s32 goodix_tool_read(char *page, char **start, off_t off, int count,
@@ -188,7 +191,7 @@ static void unregister_i2c_func(void)
 
 s32 init_wr_node(struct i2c_client *client)
 {
-	s32 i;
+	u8 i;
 
 	gt_client = client;
 	memset(&cmd_head, 0, sizeof(cmd_head));
@@ -202,8 +205,8 @@ s32 init_wr_node(struct i2c_client *client)
 		i--;
 	}
 	if (i) {
-		DATA_LENGTH = i * DATA_LENGTH_UINT + GTP_ADDR_LENGTH;
-		GTP_INFO("Applied memory size:%d.", DATA_LENGTH);
+		DATA_LENGTH = i * DATA_LENGTH_UINT;
+		dev_dbg(&client->dev, "Applied memory size:%d.", DATA_LENGTH);
 	} else {
 		GTP_ERROR("Apply for memory failed.");
 		return FAIL;
@@ -214,8 +217,9 @@ s32 init_wr_node(struct i2c_client *client)
 
 	register_i2c_func();
 
+	mutex_init(&lock);
 	tool_set_proc_name(procname);
-	goodix_proc_entry = create_proc_entry(procname, 0666, NULL);
+	goodix_proc_entry = create_proc_entry(procname, 0660, NULL);
 	if (goodix_proc_entry == NULL) {
 		GTP_ERROR("Couldn't create proc entry!");
 		return FAIL;
@@ -334,9 +338,13 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
 	GTP_DEBUG_FUNC();
 	GTP_DEBUG_ARRAY((u8 *)buff, len);
 
+	mutex_lock(&lock);
 	ret = copy_from_user(&cmd_head, buff, CMD_HEAD_LENGTH);
-	if (ret)
+	if (ret) {
 		GTP_ERROR("copy_from_user failed.");
+		ret = -EACCES;
+		goto exit;
+	}
 
 	GTP_DEBUG("wr  :0x%02x.", cmd_head.wr);
 	GTP_DEBUG("flag:0x%02x.", cmd_head.flag);
@@ -354,6 +362,19 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
 	GTP_DEBUG("len:%d.", (s32)len);
 	GTP_DEBUG("buf[20]:0x%02x.", buff[CMD_HEAD_LENGTH]);
 
+	if (cmd_head.data_len > (DATA_LENGTH - GTP_ADDR_LENGTH)) {
+		pr_err("data len %d > data buff %d, rejected!\n",
+			cmd_head.data_len, (DATA_LENGTH - GTP_ADDR_LENGTH));
+		ret = -EINVAL;
+		goto exit;
+	}
+	if (cmd_head.addr_len > GTP_ADDR_LENGTH) {
+		pr_err(" addr len %d > data buff %d, rejected!\n",
+			cmd_head.addr_len, GTP_ADDR_LENGTH);
+		ret = -EINVAL;
+		goto exit;
+	}
+
 	if (cmd_head.wr == 1) {
 		/*  copy_from_user(&cmd_head.data[cmd_head.addr_len],
 				&buff[CMD_HEAD_LENGTH], cmd_head.data_len); */
@@ -373,7 +394,8 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
 		if (cmd_head.flag == 1) {
 			if (FAIL == comfirm()) {
 				GTP_ERROR("[WRITE]Comfirm fail!");
-				return FAIL;
+				ret = -EINVAL;
+				goto exit;
 			}
 		} else if (cmd_head.flag == 2) {
 			/* Need interrupt! */
@@ -382,7 +404,8 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
 		&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len],
 		cmd_head.data_len + cmd_head.addr_len) <= 0) {
 			GTP_ERROR("[WRITE]Write data failed!");
-			return FAIL;
+			ret = -EIO;
+			goto exit;
 		}
 
 		GTP_DEBUG_ARRAY(
@@ -391,7 +414,8 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
 		if (cmd_head.delay)
 			msleep(cmd_head.delay);
 
-		return cmd_head.data_len + CMD_HEAD_LENGTH;
+		ret = cmd_head.data_len + CMD_HEAD_LENGTH;
+		goto exit;
 	} else if (cmd_head.wr == 3) {  /* Write ic type */
 
 		ret = copy_from_user(&cmd_head.data[0], &buff[CMD_HEAD_LENGTH],
@@ -399,30 +423,40 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
 		if (ret)
 			GTP_ERROR("copy_from_user failed.");
 
+		if (cmd_head.data_len > sizeof(IC_TYPE)) {
+			pr_err("<<-GTP->> data len %d > data buff %d, rejected!\n",
+			cmd_head.data_len, sizeof(IC_TYPE));
+			ret = -EINVAL;
+			goto exit;
+		}
 		memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len);
 
 		register_i2c_func();
 
-		return cmd_head.data_len + CMD_HEAD_LENGTH;
-	} else if (cmd_head.wr == 3) {
+		ret = cmd_head.data_len + CMD_HEAD_LENGTH;
+		goto exit;
+	} else if (cmd_head.wr == 5) {
 
 		/* memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len); */
 
-		return cmd_head.data_len + CMD_HEAD_LENGTH;
+		ret = cmd_head.data_len + CMD_HEAD_LENGTH;
+		goto exit;
 	} else if (cmd_head.wr == 7) { /* disable irq! */
 		gtp_irq_disable(i2c_get_clientdata(gt_client));
 
 		#if GTP_ESD_PROTECT
 		gtp_esd_switch(gt_client, SWITCH_OFF);
 		#endif
-		return CMD_HEAD_LENGTH;
+		ret = CMD_HEAD_LENGTH;
+		goto exit;
 	} else if (cmd_head.wr == 9) { /* enable irq! */
 		gtp_irq_enable(i2c_get_clientdata(gt_client));
 
 		#if GTP_ESD_PROTECT
 		gtp_esd_switch(gt_client, SWITCH_ON);
 		#endif
-		return CMD_HEAD_LENGTH;
+		ret = CMD_HEAD_LENGTH;
+		goto exit;
 	} else if (cmd_head.wr == 17) {
 		struct goodix_ts_data *ts = i2c_get_clientdata(gt_client);
 		ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH],
@@ -436,27 +470,41 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
 			ts->gtp_rawdiff_mode = false;
 			GTP_DEBUG("gtp leave rawdiff.");
 		}
-		return CMD_HEAD_LENGTH;
+		ret = CMD_HEAD_LENGTH;
+		goto exit;
 	}
 #ifdef UPDATE_FUNCTIONS
 	else if (cmd_head.wr == 11) { /* Enter update mode! */
-		if (FAIL == gup_enter_update_mode(gt_client))
-			return FAIL;
+		if (FAIL == gup_enter_update_mode(gt_client)) {
+			ret = -EBUSY;
+			goto exit;
+		}
 	} else if (cmd_head.wr == 13) { /* Leave update mode! */
 		gup_leave_update_mode();
 	} else if (cmd_head.wr == 15) { /* Update firmware! */
 		show_len = 0;
 		total_len = 0;
+		if (cmd_head.data_len + 1 > DATA_LENGTH) {
+			pr_err("<<-GTP->> data len %d > data buff %d, rejected!\n",
+			cmd_head.data_len + 1, DATA_LENGTH);
+			ret = -EINVAL;
+			goto exit;
+		}
 		memset(cmd_head.data, 0, cmd_head.data_len + 1);
 		memcpy(cmd_head.data, &buff[CMD_HEAD_LENGTH],
 					cmd_head.data_len);
 
-		if (FAIL == gup_update_proc((void *)cmd_head.data))
-			return FAIL;
+		if (FAIL == gup_update_proc((void *)cmd_head.data)) {
+			ret = -EBUSY;
+			goto exit;
+		}
 	}
 #endif
+	ret = CMD_HEAD_LENGTH;
 
-	return CMD_HEAD_LENGTH;
+exit:
+	mutex_unlock(&lock);
+	return ret;
 }
 
 /*******************************************************
@@ -470,10 +518,14 @@ Output:
 static s32 goodix_tool_read(char *page, char **start, off_t off, int count,
 							int *eof, void *data)
 {
+	s32 ret;
 	GTP_DEBUG_FUNC();
 
+	mutex_lock(&lock);
 	if (cmd_head.wr % 2) {
-		return FAIL;
+		pr_err("<< [READ]command head wrong\n");
+		ret = -EINVAL;
+		goto exit;
 	} else if (!cmd_head.wr) {
 		u16 len = 0;
 		s16 data_len = 0;
@@ -482,7 +534,8 @@ static s32 goodix_tool_read(char *page, char **start, off_t off, int count,
 		if (cmd_head.flag == 1) {
 			if (FAIL == comfirm()) {
 				GTP_ERROR("[READ]Comfirm fail!");
-				return FAIL;
+				ret = -EINVAL;
+				goto exit;
 			}
 		} else if (cmd_head.flag == 2) {
 			/* Need interrupt! */
@@ -505,11 +558,12 @@ static s32 goodix_tool_read(char *page, char **start, off_t off, int count,
 			else
 				len = data_len;
 
-			data_len -= DATA_LENGTH;
+			data_len -= len;
 
 			if (tool_i2c_read(cmd_head.data, len) <= 0) {
 				GTP_ERROR("[READ]Read data failed!");
-				return FAIL;
+				ret = -EINVAL;
+				goto exit;
 			}
 			memcpy(&page[loc], &cmd_head.data[GTP_ADDR_LENGTH],
 									len);
@@ -525,15 +579,14 @@ static s32 goodix_tool_read(char *page, char **start, off_t off, int count,
 
 		GTP_DEBUG("Return ic type:%s len:%d.", page,
 						(s32)cmd_head.data_len);
-		return cmd_head.data_len;
+		ret = cmd_head.data_len;
+		goto exit;
 		/* return sizeof(IC_TYPE_NAME); */
 	} else if (cmd_head.wr == 4) {
 		page[0] = show_len >> 8;
 		page[1] = show_len & 0xff;
 		page[2] = total_len >> 8;
 		page[3] = total_len & 0xff;
-
-		return cmd_head.data_len;
 	} else if (6 == cmd_head.wr) {
 		/* Read error code! */
 	} else if (8 == cmd_head.wr) { /*Read driver version */
@@ -544,6 +597,9 @@ static s32 goodix_tool_read(char *page, char **start, off_t off, int count,
 		memcpy(page, GTP_DRIVER_VERSION, tmp_len);
 		page[tmp_len] = 0;
 	}
+	ret = cmd_head.data_len;
 
-	return cmd_head.data_len;
+exit:
+	mutex_unlock(&lock);
+	return ret;
 }
-- 
cgit v1.1