diff options
author | Mekala Natarajan <mekalan@codeaurora.org> | 2015-01-27 16:26:51 -0800 |
---|---|---|
committer | Pat Tjin <pattjin@google.com> | 2015-01-28 22:02:30 +0000 |
commit | eec2459384835d85318caddbd8245876afc1933b (patch) | |
tree | db61dc25cf1468dfab49371f8398386a1be126d8 | |
parent | d37cf21b92d65aa2961b5b059e23551951c93737 (diff) |
qseecom: Add checks for send_cmd inputsandroid-5.1.0_r0.7
Improve user input validation across send cmd APIs. Add new
API __validate_send_cmd_inputs() to validate all user provided
inputs.
Bug: 19136582
Change-Id: Ibbb0c0e7e5483f653bd59b927562b63c1e43c365
Signed-off-by: Mona Hossain <mhossain@codeaurora.org>
Signed-off-by: Mekala Natarajan <mekalan@codeaurora.org>
-rw-r--r-- | drivers/misc/qseecom.c | 163 |
1 files changed, 92 insertions, 71 deletions
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 4399140e227f..6caec4614d06 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -615,7 +615,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; } mutex_lock(&qsee_bw_mutex); ret = __qseecom_register_bus_bandwidth_needs(data, req_mode); @@ -1343,19 +1343,15 @@ 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 (req->cmd_req_buf == NULL || req->resp_buf == NULL) { + if (!data || !data->client.ihandle) { + pr_err("Client or client handle is not initialized\n"); + return -EINVAL; + } + 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; } @@ -1365,36 +1361,68 @@ static int __qseecom_send_cmd(struct qseecom_dev_handle *data, pr_err("cmd buffer address not within shared bufffer\n"); return -EINVAL; } - - - if (((uint32_t)req->resp_buf < data->client.user_virt_sb_base) || - ((uint32_t)req->resp_buf >= (data->client.user_virt_sb_base + - data->client.sb_length))){ + 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->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) { - pr_debug("Not enough memory to fit cmd_buf and " - "resp_buf. Required: %u, Available: %u\n", - 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", + (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, @@ -1468,6 +1496,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) @@ -1476,30 +1508,33 @@ 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 *cmd_req, +int __boundary_checks_offset(struct qseecom_send_modfd_cmd_req *cmd_req, struct qseecom_send_modfd_listener_resp *lstnr_resp, struct qseecom_dev_handle *data, bool listener_svc, int i) { - int ret = 0; if ((!listener_svc) && (cmd_req->ifd_data[i].fd > 0)) { - if (cmd_req->ifd_data[i].cmd_buf_offset > - cmd_req->cmd_req_len - sizeof(uint32_t)) { + if ((cmd_req->cmd_req_len < sizeof(uint32_t)) || + (cmd_req->ifd_data[i].cmd_buf_offset > + cmd_req->cmd_req_len - sizeof(uint32_t))) { pr_err("Invalid offset 0x%x\n", cmd_req->ifd_data[i].cmd_buf_offset); - return ++ret; + return -EINVAL; } } else if ((listener_svc) && (lstnr_resp->ifd_data[i].fd > 0)) { - if (lstnr_resp->ifd_data[i].cmd_buf_offset > - lstnr_resp->resp_len - sizeof(uint32_t)) { + 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 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 listener_svc) @@ -1574,7 +1609,7 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup, uint32_t *update; update = (uint32_t *) field; - if (boundary_checks_offset(cmd_req, lstnr_resp, data, + if (__boundary_checks_offset(cmd_req, lstnr_resp, data, listener_svc, i)) goto err; if (cleanup) @@ -1588,9 +1623,11 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup, int j = 0; if ((!listener_svc) && (cmd_req->ifd_data[i].fd > 0)) { - if (cmd_req->ifd_data[i].cmd_buf_offset > - cmd_req->cmd_req_len - - sizeof(struct qseecom_sg_entry)) { + if ((cmd_req->cmd_req_len < + SG_ENTRY_SZ * sg_ptr->nents) || + (cmd_req->ifd_data[i].cmd_buf_offset > + (cmd_req->cmd_req_len - + SG_ENTRY_SZ * sg_ptr->nents))) { pr_err("Invalid offset = 0x%x\n", cmd_req->ifd_data[i]. cmd_buf_offset); @@ -1598,9 +1635,11 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup, } } else if ((listener_svc) && (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)) { + 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))) { pr_err("Invalid offset = 0x%x\n", lstnr_resp->ifd_data[i]. cmd_buf_offset); @@ -1655,35 +1694,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 (((uint32_t)req.cmd_req_buf < data->client.user_virt_sb_base) || - ((uint32_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 (((uint32_t)req.resp_buf < data->client.user_virt_sb_base) || - ((uint32_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) { @@ -2331,6 +2349,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) { |