From e80b88323f9ff0bb0e545f209eec08ec56fca816 Mon Sep 17 00:00:00 2001 From: Zhen Kong <zkong@codeaurora.org> Date: Mon, 18 Jul 2016 13:20:18 -0700 Subject: qseecom: validate the inputs of __qseecom_send_modfd_resp The resp_len and resp_buf_ptr of qseecom_send_modfd_listener_resp are not checked, then an userspace application that manipulates resp_len can corrupt the kernel memory. Thus make changes to validate these parameters. CRs-fixed: 1036418 Change-Id: Id43ec6b55b332d0dac09a9abb998a410f49b44f7 Signed-off-by: Zhen Kong <zkong@codeaurora.org> --- drivers/misc/qseecom.c | 78 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 17 deletions(-) diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index b175965c..1168181 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -3065,41 +3065,80 @@ static int qseecom_send_resp(void) } -static int qseecom_send_modfd_resp(struct qseecom_dev_handle *data, - void __user *argp) +static int __validate_send_modfd_resp_inputs(struct qseecom_dev_handle *data, + struct qseecom_send_modfd_listener_resp *resp, + struct qseecom_registered_listener_list *this_lstnr) { - struct qseecom_send_modfd_listener_resp resp; int i; - struct qseecom_registered_listener_list *this_lstnr = NULL; - if (copy_from_user(&resp, argp, sizeof(resp))) { - pr_err("copy_from_user failed"); + if (!data || !resp || !this_lstnr) { + pr_err("listener handle or resp msg is null\n"); return -EINVAL; } - this_lstnr = __qseecom_find_svc(data->listener.id); - if (this_lstnr == NULL) + + if (resp->resp_buf_ptr == NULL) { + pr_err("resp buffer is null\n"); + return -EINVAL; + } + /* validate resp buf length */ + if ((resp->resp_len == 0) || + (resp->resp_len > this_lstnr->sb_length)) { + pr_err("resp buf length %d not valid\n", resp->resp_len); return -EINVAL; + } - if (resp.resp_buf_ptr == NULL) { - pr_err("Invalid resp_buf_ptr\n"); + if ((uintptr_t)resp->resp_buf_ptr > (ULONG_MAX - resp->resp_len)) { + pr_err("Integer overflow in resp_len & resp_buf\n"); + return -EINVAL; + } + if ((uintptr_t)this_lstnr->user_virt_sb_base > + (ULONG_MAX - this_lstnr->sb_length)) { + pr_err("Integer overflow in user_virt_sb_base & sb_length\n"); return -EINVAL; } + /* validate resp buf */ + if (((uintptr_t)resp->resp_buf_ptr < + (uintptr_t)this_lstnr->user_virt_sb_base) || + ((uintptr_t)resp->resp_buf_ptr >= + ((uintptr_t)this_lstnr->user_virt_sb_base + + this_lstnr->sb_length)) || + (((uintptr_t)resp->resp_buf_ptr + resp->resp_len) > + ((uintptr_t)this_lstnr->user_virt_sb_base + + this_lstnr->sb_length))) { + pr_err("resp buf is out of shared buffer region\n"); + return -EINVAL; + } + /* validate offsets */ for (i = 0; i < MAX_ION_FD; i++) { - if (resp.ifd_data[i].cmd_buf_offset >= resp.resp_len) { + if (resp->ifd_data[i].cmd_buf_offset >= resp->resp_len) { pr_err("Invalid offset %d = 0x%x\n", - i, resp.ifd_data[i].cmd_buf_offset); + i, resp->ifd_data[i].cmd_buf_offset); return -EINVAL; } } - if ((resp.resp_buf_ptr < this_lstnr->user_virt_sb_base) || - ((uintptr_t)resp.resp_buf_ptr >= - ((uintptr_t)this_lstnr->user_virt_sb_base + - this_lstnr->sb_length))) { - pr_err("resp_buf_ptr address not within shared buffer\n"); + return 0; +} + +static int __qseecom_send_modfd_resp(struct qseecom_dev_handle *data, + void __user *argp, bool is_64bit_addr) +{ + struct qseecom_send_modfd_listener_resp resp; + struct qseecom_registered_listener_list *this_lstnr = NULL; + + if (copy_from_user(&resp, argp, sizeof(resp))) { + pr_err("copy_from_user failed"); return -EINVAL; } + + this_lstnr = __qseecom_find_svc(data->listener.id); + if (this_lstnr == NULL) + return -EINVAL; + + if (__validate_send_modfd_resp_inputs(data, &resp, this_lstnr)) + return -EINVAL; + resp.resp_buf_ptr = this_lstnr->sb_virt + (uintptr_t)(resp.resp_buf_ptr - this_lstnr->user_virt_sb_base); __qseecom_update_cmd_buf(&resp, false, data, true); @@ -3108,6 +3147,11 @@ static int qseecom_send_modfd_resp(struct qseecom_dev_handle *data, return 0; } +static int qseecom_send_modfd_resp(struct qseecom_dev_handle *data, + void __user *argp) +{ + return __qseecom_send_modfd_resp(data, argp, false); +} static int qseecom_get_qseos_version(struct qseecom_dev_handle *data, void __user *argp) -- cgit v1.1