From e909d95e6bded328e388d5b8d123297bbbb70728 Mon Sep 17 00:00:00 2001 From: Mona Hossain 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 --- 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