diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c index 6af6e23c797f..26ce5f24112c 100644 --- a/drivers/gpu/msm/adreno_drawctxt.c +++ b/drivers/gpu/msm/adreno_drawctxt.c @@ -1,4 +1,5 @@ /* Copyright (c) 2002,2007-2018,2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. 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 @@ -629,8 +630,6 @@ int adreno_drawctxt_switch(struct adreno_device *adreno_dev, if (drawctxt != NULL && kgsl_context_detached(&drawctxt->base)) return -ENOENT; - trace_adreno_drawctxt_switch(rb, drawctxt); - /* Get a refcount to the new instance */ if (drawctxt) { if (!_kgsl_context_get(&drawctxt->base)) @@ -643,7 +642,7 @@ int adreno_drawctxt_switch(struct adreno_device *adreno_dev, } ret = adreno_ringbuffer_set_pt_ctx(rb, new_pt, drawctxt, flags); if (ret) - return ret; + goto err; if (rb->drawctxt_active) { /* Wait for the timestamp to expire */ @@ -654,6 +653,13 @@ int adreno_drawctxt_switch(struct adreno_device *adreno_dev, } } + trace_adreno_drawctxt_switch(rb, drawctxt); + rb->drawctxt_active = drawctxt; + return 0; +err: + if (drawctxt) + kgsl_context_put(&drawctxt->base); + return ret; } diff --git a/drivers/gpu/msm/adreno_iommu.c b/drivers/gpu/msm/adreno_iommu.c index 032a19c1d37a..d5a37fcab45c 100644 --- a/drivers/gpu/msm/adreno_iommu.c +++ b/drivers/gpu/msm/adreno_iommu.c @@ -1,5 +1,5 @@ /* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022,2024 Qualcomm Innovation Center, Inc. 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 @@ -964,5 +964,17 @@ int adreno_iommu_set_pt_ctx(struct adreno_ringbuffer *rb, else result = _set_ctxt_gpu(rb, drawctxt); + /* + * In case ctxt switch fails, revert the pagetable back to the + * original. Not reverting the pagetable will lead to incorrect + * hardware state in the ringbuffer. + */ + if (result && (new_pt != cur_pt)) { + if (cpu_path) + result = _set_pagetable_cpu(rb, cur_pt); + else + result = _set_pagetable_gpu(rb, cur_pt); + } + return result; } diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c index 5110386df95e..9c3afb3b90cc 100644 --- a/drivers/gpu/msm/kgsl_mmu.c +++ b/drivers/gpu/msm/kgsl_mmu.c @@ -1,5 +1,5 @@ /* Copyright (c) 2002,2007-2017,2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. 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 diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index c870fa955024..42d221874a13 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -353,7 +353,7 @@ struct qseecom_client_handle { struct qseecom_listener_handle { u32 id; - bool unregister_pending; + bool register_pending; bool release_called; }; @@ -1233,6 +1233,11 @@ static int qseecom_register_listener(struct qseecom_dev_handle *data, struct qseecom_registered_listener_list *new_entry; struct qseecom_registered_listener_list *ptr_svc; + if (data->listener.register_pending) { + pr_err("Already a listner registration is in process on this FD\n"); + return -EINVAL; + } + ret = copy_from_user(&rcvd_lstnr, argp, sizeof(rcvd_lstnr)); if (ret) { pr_err("copy_from_user failed\n"); @@ -1242,6 +1247,13 @@ static int qseecom_register_listener(struct qseecom_dev_handle *data, rcvd_lstnr.sb_size)) return -EFAULT; + ptr_svc = __qseecom_find_svc(data->listener.id); + if (ptr_svc) { + pr_err("Already a listener registered on this data: lid=%d\n", + data->listener.id); + return -EINVAL; + } + ptr_svc = __qseecom_find_svc(rcvd_lstnr.listener_id); if (ptr_svc) { if (ptr_svc->unregister_pending == false) { @@ -1276,12 +1288,15 @@ static int qseecom_register_listener(struct qseecom_dev_handle *data, new_entry->svc.listener_id = rcvd_lstnr.listener_id; new_entry->sb_length = rcvd_lstnr.sb_size; new_entry->user_virt_sb_base = rcvd_lstnr.virt_sb_base; + data->listener.register_pending = true; if (__qseecom_set_sb_memory(new_entry, data, &rcvd_lstnr)) { pr_err("qseecom_set_sb_memory failed for listener %d, size %d\n", rcvd_lstnr.listener_id, rcvd_lstnr.sb_size); kzfree(new_entry); + data->listener.register_pending = false; return -ENOMEM; } + data->listener.register_pending = false; init_waitqueue_head(&new_entry->rcv_req_wq); init_waitqueue_head(&new_entry->listener_block_app_wq); diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index ef576a527787..59b60c93db64 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -85,6 +85,17 @@ /* Max number of processors/hosts in a system */ #define SMEM_HOST_COUNT 9 +/* Entry range check + * ptr >= start : Checks if ptr is greater than the start of access region + * ptr + size >= ptr: Check for integer overflow (On 32bit system where ptr + * and size are 32bits, ptr + size can wrap around to be a small integer) + * ptr + size <= end: Checks if ptr+size is less than the end of access region + */ +#define IN_PARTITION_RANGE(ptr, size, start, end) \ + (((void *)(ptr) >= (void *)(start)) && \ + (((void *)(ptr) + (size)) >= (void *)(ptr)) && \ + (((void *)(ptr) + (size)) <= (void *)(end))) + /** * struct smem_proc_comm - proc_comm communication struct (legacy) * @command: current command to be executed @@ -301,6 +312,7 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem, size_t size) { struct smem_private_entry *hdr, *end; + struct smem_private_entry *next_hdr; struct smem_partition_header *phdr; size_t alloc_size; void *cached; @@ -313,10 +325,11 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem, end = phdr_to_last_private_entry(phdr); cached = phdr_to_first_cached_entry(phdr); - if (WARN_ON((void *)end > p_end || (void *)cached > p_end)) + if (WARN_ON(!IN_PARTITION_RANGE(end, 0, phdr, cached) || + cached > p_end)) return -EINVAL; - while (hdr < end) { + while ((hdr < end) && ((hdr + 1) < end)) { if (hdr->canary != SMEM_PRIVATE_CANARY) { dev_err(smem->dev, "Found invalid canary in host %d:%d partition\n", @@ -327,9 +340,15 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem, if (le16_to_cpu(hdr->item) == item) return -EEXIST; - hdr = private_entry_next(hdr); + next_hdr = private_entry_next(hdr); + + if (WARN_ON(next_hdr <= hdr)) + return -EINVAL; + + hdr = next_hdr; } - if (WARN_ON((void *)hdr > p_end)) + + if (WARN_ON((void *)hdr > (void *)end)) return -EINVAL; /* Check that we don't grow into the cached region */ @@ -484,7 +503,9 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, { struct smem_partition_header *phdr; struct smem_private_entry *e, *end; + struct smem_private_entry *next_e; void *item_ptr, *p_end; + size_t entry_size = 0; u32 partition_size; u32 padding_data; u32 e_size; @@ -499,7 +520,7 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, if (WARN_ON((void *)end > p_end)) return ERR_PTR(-EINVAL); - while (e < end) { + while ((e < end) && ((e + 1) < end)) { if (e->canary != SMEM_PRIVATE_CANARY) { dev_err(smem->dev, "Found invalid canary in host %d:%d partition\n", @@ -508,25 +529,31 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, } if (le16_to_cpu(e->item) == item) { - if (size != NULL) { - e_size = le32_to_cpu(e->size); - padding_data = le16_to_cpu(e->padding_data); + e_size = le32_to_cpu(e->size); + padding_data = le16_to_cpu(e->padding_data); - if (e_size < partition_size - && padding_data < e_size) - *size = e_size - padding_data; - else - return ERR_PTR(-EINVAL); - } + if (e_size < partition_size && padding_data < e_size) + entry_size = e_size - padding_data; + else + return ERR_PTR(-EINVAL); item_ptr = entry_to_item(e); - if (WARN_ON(item_ptr > p_end)) + + if (WARN_ON(!IN_PARTITION_RANGE(item_ptr, entry_size, + e, end))) return ERR_PTR(-EINVAL); + if (size != NULL) + *size = entry_size; + return item_ptr; } - e = private_entry_next(e); + next_e = private_entry_next(e); + if (WARN_ON(next_e <= e)) + return ERR_PTR(-EINVAL); + + e = next_e; } if (WARN_ON((void *)e > p_end)) return ERR_PTR(-EINVAL);