mirror of
https://github.com/Divested-Mobile/DivestOS-Build.git
synced 2024-10-01 01:35:54 -04:00
384 lines
12 KiB
Diff
384 lines
12 KiB
Diff
From e909d95e6bded328e388d5b8d123297bbbb70728 Mon Sep 17 00:00:00 2001
|
|
From: Mona Hossain <mhossain@codeaurora.org>
|
|
Date: Mon, 3 Nov 2014 17:05:48 -0800
|
|
Subject: qseecom: Add checks for send_cmd inputs
|
|
|
|
Improve user input validation across send cmd APIs. Add new
|
|
API __validate_send_cmd_inputs() to validate all user provided
|
|
inputs.
|
|
|
|
Change-Id: Ibbb0c0e7e5483f653bd59b927562b63c1e43c365
|
|
Signed-off-by: Mona Hossain <mhossain@codeaurora.org>
|
|
---
|
|
drivers/misc/qseecom.c | 221 ++++++++++++++++++++++++++++++-------------------
|
|
1 file changed, 134 insertions(+), 87 deletions(-)
|
|
|
|
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
|
|
index 65001c5..244f1bf 100644
|
|
--- a/drivers/misc/qseecom.c
|
|
+++ b/drivers/misc/qseecom.c
|
|
@@ -981,7 +981,7 @@ static int qseecom_scale_bus_bandwidth(struct qseecom_dev_handle *data,
|
|
}
|
|
if (req_mode > HIGH) {
|
|
pr_err("Invalid bandwidth mode (%d)\n", req_mode);
|
|
- return ret;
|
|
+ return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
@@ -1834,24 +1834,16 @@ exit:
|
|
return ret;
|
|
}
|
|
|
|
-static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
|
|
+static int __validate_send_cmd_inputs(struct qseecom_dev_handle *data,
|
|
struct qseecom_send_cmd_req *req)
|
|
-{
|
|
- int ret = 0;
|
|
- u32 reqd_len_sb_in = 0;
|
|
- struct qseecom_client_send_data_ireq send_data_req;
|
|
- struct qseecom_command_scm_resp resp;
|
|
- unsigned long flags;
|
|
- struct qseecom_registered_app_list *ptr_app;
|
|
- bool found_app = false;
|
|
- int name_len = 0;
|
|
|
|
+{
|
|
if (!data || !data->client.ihandle) {
|
|
pr_err("Client or client handle is not initialized\n");
|
|
return -EINVAL;
|
|
}
|
|
-
|
|
- if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
|
|
+ if (((req->resp_buf == NULL) && (req->resp_len != 0)) ||
|
|
+ (req->cmd_req_buf == NULL)) {
|
|
pr_err("cmd buffer or response buffer is null\n");
|
|
return -EINVAL;
|
|
}
|
|
@@ -1862,8 +1854,6 @@ static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
|
|
pr_err("cmd buffer address not within shared bufffer\n");
|
|
return -EINVAL;
|
|
}
|
|
-
|
|
-
|
|
if (((uintptr_t)req->resp_buf <
|
|
data->client.user_virt_sb_base) ||
|
|
((uintptr_t)req->resp_buf >=
|
|
@@ -1871,27 +1861,62 @@ static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
|
|
pr_err("response buffer address not within shared bufffer\n");
|
|
return -EINVAL;
|
|
}
|
|
-
|
|
- if ((req->cmd_req_len == 0) || (req->resp_len == 0) ||
|
|
- req->cmd_req_len > data->client.sb_length ||
|
|
- req->resp_len > data->client.sb_length) {
|
|
- pr_err("cmd buffer length or response buffer length not valid\n");
|
|
+ if ((req->cmd_req_len == 0) ||
|
|
+ (req->cmd_req_len > data->client.sb_length) ||
|
|
+ (req->resp_len > data->client.sb_length)) {
|
|
+ pr_err("cmd buf length or response buf length not valid\n");
|
|
return -EINVAL;
|
|
}
|
|
-
|
|
if (req->cmd_req_len > UINT_MAX - req->resp_len) {
|
|
- pr_err("Integer overflow detected in req_len & rsp_len, exiting now\n");
|
|
+ pr_err("Integer overflow detected in req_len & rsp_len\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
- reqd_len_sb_in = req->cmd_req_len + req->resp_len;
|
|
- if (reqd_len_sb_in > data->client.sb_length) {
|
|
+ if ((req->cmd_req_len + req->resp_len) > data->client.sb_length) {
|
|
pr_debug("Not enough memory to fit cmd_buf.\n");
|
|
pr_debug("resp_buf. Required: %u, Available: %zu\n",
|
|
- reqd_len_sb_in, data->client.sb_length);
|
|
+ (req->cmd_req_len + req->resp_len),
|
|
+ data->client.sb_length);
|
|
return -ENOMEM;
|
|
}
|
|
+ if ((uintptr_t)req->cmd_req_buf > (ULONG_MAX - req->cmd_req_len)) {
|
|
+ pr_err("Integer overflow in req_len & cmd_req_buf\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if ((uintptr_t)req->resp_buf > (ULONG_MAX - req->resp_len)) {
|
|
+ pr_err("Integer overflow in resp_len & resp_buf\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if (data->client.user_virt_sb_base >
|
|
+ (ULONG_MAX - data->client.sb_length)) {
|
|
+ pr_err("Integer overflow in user_virt_sb_base & sb_length\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if ((((uintptr_t)req->cmd_req_buf + req->cmd_req_len) >
|
|
+ ((uintptr_t)data->client.user_virt_sb_base +
|
|
+ data->client.sb_length)) ||
|
|
+ (((uintptr_t)req->resp_buf + req->resp_len) >
|
|
+ ((uintptr_t)data->client.user_virt_sb_base +
|
|
+ data->client.sb_length))) {
|
|
+ pr_err("cmd buf or resp buf is out of shared buffer region\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
|
|
+static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
|
|
+ struct qseecom_send_cmd_req *req)
|
|
+{
|
|
+ int ret = 0;
|
|
+ u32 reqd_len_sb_in = 0;
|
|
+ struct qseecom_client_send_data_ireq send_data_req;
|
|
+ struct qseecom_command_scm_resp resp;
|
|
+ unsigned long flags;
|
|
+ struct qseecom_registered_app_list *ptr_app;
|
|
+ bool found_app = false;
|
|
+ int name_len = 0;
|
|
+
|
|
+ reqd_len_sb_in = req->cmd_req_len + req->resp_len;
|
|
/* find app_id & img_name from list */
|
|
spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
|
|
list_for_each_entry(ptr_app, &qseecom.registered_app_list_head,
|
|
@@ -1965,6 +1990,10 @@ static int qseecom_send_cmd(struct qseecom_dev_handle *data, void __user *argp)
|
|
pr_err("copy_from_user failed\n");
|
|
return ret;
|
|
}
|
|
+
|
|
+ if (__validate_send_cmd_inputs(data, &req))
|
|
+ return -EINVAL;
|
|
+
|
|
ret = __qseecom_send_cmd(data, &req);
|
|
|
|
if (ret)
|
|
@@ -1973,50 +2002,54 @@ static int qseecom_send_cmd(struct qseecom_dev_handle *data, void __user *argp)
|
|
return ret;
|
|
}
|
|
|
|
-int boundary_checks_offset(struct qseecom_send_modfd_cmd_req *req,
|
|
+int __boundary_checks_offset(struct qseecom_send_modfd_cmd_req *req,
|
|
struct qseecom_send_modfd_listener_resp *lstnr_resp,
|
|
struct qseecom_dev_handle *data, bool qteec,
|
|
int i) {
|
|
- int ret = 0;
|
|
|
|
if ((data->type != QSEECOM_LISTENER_SERVICE) &&
|
|
(req->ifd_data[i].fd > 0)) {
|
|
if (qteec) {
|
|
- if (req->ifd_data[i].cmd_buf_offset >
|
|
- req->cmd_req_len - TWO * sizeof(uint32_t)) {
|
|
- pr_err("Invalid offset 0x%x\n",
|
|
+ if ((req->cmd_req_len < (TWO * sizeof(uint32_t))) ||
|
|
+ (req->ifd_data[i].cmd_buf_offset >
|
|
+ req->cmd_req_len - (TWO * sizeof(uint32_t)))) {
|
|
+ pr_err("Invalid offset (QTEEC req len) 0x%x\n",
|
|
req->ifd_data[i].cmd_buf_offset);
|
|
- return ++ret;
|
|
+ return -EINVAL;
|
|
}
|
|
} else {
|
|
- if (req->ifd_data[i].cmd_buf_offset >
|
|
- req->cmd_req_len - sizeof(uint32_t)) {
|
|
- pr_err("Invalid offset 0x%x\n",
|
|
+ if ((req->cmd_req_len < sizeof(uint32_t)) ||
|
|
+ (req->ifd_data[i].cmd_buf_offset >
|
|
+ req->cmd_req_len - sizeof(uint32_t))) {
|
|
+ pr_err("Invalid offset (req len) 0x%x\n",
|
|
req->ifd_data[i].cmd_buf_offset);
|
|
- return ++ret;
|
|
+ return -EINVAL;
|
|
}
|
|
}
|
|
} else if ((data->type == QSEECOM_LISTENER_SERVICE) &&
|
|
(lstnr_resp->ifd_data[i].fd > 0)) {
|
|
if (qteec) {
|
|
- if (lstnr_resp->ifd_data[i].cmd_buf_offset >
|
|
- lstnr_resp->resp_len - TWO * sizeof(uint32_t)) {
|
|
- pr_err("Invalid offset 0x%x\n",
|
|
+ if ((lstnr_resp->resp_len < TWO * sizeof(uint32_t)) ||
|
|
+ (lstnr_resp->ifd_data[i].cmd_buf_offset >
|
|
+ lstnr_resp->resp_len - TWO*sizeof(uint32_t))) {
|
|
+ pr_err("Invalid offset (QTEEC resp len) 0x%x\n",
|
|
lstnr_resp->ifd_data[i].cmd_buf_offset);
|
|
- return ++ret;
|
|
+ return -EINVAL;
|
|
}
|
|
} else {
|
|
- if (lstnr_resp->ifd_data[i].cmd_buf_offset >
|
|
- lstnr_resp->resp_len - sizeof(uint32_t)) {
|
|
- pr_err("Invalid offset 0x%x\n",
|
|
+ if ((lstnr_resp->resp_len < sizeof(uint32_t)) ||
|
|
+ (lstnr_resp->ifd_data[i].cmd_buf_offset >
|
|
+ lstnr_resp->resp_len - sizeof(uint32_t))) {
|
|
+ pr_err("Invalid offset (lstnr resp len) 0x%x\n",
|
|
lstnr_resp->ifd_data[i].cmd_buf_offset);
|
|
- return ++ret;
|
|
+ return -EINVAL;
|
|
}
|
|
}
|
|
}
|
|
- return ret;
|
|
+ return 0;
|
|
}
|
|
|
|
+#define SG_ENTRY_SZ sizeof(struct qseecom_sg_entry)
|
|
static int __qseecom_update_cmd_buf(void *msg, bool cleanup,
|
|
struct qseecom_dev_handle *data, bool qteec)
|
|
{
|
|
@@ -2095,7 +2128,7 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup,
|
|
uint32_t *update;
|
|
update = (uint32_t *) field;
|
|
|
|
- if (boundary_checks_offset(req, lstnr_resp, data,
|
|
+ if (__boundary_checks_offset(req, lstnr_resp, data,
|
|
qteec, i))
|
|
goto err;
|
|
if (cleanup)
|
|
@@ -2112,22 +2145,25 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup,
|
|
|
|
if ((data->type != QSEECOM_LISTENER_SERVICE) &&
|
|
(req->ifd_data[i].fd > 0)) {
|
|
- if (req->ifd_data[i].cmd_buf_offset >
|
|
- req->cmd_req_len -
|
|
- sizeof(struct qseecom_sg_entry)) {
|
|
+
|
|
+ if ((req->cmd_req_len <
|
|
+ SG_ENTRY_SZ * sg_ptr->nents) ||
|
|
+ (req->ifd_data[i].cmd_buf_offset >
|
|
+ (req->cmd_req_len -
|
|
+ SG_ENTRY_SZ * sg_ptr->nents))) {
|
|
pr_err("Invalid offset = 0x%x\n",
|
|
- req->ifd_data[i].
|
|
- cmd_buf_offset);
|
|
+ req->ifd_data[i].cmd_buf_offset);
|
|
goto err;
|
|
}
|
|
+
|
|
} else if ((data->type == QSEECOM_LISTENER_SERVICE) &&
|
|
(lstnr_resp->ifd_data[i].fd > 0)) {
|
|
- if (lstnr_resp->ifd_data[i].cmd_buf_offset >
|
|
- lstnr_resp->resp_len -
|
|
- sizeof(struct qseecom_sg_entry)) {
|
|
- pr_err("Invalid offset = 0x%x\n",
|
|
- lstnr_resp->ifd_data[i].
|
|
- cmd_buf_offset);
|
|
+
|
|
+ if ((lstnr_resp->resp_len <
|
|
+ SG_ENTRY_SZ * sg_ptr->nents) ||
|
|
+ (lstnr_resp->ifd_data[i].cmd_buf_offset >
|
|
+ (lstnr_resp->resp_len -
|
|
+ SG_ENTRY_SZ * sg_ptr->nents))) {
|
|
goto err;
|
|
}
|
|
}
|
|
@@ -2179,37 +2215,14 @@ static int qseecom_send_modfd_cmd(struct qseecom_dev_handle *data,
|
|
return ret;
|
|
}
|
|
|
|
- if (req.cmd_req_buf == NULL || req.resp_buf == NULL) {
|
|
- pr_err("cmd buffer or response buffer is null\n");
|
|
- return -EINVAL;
|
|
- }
|
|
- if (((uintptr_t)req.cmd_req_buf <
|
|
- data->client.user_virt_sb_base) ||
|
|
- ((uintptr_t)req.cmd_req_buf >=
|
|
- (data->client.user_virt_sb_base + data->client.sb_length))) {
|
|
- pr_err("cmd buffer address not within shared bufffer\n");
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- if (((uintptr_t)req.resp_buf <
|
|
- data->client.user_virt_sb_base) ||
|
|
- ((uintptr_t)req.resp_buf >=
|
|
- (data->client.user_virt_sb_base + data->client.sb_length))) {
|
|
- pr_err("response buffer address not within shared bufffer\n");
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- if (req.cmd_req_len == 0 || req.cmd_req_len > data->client.sb_length ||
|
|
- req.resp_len > data->client.sb_length) {
|
|
- pr_err("cmd or response buffer length not valid\n");
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
send_cmd_req.cmd_req_buf = req.cmd_req_buf;
|
|
send_cmd_req.cmd_req_len = req.cmd_req_len;
|
|
send_cmd_req.resp_buf = req.resp_buf;
|
|
send_cmd_req.resp_len = req.resp_len;
|
|
|
|
+ if (__validate_send_cmd_inputs(data, &send_cmd_req))
|
|
+ return -EINVAL;
|
|
+
|
|
/* validate offsets */
|
|
for (i = 0; i < MAX_ION_FD; i++) {
|
|
if (req.ifd_data[i].cmd_buf_offset >= req.cmd_req_len) {
|
|
@@ -2897,6 +2910,9 @@ int qseecom_send_command(struct qseecom_handle *handle, void *send_buf,
|
|
req.cmd_req_buf = send_buf;
|
|
req.resp_buf = resp_buf;
|
|
|
|
+ if (__validate_send_cmd_inputs(data, &req))
|
|
+ return -EINVAL;
|
|
+
|
|
mutex_lock(&app_access_lock);
|
|
atomic_inc(&data->ioctl_count);
|
|
if (qseecom.support_bus_scaling) {
|
|
@@ -4111,6 +4127,19 @@ static int qseecom_save_partition_hash(void __user *argp)
|
|
static int __qseecom_qteec_validate_msg(struct qseecom_dev_handle *data,
|
|
struct qseecom_qteec_req *req)
|
|
{
|
|
+
|
|
+ if (req->req_len > UINT_MAX - req->resp_len) {
|
|
+ pr_err("Integer overflow detected in req_len & rsp_len\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (req->req_len + req->resp_len > data->client.sb_length) {
|
|
+ pr_debug("Not enough memory to fit cmd_buf.\n");
|
|
+ pr_debug("resp_buf. Required: %u, Available: %zu\n",
|
|
+ (req->req_len + req->resp_len), data->client.sb_length);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
if (req->req_ptr == NULL || req->resp_ptr == NULL) {
|
|
pr_err("cmd buffer or response buffer is null\n");
|
|
return -EINVAL;
|
|
@@ -4131,15 +4160,33 @@ static int __qseecom_qteec_validate_msg(struct qseecom_dev_handle *data,
|
|
return -EINVAL;
|
|
}
|
|
|
|
- if ((req->req_len == 0) || (req->resp_len == 0) ||
|
|
- req->req_len > data->client.sb_length ||
|
|
- req->resp_len > data->client.sb_length) {
|
|
+ if ((req->req_len == 0) || (req->resp_len == 0)) {
|
|
pr_err("cmd buf lengtgh/response buf length not valid\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
- if (req->req_len > UINT_MAX - req->resp_len) {
|
|
- pr_err("Integer overflow detected in req_len/rsp_len, exit\n");
|
|
+ if ((uintptr_t)req->req_ptr > (ULONG_MAX - req->req_len)) {
|
|
+ pr_err("Integer overflow in req_len & req_ptr\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if ((uintptr_t)req->resp_ptr > (ULONG_MAX - req->resp_len)) {
|
|
+ pr_err("Integer overflow in resp_len & resp_ptr\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (data->client.user_virt_sb_base >
|
|
+ (ULONG_MAX - data->client.sb_length)) {
|
|
+ pr_err("Integer overflow in user_virt_sb_base & sb_length\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if ((((uintptr_t)req->req_ptr + req->req_len) >
|
|
+ ((uintptr_t)data->client.user_virt_sb_base +
|
|
+ data->client.sb_length)) ||
|
|
+ (((uintptr_t)req->resp_ptr + req->resp_len) >
|
|
+ ((uintptr_t)data->client.user_virt_sb_base +
|
|
+ data->client.sb_length))) {
|
|
+ pr_err("cmd buf or resp buf is out of shared buffer region\n");
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
--
|
|
cgit v1.1
|
|
|