touchscreen: fts: Fix race conditions in fts diag test interface.

fts touch driver diag core file operations were not thread safe,
allowing potentially multiple user space processes/threads to
operate on the exposed diag procfs/sysfs entry. This can cause
race while allocating/freeing/accessing global command buffer
used during diagnostics command execution. This CL fixes multiple
such races by allowing only one thread to perform file operation at
any given point of time.

Bug: 119120571
Bug: 120141034

Change-Id: Ia663988260faa948854f370c48f541fa285cc2e8
Signed-off-by: Biswajit Dash <bisdash@google.com>
diff --git a/drivers/input/touchscreen/stm/fts.c b/drivers/input/touchscreen/stm/fts.c
index d026339..d646639 100644
--- a/drivers/input/touchscreen/stm/fts.c
+++ b/drivers/input/touchscreen/stm/fts.c
@@ -1422,10 +1422,22 @@
 {
 	int n;
 	char *p = (char *)buf;
+	struct fts_ts_info *info = dev_get_drvdata(dev);
 
 	memset(typeOfComand, 0, CMD_STR_LEN * sizeof(u32));
 
 	pr_info("%s:\n", __func__);
+
+	if (!info) {
+		pr_err("%s: Unable to access driver data\n", __func__);
+		return  -EINVAL;
+	}
+
+	if (!mutex_trylock(&info->diag_cmd_lock)) {
+		pr_err("%s: Blocking concurrent access\n", __func__);
+		return -EBUSY;
+	}
+
 	for (n = 0; n < (count + 1) / 3; n++) {
 		sscanf(p, "%02X ", &typeOfComand[n]);
 		p += 3;
@@ -1434,6 +1446,9 @@
 
 	numberParameters = n;
 	pr_info("Number of Parameters = %d\n", numberParameters);
+
+	mutex_unlock(&info->diag_cmd_lock);
+
 	return count;
 }
 
@@ -1452,11 +1467,22 @@
 	MutualSenseFrame frameMS;
 	SelfSenseFrame frameSS;
 
+	if (!info) {
+		pr_err("%s: Unable to access driver data\n", __func__);
+		return  -EINVAL;
+	}
+
+	if (!mutex_trylock(&info->diag_cmd_lock)) {
+		pr_err("%s: Blocking concurrent access\n", __func__);
+		return -EBUSY;
+	}
+
 	if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
 		res = ERROR_BUS_WR;
 		pr_err("%s: bus is not accessible.\n", __func__);
 		scnprintf(buf, PAGE_SIZE, "{ %08X }\n", res);
 		fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+		mutex_unlock(&info->diag_cmd_lock);
 		return 0;
 	}
 
@@ -1959,6 +1985,8 @@
 	/* pr_err("numberParameters = %d\n", numberParameters); */
 
 	fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+	mutex_unlock(&info->diag_cmd_lock);
+
 	return index;
 }
 
@@ -4161,6 +4189,8 @@
 	input_set_capability(info->input_dev, EV_KEY, KEY_MENU);
 #endif
 
+	mutex_init(&info->diag_cmd_lock);
+
 	mutex_init(&(info->input_report_mutex));
 	mutex_init(&info->bus_mutex);
 
diff --git a/drivers/input/touchscreen/stm/fts.h b/drivers/input/touchscreen/stm/fts.h
index cb741d3..5372736 100644
--- a/drivers/input/touchscreen/stm/fts.h
+++ b/drivers/input/touchscreen/stm/fts.h
@@ -381,6 +381,11 @@
 	struct tbn_context	*tbn;
 #endif
 
+	/* Allow only one thread to execute diag command code*/
+	struct mutex diag_cmd_lock;
+	/* Allow one process to open procfs node */
+	bool diag_node_open;
+
 	/* Preallocated i/o read buffer */
 	u8 io_read_buf[READ_CHUNK + DUMMY_FIFO];
 	/* Preallocated i/o write buffer */
diff --git a/drivers/input/touchscreen/stm/fts_proc.c b/drivers/input/touchscreen/stm/fts_proc.c
index 2b5f84d..b51e954 100644
--- a/drivers/input/touchscreen/stm/fts_proc.c
+++ b/drivers/input/touchscreen/stm/fts_proc.c
@@ -667,11 +667,139 @@
   * @param file file associated to the file node
   * @return error code, 0 if success
   */
-static int fts_open(struct inode *inode, struct file *file)
+static int fts_driver_test_open(struct inode *inode, struct file *file)
 {
-	return seq_open(file, &fts_seq_ops);
+	struct fts_ts_info *info = dev_get_drvdata(getDev());
+	int retval;
+
+	if (!info) {
+		pr_err("%s: Unable to access driver data\n", __func__);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	if (!mutex_trylock(&info->diag_cmd_lock)) {
+		pr_err("%s: Blocking concurrent access\n", __func__);
+		retval = -EBUSY;
+		goto exit;
+	}
+
+	/* Allowing only a single process to open diag procfs node */
+	if (info->diag_node_open == true) {
+		pr_err("%s: Blocking multiple open\n", __func__);
+		retval = -EBUSY;
+		goto unlock;
+	}
+
+	retval = seq_open(file, &fts_seq_ops);
+	if(!retval) {
+		info->diag_node_open = true;
+	}
+
+unlock:
+	mutex_unlock(&info->diag_cmd_lock);
+exit:
+	return retval;
 };
 
+/**
+  * This function closes a sequential file
+  * @param inode Inode in the file system that was called and triggered this
+  * function
+  * @param file file associated to the file node
+  * @return error code, 0 if success
+  */
+static int fts_driver_test_release(struct inode *inode, struct file *file)
+{
+	struct fts_ts_info *info = dev_get_drvdata(getDev());
+	int retval;
+
+	if (!info) {
+		pr_err("%s: Unable to access driver data\n", __func__);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	if (!mutex_trylock(&info->diag_cmd_lock)) {
+		pr_err("%s: Blocking concurrent access\n", __func__);
+		retval = -EBUSY;
+		goto exit;
+	}
+
+	retval = seq_release(inode, file);
+	info->diag_node_open = false;
+
+	mutex_unlock(&info->diag_cmd_lock);
+exit:
+	return retval;
+}
+
+
+/**
+  * This function reads a sequential file
+  * @param file  file associated to the file node
+  * @param buf 	 userspace buffer where the newly read data should be placed
+  * @param count size of the requested transfer.
+  * @param pos   start position from which data should be written in the file.
+  * @return error code, 0 if success
+  */
+static ssize_t fts_driver_test_read(struct file *file, char __user *buf,
+					size_t count, loff_t *pos)
+{
+	struct fts_ts_info *info = dev_get_drvdata(getDev());
+	ssize_t bytes_read = -EINVAL;
+
+	if (!info) {
+		pr_err("%s: Unable to access driver data\n", __func__);
+		bytes_read = -ENODEV;
+		goto exit;
+	}
+
+	if (!mutex_trylock(&info->diag_cmd_lock)) {
+		pr_err("%s: Blocking concurrent access\n", __func__);
+		bytes_read = -EBUSY;
+		goto exit;
+	}
+
+	bytes_read = seq_read(file, buf, count, pos);
+
+	mutex_unlock(&info->diag_cmd_lock);
+exit:
+	return bytes_read;
+}
+
+/**
+  * This function moves the cursor position within a file.
+  * @param file   file associated to the file node
+  * @param offset offset relative to the current file position.
+  * @param whence defines where to seek from.
+  * @return error code, 0 if success
+  */
+static loff_t fts_driver_test_lseek(struct file *file, loff_t offset,
+					int whence)
+{
+	struct fts_ts_info *info = dev_get_drvdata(getDev());
+	loff_t retval;
+
+	if (!info) {
+		pr_err("%s: Unable to access driver data\n", __func__);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	if (!mutex_trylock(&info->diag_cmd_lock)) {
+		pr_err("%s: Blocking concurrent access\n", __func__);
+		retval = -EBUSY;
+		goto exit;
+	}
+
+	retval = seq_lseek(file, offset, whence);
+
+	mutex_unlock(&info->diag_cmd_lock);
+
+exit:
+	return retval;
+}
 
 /*****************************************************************************/
 
@@ -729,6 +857,18 @@
 	Firmware fw;
 	LimitFile lim;
 
+	if (!info) {
+		pr_err("%s: Unable to access driver data\n", __func__);
+		count =  -ENODEV;
+		goto exit;
+	}
+
+	if (!mutex_trylock(&info->diag_cmd_lock)) {
+		pr_err("%s: Blocking concurrent access\n", __func__);
+		count = -EBUSY;
+		goto exit;
+	}
+
 	mess.dummy = 0;
 	mess.action = 0;
 	mess.msg_size = 0;
@@ -739,7 +879,6 @@
 		if (driver_test_buff)
 			limit = scnprintf(driver_test_buff, size, "{ %08X }\n",
 					  res);
-		fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
 		goto ERROR;
 	}
 
@@ -3276,6 +3415,8 @@
 
 	fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
 
+	mutex_unlock(&info->diag_cmd_lock);
+exit:
 	return count;
 }
 
@@ -3286,11 +3427,11 @@
   * operation on a device file node (open. read, write etc.)
   */
 static struct file_operations fts_driver_test_ops = {
-	.open		= fts_open,
-	.read		= seq_read,
+	.open		= fts_driver_test_open,
+	.read		= fts_driver_test_read,
 	.write		= fts_driver_test_write,
-	.llseek		= seq_lseek,
-	.release	= seq_release
+	.llseek		= fts_driver_test_lseek,
+	.release	= fts_driver_test_release
 };
 
 /*****************************************************************************/