tzcom: Add more error checks and safe operations

Use safe list iterator where needed, check return
value for scm_call and add condition check for
different input buffer lengths.

Signed-off-by: Sachin Shah <sachins@codeaurora.org>
diff --git a/drivers/misc/tzcom.c b/drivers/misc/tzcom.c
index ef8f126..ccd8444 100644
--- a/drivers/misc/tzcom.c
+++ b/drivers/misc/tzcom.c
@@ -208,7 +208,7 @@
 	int ret = 0;
 	unsigned long flags;
 	struct tzcom_unregister_svc_op_req req;
-	struct tzcom_registered_svc_list *ptr;
+	struct tzcom_registered_svc_list *ptr, *next;
 	ret = copy_from_user(&req, argp, sizeof(req));
 	if (ret) {
 		PERR("copy_from_user failed");
@@ -216,7 +216,8 @@
 	}
 
 	spin_lock_irqsave(&data->registered_svc_list_lock, flags);
-	list_for_each_entry(ptr, &data->registered_svc_list_head, list) {
+	list_for_each_entry_safe(ptr, next, &data->registered_svc_list_head,
+			list) {
 		if (req.svc_id == ptr->svc.svc_id &&
 				req.instance_id == ptr->svc.instance_id) {
 			wake_up_all(&ptr->next_cmd_wq);
@@ -296,7 +297,8 @@
 		return -EINVAL;
 	}
 
-	if (req.cmd_len <= 0 || req.resp_len <= 0) {
+	if (req.cmd_len <= 0 || req.resp_len <= 0 ||
+		req.cmd_len > sb_in_length || req.resp_len > sb_in_length) {
 		PERR("cmd buffer length or "
 				"response buffer length not valid");
 		return -EINVAL;
@@ -334,9 +336,15 @@
 	PDEBUG("before call tzcom_scm_call, cmd_id = : %u", req.cmd_id);
 	PDEBUG("before call tzcom_scm_call, sizeof(cmd) = : %u", sizeof(cmd));
 
-	tzcom_scm_call((const void *) &cmd, sizeof(cmd), &resp, sizeof(resp));
+	ret = tzcom_scm_call((const void *) &cmd, sizeof(cmd),
+			&resp, sizeof(resp));
 	mutex_unlock(&sb_in_lock);
 
+	if (ret) {
+		PERR("tzcom_scm_call failed with err: %d", ret);
+		return ret;
+	}
+
 	while (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
 		/*
 		 * If cmd is incomplete, get the callback cmd out from SB out
@@ -348,11 +356,13 @@
 		mutex_lock(&sb_out_lock);
 		reqd_len_sb_out = sizeof(*next_callback)
 					+ next_callback->sb_out_cb_data_len;
-		if (reqd_len_sb_out > sb_out_length) {
-			PERR("Not enough memory to"
-					" fit tzcom_callback buffer."
-					" Required: %u, Available: %u",
-					reqd_len_sb_out, sb_out_length);
+		if (reqd_len_sb_out > sb_out_length ||
+			reqd_len_sb_out < sizeof(*next_callback) ||
+			next_callback->sb_out_cb_data_len > sb_out_length) {
+			PERR("Incorrect callback data length"
+					" Required: %u, Available: %u, Min: %u",
+					reqd_len_sb_out, sb_out_length,
+					sizeof(*next_callback));
 			mutex_unlock(&sb_out_lock);
 			return -ENOMEM;
 		}
@@ -370,7 +380,7 @@
 		cb = &new_entry->callback;
 		cb->cmd_id = next_callback->cmd_id;
 		cb->sb_out_cb_data_len = next_callback->sb_out_cb_data_len;
-		cb->sb_out_cb_data_off = next_callback->sb_out_cb_data_off;
+		cb->sb_out_cb_data_off = sizeof(*cb);
 
 		cb_data = (u8 *)next_callback
 				+ next_callback->sb_out_cb_data_off;
@@ -406,9 +416,13 @@
 		data->cont_cmd_flag = 0;
 		cmd.cmd_type = TZ_SCHED_CMD_PENDING;
 		mutex_lock(&sb_in_lock);
-		tzcom_scm_call((const void *) &cmd, sizeof(cmd), &resp,
+		ret = tzcom_scm_call((const void *) &cmd, sizeof(cmd), &resp,
 				sizeof(resp));
 		mutex_unlock(&sb_in_lock);
+		if (ret) {
+			PERR("tzcom_scm_call failed with err: %d", ret);
+			return ret;
+		}
 	}
 
 	mutex_lock(&sb_in_lock);
@@ -455,13 +469,13 @@
 {
 	int found = 0;
 	int ret = -EAGAIN;
-	struct tzcom_callback_list *entry;
+	struct tzcom_callback_list *entry, *next;
 	struct tzcom_callback *cb;
 
 	PDEBUG("In here");
 	mutex_lock(&data->callback_list_lock);
 	PDEBUG("Before looping through cmd and svc lists.");
-	list_for_each_entry(entry, &data->callback_list_head, list) {
+	list_for_each_entry_safe(entry, next, &data->callback_list_head, list) {
 		cb = &entry->callback;
 		if (req->svc_id == ptr_svc->svc.svc_id &&
 			req->instance_id == ptr_svc->svc.instance_id &&
@@ -644,6 +658,7 @@
 
 static int tzcom_open(struct inode *inode, struct file *file)
 {
+	int ret;
 	long pil_error;
 	struct tz_pr_init_sb_req_s sb_out_init_req;
 	struct tz_pr_init_sb_rsp_s sb_out_init_rsp;
@@ -689,8 +704,13 @@
 	resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
 
 	PDEBUG("Before scm_call for sb_init");
-	tzcom_scm_call(&cmd, sizeof(cmd), &resp, sizeof(resp));
+	ret = tzcom_scm_call(&cmd, sizeof(cmd), &resp, sizeof(resp));
+	if (ret) {
+		PERR("tzcom_scm_call failed with err: %d", ret);
+		return ret;
+	}
 	PDEBUG("After scm_call for sb_init");
+
 	PDEBUG("tzcom_response after scm cmd_status: %u", resp.cmd_status);
 	if (resp.cmd_status == TZ_SCHED_STATUS_COMPLETE) {
 		resp.sb_in_rsp_addr = (u8 *)cmd.sb_in_cmd_addr +