DivestOS/Patches/Linux_CVEs/CVE-2014-4322/ANY/0002.patch

384 lines
12 KiB
Diff
Raw Normal View History

2017-11-07 17:32:46 -05:00
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