From c108feca0a096f8db25d1b69130035025e9e936d Mon Sep 17 00:00:00 2001 From: Shao-Fu Chen Date: Fri, 18 Sep 2026 18:10:46 +0800 Subject: [PATCH 1/2] optee: register TEE devices only once fully initialized optee_probe() called tee_device_register() on both the client and the supplicant device before the rest of struct optee had been initialized. tee_device_register() calls cdev_device_add(), which does two things at once: it creates /dev/tee0 and /dev/teepriv0, and it links the device into the tee class so that class_find_device() can find it. From that moment on the device is reachable both from user space via tee_open() and from kernel space via tee_client_open_context(). The only gate in teedev_open() is tee_device_get(), which merely checks that teedev->desc is non-NULL, which was already set by tee_device_alloc(). Therefore, there is effectively no gate at all. A context opened in that window can run against a struct optee where - optee->call_queue.mutex is not initialized by optee_cq_init() - optee->supp mutex and completions is not initialized by optee_supp_init() - optee->rpmb_dev_mutex is not initialized yet, - the message argument cache is not initialized by optee_shm_arg_cache_init() - optee->ctx is still NULL Any open session or invoke during this window takes uninitialized mutexes and dereferences a NULL pointer. Fix it by moving both tee_device_register() calls down to the point where all of struct optee is set up. Fixes: 4fb0a5eb364d ("tee: add OP-TEE driver") Cc: stable@vger.kernel.org Signed-off-by: Shao-Fu Chen [JW: adding fixes and cc] Signed-off-by: Jens Wiklander --- drivers/tee/optee/ffa_abi.c | 16 ++++++++-------- drivers/tee/optee/smc_abi.c | 17 ++++++++--------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c index 633715b98625..d3cc7c5fc1e9 100644 --- a/drivers/tee/optee/ffa_abi.c +++ b/drivers/tee/optee/ffa_abi.c @@ -1123,14 +1123,6 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) optee_set_dev_group(optee); - rc = tee_device_register(optee->teedev); - if (rc) - goto err_unreg_supp_teedev; - - rc = tee_device_register(optee->supp_teedev); - if (rc) - goto err_unreg_supp_teedev; - rc = rhashtable_init(&optee->ffa.global_ids, &shm_rhash_params); if (rc) goto err_unreg_supp_teedev; @@ -1159,6 +1151,14 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) if (optee_ffa_protmem_pool_init(optee, sec_caps)) pr_info("Protected memory service not available\n"); + rc = tee_device_register(optee->teedev); + if (rc) + goto err_unregister_devices; + + rc = tee_device_register(optee->supp_teedev); + if (rc) + goto err_unregister_devices; + rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); if (rc) goto err_unregister_devices; diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c index b8a2bdac3208..51624443359b 100644 --- a/drivers/tee/optee/smc_abi.c +++ b/drivers/tee/optee/smc_abi.c @@ -1849,14 +1849,6 @@ static int optee_probe(struct platform_device *pdev) optee_set_dev_group(optee); - rc = tee_device_register(optee->teedev); - if (rc) - goto err_unreg_supp_teedev; - - rc = tee_device_register(optee->supp_teedev); - if (rc) - goto err_unreg_supp_teedev; - optee_cq_init(&optee->call_queue, thread_count); optee_supp_init(&optee->supp); optee->smc.memremaped_shm = memremaped_shm; @@ -1916,6 +1908,14 @@ static int optee_probe(struct platform_device *pdev) if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) pr_info("dynamic shared memory is enabled\n"); + rc = tee_device_register(optee->teedev); + if (rc) + goto err_disable_shm_cache; + + rc = tee_device_register(optee->supp_teedev); + if (rc) + goto err_disable_shm_cache; + rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); if (rc) goto err_disable_shm_cache; @@ -1942,7 +1942,6 @@ static int optee_probe(struct platform_device *pdev) optee_shm_arg_cache_uninit(optee); optee_supp_uninit(&optee->supp); mutex_destroy(&optee->call_queue.mutex); -err_unreg_supp_teedev: tee_device_unregister(optee->supp_teedev); err_unreg_teedev: tee_device_unregister(optee->teedev); From 3c0d2f1f431c51a11253f28763be723ac782fd7a Mon Sep 17 00:00:00 2001 From: Mahantesh Salimath Date: Thu, 10 Sep 2026 16:11:24 +0000 Subject: [PATCH 2/2] tee: optee: ffa: support shared memory offsets on large-page kernels OP-TEE FF-A memory objects use 4 KiB pages, while the kernel page size may be larger. Consequently, tee_shm->offset can be greater than or equal to FFA_PAGE_SIZE, but OP-TEE rejects such a value in internal_offs. Do not encode the excess page offset in offs_low/offs_high. Those fields describe the logical memref offset and are copied back into tee_param->shm_offs on return. Folding the page offset into them breaks parameter round trips when a memref is reused. They are also ignored by the OPTEE_RPC_CMD_SHM_ALLOC response path, which uses only global_id and internal_offs to construct the shared-memory mobj. Instead, start the FF-A descriptor at the 4 KiB page containing the shared buffer, the same approach as optee_fill_pages_list() in the SMC ABI. Store the remaining in-page offset in internal_offs and preserve shm_offs in offs_low/offs_high. This keeps internal_offs within the FF-A page size, maps RPC allocations at the correct address, and preserves normal memref offsets across repeated invocations. Tested on ARMv8-A with 64 KiB PAGE_SIZE. OP-TEE OS ran as a secure partition under Hafnium (SPMC) over FF-A. Verified registered shared memory with tee_shm->offset >= 4 KiB, memref reuse on the same TEEC_Operation, and RPC OPTEE_RPC_CMD_SHM_ALLOC (xtest regression 6007-6009). optee_hello_world, optee_aes, and xtest regression 1005, 1007, 1008, 4001-4003 and 6001-6003 also passed. Fixes: 4615e5a34b95 ("optee: add FF-A support") Acked-by: Liming Sun Acked-by: James Hurley Acked-by: Dave Thompson Signed-off-by: Mahantesh Salimath Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/optee/ffa_abi.c | 22 ++++++++++++++++++---- drivers/tee/optee/optee_msg.h | 4 ++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c index d3cc7c5fc1e9..d027fe7ce86f 100644 --- a/drivers/tee/optee/ffa_abi.c +++ b/drivers/tee/optee/ffa_abi.c @@ -198,7 +198,8 @@ static int to_msg_param_ffa_mem(struct optee_msg_param *mp, if (shm) { u64 shm_offs = p->u.memref.shm_offs; - mp->u.fmem.internal_offs = shm->offset; + mp->u.fmem.internal_offs = tee_shm_get_page_offset(shm) & + (FFA_PAGE_SIZE - 1); mp->u.fmem.offs_low = shm_offs; mp->u.fmem.offs_high = shm_offs >> 32; @@ -284,14 +285,26 @@ static int optee_ffa_shm_register(struct tee_context *ctx, struct tee_shm *shm, .nattrs = 1, }; struct sg_table sgt; + size_t ffa_offs; int rc; + if (!num_pages) + return -EINVAL; + rc = optee_check_mem_type(start, num_pages); if (rc) return rc; - rc = sg_alloc_table_from_pages(&sgt, pages, num_pages, 0, - num_pages * PAGE_SIZE, GFP_KERNEL); + /* + * Start the FF-A descriptor at the 4 KiB page containing the shared + * buffer, skipping unused leading 4 KiB pages when PAGE_SIZE is + * larger. Same approach as optee_fill_pages_list() in the SMC ABI. + * This leaves only the offset into that 4 KiB page for internal_offs. + */ + ffa_offs = round_down(tee_shm_get_page_offset(shm), FFA_PAGE_SIZE); + rc = sg_alloc_table_from_pages(&sgt, pages, num_pages, ffa_offs, + num_pages * PAGE_SIZE - ffa_offs, + GFP_KERNEL); if (rc) return rc; args.sg = sgt.sgl; @@ -458,7 +471,8 @@ static void handle_ffa_rpc_func_cmd_shm_alloc(struct tee_context *ctx, .attr = OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT, .u.fmem.size = tee_shm_get_size(shm), .u.fmem.global_id = shm->sec_world_id, - .u.fmem.internal_offs = shm->offset, + .u.fmem.internal_offs = tee_shm_get_page_offset(shm) & + (FFA_PAGE_SIZE - 1), }; arg->ret = TEEC_SUCCESS; diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h index 7d9b12e71c03..6c3043f8da33 100644 --- a/drivers/tee/optee/optee_msg.h +++ b/drivers/tee/optee/optee_msg.h @@ -136,8 +136,8 @@ struct optee_msg_param_rmem { * struct optee_msg_param_fmem - FF-A memory reference parameter * @offs_low: lower bits of offset into shared memory reference * @offs_high: higher bits of offset into shared memory reference - * @internal_offs: internal offset into the first page of shared memory - * reference + * @internal_offs: offset into the first 4 KiB page of the FF-A shared + * memory region * @size: size of the buffer * @global_id: global identifier of the shared memory */