From 5ab77bca4471e1223f07b72e434408cf151ddb8d Mon Sep 17 00:00:00 2001 From: Shivakumar Malke Date: Tue, 5 Nov 2024 17:44:05 +0530 Subject: [PATCH] msm: camera: isp: Copy the userdata in kernel to avoid TOCTOU Userspace is still able to access blob data after submitting to kmd and can alter it's contents. This change copy the information in kernel space and then perform relevant checks and actions if the data is unaltered. CRs-Fixed: 3923843 Change-Id: I9ec3c95a3e04770ab0b00bc07c8a0d133f779ed1 Signed-off-by: Pranav Sanwal Signed-off-by: Shivakumar Malke --- drivers/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c | 60 +++++++++++++++++---- drivers/cam_utils/cam_common_util.c | 35 ++++++++++++ drivers/cam_utils/cam_common_util.h | 19 +++++++ 3 files changed, 105 insertions(+), 9 deletions(-) diff --git a/drivers/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c b/drivers/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c index e54e240f0fb6..5a8a658a3c0b 100644 --- a/drivers/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c +++ b/drivers/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -5663,7 +5663,9 @@ static int cam_isp_packet_generic_blob_handler(void *user_data, break; case CAM_ISP_GENERIC_BLOB_TYPE_BW_CONFIG: { struct cam_isp_bw_config *bw_config; + struct cam_isp_bw_config *bw_config_u; struct cam_isp_prepare_hw_update_data *prepare_hw_data; + size_t bw_config_size; CAM_WARN_RATE_LIMIT_CUSTOM(CAM_PERF, 300, 1, "Deprecated Blob TYPE_BW_CONFIG"); @@ -5672,11 +5674,26 @@ static int cam_isp_packet_generic_blob_handler(void *user_data, return -EINVAL; } - bw_config = (struct cam_isp_bw_config *)blob_data; + bw_config_u = (struct cam_isp_bw_config *)blob_data; - if (bw_config->num_rdi > CAM_IFE_RDI_NUM_MAX) { - CAM_ERR(CAM_ISP, "Invalid num_rdi %u in bw config", + if (bw_config_u->num_rdi > CAM_IFE_RDI_NUM_MAX || !bw_config_u->num_rdi) { + CAM_ERR(CAM_ISP, "Invalid num_rdi %u in bw config, ctx_idx: %u", + bw_config_u->num_rdi, ife_mgr_ctx->ctx_index); + return -EINVAL; + } + + bw_config_size = sizeof(struct cam_isp_bw_config) + ((bw_config_u->num_rdi-1)* + sizeof(struct cam_isp_bw_vote)); + + rc = cam_common_mem_kdup((void **)&bw_config, bw_config_u, bw_config_size); + if (rc) { + CAM_ERR(CAM_ISP, "Alloc and copy request bw_config failed"); + return rc; + } + if (bw_config_u->num_rdi != bw_config->num_rdi) { + CAM_ERR(CAM_ISP, "num_rdi changed,userspace:%d, kernel:%d", bw_config_u->num_rdi, bw_config->num_rdi); + cam_common_mem_free(bw_config); return -EINVAL; } @@ -5689,6 +5706,7 @@ static int cam_isp_packet_generic_blob_handler(void *user_data, "Max size exceeded in bw config num_rdi:%u size per port:%lu", bw_config->num_rdi, sizeof(struct cam_isp_bw_vote)); + cam_common_mem_free(bw_config); return -EINVAL; } } @@ -5701,12 +5719,14 @@ static int cam_isp_packet_generic_blob_handler(void *user_data, blob_size, sizeof(struct cam_isp_bw_config) + (bw_config->num_rdi - 1) * sizeof(struct cam_isp_bw_vote)); + cam_common_mem_free(bw_config); return -EINVAL; } if (!prepare || !prepare->priv || (bw_config->usage_type >= CAM_IFE_HW_NUM_MAX)) { CAM_ERR(CAM_ISP, "Invalid inputs"); + cam_common_mem_free(bw_config); return -EINVAL; } @@ -5717,11 +5737,13 @@ static int cam_isp_packet_generic_blob_handler(void *user_data, bw_config, sizeof(prepare_hw_data->bw_config[0])); prepare_hw_data->bw_config_version = CAM_ISP_BW_CONFIG_V1; prepare_hw_data->bw_config_valid[bw_config->usage_type] = true; + cam_common_mem_free(bw_config); } break; case CAM_ISP_GENERIC_BLOB_TYPE_BW_CONFIG_V2: { size_t bw_config_size = 0; struct cam_isp_bw_config_v2 *bw_config; + struct cam_isp_bw_config_v2 *bw_config_u; struct cam_isp_prepare_hw_update_data *prepare_hw_data; if (blob_size < sizeof(struct cam_isp_bw_config_v2)) { @@ -5729,12 +5751,28 @@ static int cam_isp_packet_generic_blob_handler(void *user_data, return -EINVAL; } - bw_config = (struct cam_isp_bw_config_v2 *)blob_data; + bw_config_u = (struct cam_isp_bw_config_v2 *)blob_data; - if ((bw_config->num_paths > CAM_ISP_MAX_PER_PATH_VOTES) || - !bw_config->num_paths) { - CAM_ERR(CAM_ISP, "Invalid num paths %d", - bw_config->num_paths); + if (bw_config_u->num_paths > CAM_ISP_MAX_PER_PATH_VOTES || + !bw_config_u->num_paths) { + CAM_ERR(CAM_ISP, "Invalid num paths %d ctx_idx: %u", + bw_config_u->num_paths, ife_mgr_ctx->ctx_index); + return -EINVAL; + } + + bw_config_size = sizeof(struct cam_isp_bw_config_v2) + ((bw_config_u->num_paths-1)* + sizeof(struct cam_axi_per_path_bw_vote)); + + rc = cam_common_mem_kdup((void **)&bw_config, bw_config_u, bw_config_size); + if (rc) { + CAM_ERR(CAM_ISP, "Alloc and copy request bw_config failed"); + return rc; + } + + if (bw_config_u->num_paths != bw_config->num_paths) { + CAM_ERR(CAM_ISP, "num_paths changed,userspace:%d, kernel:%d", bw_config_u->num_paths, + bw_config->num_paths); + cam_common_mem_free(bw_config); return -EINVAL; } @@ -5749,6 +5787,7 @@ static int cam_isp_packet_generic_blob_handler(void *user_data, bw_config->num_paths - 1, sizeof( struct cam_axi_per_path_bw_vote)); + cam_common_mem_free(bw_config); return -EINVAL; } } @@ -5762,12 +5801,14 @@ static int cam_isp_packet_generic_blob_handler(void *user_data, blob_size, bw_config->num_paths, sizeof(struct cam_isp_bw_config_v2), sizeof(struct cam_axi_per_path_bw_vote)); + cam_common_mem_free(bw_config); return -EINVAL; } if (!prepare || !prepare->priv || (bw_config->usage_type >= CAM_IFE_HW_NUM_MAX)) { CAM_ERR(CAM_ISP, "Invalid inputs"); + cam_common_mem_free(bw_config); return -EINVAL; } @@ -5785,6 +5826,7 @@ static int cam_isp_packet_generic_blob_handler(void *user_data, prepare_hw_data->bw_config_version = CAM_ISP_BW_CONFIG_V2; prepare_hw_data->bw_config_valid[bw_config->usage_type] = true; + cam_common_mem_free(bw_config); } break; case CAM_ISP_GENERIC_BLOB_TYPE_UBWC_CONFIG: { diff --git a/drivers/cam_utils/cam_common_util.c b/drivers/cam_utils/cam_common_util.c index dbcb31d7bf0e..8267b9b0ada4 100644 --- a/drivers/cam_utils/cam_common_util.c +++ b/drivers/cam_utils/cam_common_util.c @@ -1,11 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include #include +#include #include "cam_common_util.h" #include "cam_debug_util.h" @@ -48,3 +50,36 @@ uint32_t cam_common_util_remove_duplicate_arr(int32_t *arr, uint32_t num) return wr_idx; } + +int cam_common_mem_kdup(void **dst, + void *src, size_t size) +{ + gfp_t flag = GFP_KERNEL; + + if (!src || !dst || !size) { + CAM_ERR(CAM_UTIL, "Invalid params src: %pK dst: %pK size: %u", + src, dst, size); + return -EINVAL; + } + + if (!in_task()) + flag = GFP_ATOMIC; + + *dst = kvzalloc(size, flag); + if (!*dst) { + CAM_ERR(CAM_UTIL, "Failed to allocate memory with size: %u", size); + return -ENOMEM; + } + + memcpy(*dst, src, size); + CAM_DBG(CAM_UTIL, "Allocate and copy memory with size: %u", size); + + return 0; +} +EXPORT_SYMBOL(cam_common_mem_kdup); + +void cam_common_mem_free(void *memory) +{ + kvfree(memory); +} +EXPORT_SYMBOL(cam_common_mem_free); diff --git a/drivers/cam_utils/cam_common_util.h b/drivers/cam_utils/cam_common_util.h index ebe75f6eb5e9..9c2e5300100a 100644 --- a/drivers/cam_utils/cam_common_util.h +++ b/drivers/cam_utils/cam_common_util.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _CAM_COMMON_UTIL_H_ @@ -64,4 +65,22 @@ int cam_common_util_get_string_index(const char **strings, uint32_t cam_common_util_remove_duplicate_arr(int32_t *array, uint32_t num); +/** + * @brief: Memory alloc and copy + * + * @dst: Address of destination address of memory + * @src: Source address of memory + * @size: Length of memory + * + * @return 0 if success in register non-zero if failes + */ +int cam_common_mem_kdup(void **dst, void *src, size_t size); + +/** + * @brief: Free the memory + * + * @memory: Address of memory + */ +void cam_common_mem_free(void *memory); + #endif /* _CAM_COMMON_UTIL_H_ */