qseecom: improve input validatation for qseecom_send_service_cmd

Make change to improve input validation on request and response
buffers' address and length for qseecom_send_service_cmd.

Change-Id: I047e3264333d767541e43b7dadd1727232fd48ef
Signed-off-by: Zhen Kong <zkong@codeaurora.org>
This commit is contained in:
Zhen Kong 2017-01-11 12:12:31 -08:00
parent 0a8e939a4e
commit b108c651ca

View file

@ -1,6 +1,6 @@
/*Qualcomm Secure Execution Environment Communicator (QSEECOM) driver
*
* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -2634,11 +2634,6 @@ int __qseecom_process_rpmb_svc_cmd(struct qseecom_dev_handle *data_ptr,
return -EINVAL;
}
if ((!req_ptr->cmd_req_buf) || (!req_ptr->resp_buf)) {
pr_err("Invalid req/resp buffer, exiting\n");
return -EINVAL;
}
/* Clients need to ensure req_buf is at base offset of shared buffer */
if ((uintptr_t)req_ptr->cmd_req_buf !=
data_ptr->client.user_virt_sb_base) {
@ -2646,15 +2641,11 @@ int __qseecom_process_rpmb_svc_cmd(struct qseecom_dev_handle *data_ptr,
return -EINVAL;
}
if (((uintptr_t)req_ptr->resp_buf <
data_ptr->client.user_virt_sb_base) ||
((uintptr_t)req_ptr->resp_buf >=
(data_ptr->client.user_virt_sb_base +
data_ptr->client.sb_length))){
pr_err("response buffer address not within shared bufffer\n");
if (data_ptr->client.sb_length <
sizeof(struct qseecom_rpmb_provision_key)) {
pr_err("shared buffer is too small to hold key type\n");
return -EINVAL;
}
req_buf = data_ptr->client.sb_virt;
send_svc_ireq_ptr->qsee_cmd_id = req_ptr->cmd_id;
@ -2681,36 +2672,6 @@ int __qseecom_process_fsm_key_svc_cmd(struct qseecom_dev_handle *data_ptr,
return -EINVAL;
}
if (((uintptr_t)req_ptr->cmd_req_buf <
data_ptr->client.user_virt_sb_base) ||
((uintptr_t)req_ptr->cmd_req_buf >=
(data_ptr->client.user_virt_sb_base +
data_ptr->client.sb_length))) {
pr_err("cmd buffer address not within shared bufffer\n");
return -EINVAL;
}
if (((uintptr_t)req_ptr->resp_buf <
data_ptr->client.user_virt_sb_base) ||
((uintptr_t)req_ptr->resp_buf >=
(data_ptr->client.user_virt_sb_base +
data_ptr->client.sb_length))){
pr_err("response buffer address not within shared bufffer\n");
return -EINVAL;
}
if ((req_ptr->cmd_req_len == 0) || (req_ptr->resp_len == 0) ||
req_ptr->cmd_req_len > data_ptr->client.sb_length ||
req_ptr->resp_len > data_ptr->client.sb_length) {
pr_err("cmd buffer length or response buffer length not valid\n");
return -EINVAL;
}
if (req_ptr->cmd_req_len > UINT_MAX - req_ptr->resp_len) {
pr_err("Integer overflow detected in req_len & rsp_len, exiting now\n");
return -EINVAL;
}
reqd_len_sb_in = req_ptr->cmd_req_len + req_ptr->resp_len;
if (reqd_len_sb_in > data_ptr->client.sb_length) {
pr_err("Not enough memory to fit cmd_buf and resp_buf. ");
@ -2732,28 +2693,11 @@ int __qseecom_process_fsm_key_svc_cmd(struct qseecom_dev_handle *data_ptr,
return ret;
}
static int qseecom_send_service_cmd(struct qseecom_dev_handle *data,
void __user *argp)
static int __validate_send_service_cmd_inputs(struct qseecom_dev_handle *data,
struct qseecom_send_svc_cmd_req *req)
{
int ret = 0;
struct qseecom_client_send_service_ireq send_svc_ireq;
struct qseecom_client_send_fsm_key_req send_fsm_key_svc_ireq;
struct qseecom_command_scm_resp resp;
struct qseecom_send_svc_cmd_req req;
void *send_req_ptr;
size_t req_buf_size;
/*struct qseecom_command_scm_resp resp;*/
if (copy_from_user(&req,
(void __user *)argp,
sizeof(req))) {
pr_err("copy_from_user failed\n");
return -EFAULT;
}
if ((req.resp_buf == NULL) || (req.cmd_req_buf == NULL)) {
pr_err("cmd buffer or response buffer is null\n");
if (!req || !req->resp_buf || !req->cmd_req_buf) {
pr_err("req or cmd buffer or response buffer is null\n");
return -EINVAL;
}
@ -2777,6 +2721,86 @@ static int qseecom_send_service_cmd(struct qseecom_dev_handle *data,
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->resp_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\n");
return -EINVAL;
}
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_service_cmd(struct qseecom_dev_handle *data,
void __user *argp)
{
int ret = 0;
struct qseecom_client_send_service_ireq send_svc_ireq;
struct qseecom_client_send_fsm_key_req send_fsm_key_svc_ireq;
struct qseecom_command_scm_resp resp;
struct qseecom_send_svc_cmd_req req;
void *send_req_ptr;
size_t req_buf_size;
/*struct qseecom_command_scm_resp resp;*/
if (copy_from_user(&req,
(void __user *)argp,
sizeof(req))) {
pr_err("copy_from_user failed\n");
return -EFAULT;
}
if (__validate_send_service_cmd_inputs(data, &req))
return -EINVAL;
data->type = QSEECOM_SECURE_SERVICE;
switch (req.cmd_id) {