diff options
| author | Jinke Han <jinkehan@didiglobal.com> | 2026-09-08 15:37:42 +0800 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2026-09-17 09:48:42 +0200 |
| commit | a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255 (patch) | |
| tree | 73fef824f9a846cf935869d0dbc5070ef6e2f741 /drivers/tee/qcomtee/qcomtee.h | |
| download | linux-stable-a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255.tar.gz linux-stable-a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255.zip | |
x86/kprobes: Fix crash when probing CS CALL instructionsgrafted
When using eBPF to probe CS CALL instructions within a function,
a crash can be triggered.
The eBPF tool probes offset 257 of the __hrtimer_run_queues()
function:
<__hrtimer_run_queues+249>: nopl 0x0(%rax,%rax,1)
<__hrtimer_run_queues+254>: mov %r14,%rdi
<__hrtimer_run_queues+257>: cs call <__x86_indirect_thunk_r12>
<__hrtimer_run_queues+263>: mov %eax,%r12d
<__hrtimer_run_queues+266>: xchg %ax,%ax
<__hrtimer_run_queues+268>: mov %r13,%rdi
Which triggers this crash:
BUG: unable to handle page fault for address: 00000000000f41c9
#PF: supervisor write access in kernel mode
#PF: error_code(0x0002) - not-present page
PGD 0 P4D 0
Oops: 0002 [#1] SMP NOPTI
CPU: 1 PID: 0 Comm: swapper/1 Kdump: loaded Tainted: P
RIP: 0010:__hrtimer_run_queues+0x106/0x230
Note that __hrtimer_run_queues+0x106 is __hrtimer_run_queues+262, which is
at the 6th byte of the above CS CALL instruction. Since the CS CALL
instruction occupies 6 bytes, the exception occurred in the middle of that
call instruction.
The root cause is that when using eBPF tools to probe in the middle of a
function, a kprobe with INT3 is used as the underlying implementation.
During single-step emulation of the original CALL instruction,
int3_emulate_call() assumes that the probed CALL instruction is 5 bytes
long. However, the actual CS-prefixed CALL instruction occupies 6 bytes,
so it constructs an incorrect exception return address. When the CPU
returns from the kprobe handler, the next instruction to be executed is at
the address of the last byte of that CS CALL instruction. Coincidentally,
starting from that address, the CPU fetches and decodes a completely
different instruction, which ultimately triggers a kernel crash.
Fix the issue by using the actual instruction length obtained from
the instruction decoder when constructing the exception return
address, rather than relying on the hardcoded CALL_INSN_SIZE macro.
[ mingo: Refined the changelog ]
Fixes: 6256e668b7af ("x86/kprobes: Use int3 instead of debug trap for single-step")
Suggested-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Jinke Han <jinkehan@didiglobal.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Acked-by: Yafang Shao <laoar.shao@gmail.com>
Acked-by: Borislav Petkov <bp@alien8.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://patch.msgid.link/20260908073742.GA10517@didi-ThinkCentre-M920t-N000
Diffstat (limited to 'drivers/tee/qcomtee/qcomtee.h')
| -rw-r--r-- | drivers/tee/qcomtee/qcomtee.h | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/drivers/tee/qcomtee/qcomtee.h b/drivers/tee/qcomtee/qcomtee.h new file mode 100644 index 000000000..f39bf63fd --- /dev/null +++ b/drivers/tee/qcomtee/qcomtee.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef QCOMTEE_H +#define QCOMTEE_H + +#include <linux/kobject.h> +#include <linux/tee_core.h> + +#include "qcomtee_msg.h" +#include "qcomtee_object.h" + +/* Flags relating to object reference. */ +#define QCOMTEE_OBJREF_FLAG_TEE BIT(0) +#define QCOMTEE_OBJREF_FLAG_USER BIT(1) +#define QCOMTEE_OBJREF_FLAG_MEM BIT(2) + +/** + * struct qcomtee - Main service struct. + * @teedev: client device. + * @pool: shared memory pool. + * @ctx: driver private context. + * @oic: context to use for the current driver invocation. + * @wq: workqueue for QTEE async operations. + * @xa_local_objects: array of objects exported to QTEE. + * @xa_last_id: next ID to allocate. + * @qtee_version: QTEE version. + */ +struct qcomtee { + struct tee_device *teedev; + struct tee_shm_pool *pool; + struct tee_context *ctx; + struct qcomtee_object_invoke_ctx oic; + struct workqueue_struct *wq; + struct xarray xa_local_objects; + u32 xa_last_id; + u32 qtee_version; +}; + +void qcomtee_fetch_async_reqs(struct qcomtee_object_invoke_ctx *oic); +struct qcomtee_object *qcomtee_idx_erase(struct qcomtee_object_invoke_ctx *oic, + u32 idx); + +struct tee_shm_pool *qcomtee_shm_pool_alloc(void); +void qcomtee_msg_buffers_free(struct qcomtee_object_invoke_ctx *oic); +int qcomtee_msg_buffers_alloc(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_arg *u); + +/** + * qcomtee_object_do_invoke_internal() - Submit an invocation for an object. + * @oic: context to use for the current invocation. + * @object: object being invoked. + * @op: requested operation on the object. + * @u: array of arguments for the current invocation. + * @result: result returned from QTEE. + * + * The caller is responsible for keeping track of the refcount for each + * object, including @object. On return, the caller loses ownership of all + * input objects of type %QCOMTEE_OBJECT_TYPE_CB. + * + * Return: On success, returns 0; on failure, returns < 0. + */ +int qcomtee_object_do_invoke_internal(struct qcomtee_object_invoke_ctx *oic, + struct qcomtee_object *object, u32 op, + struct qcomtee_arg *u, int *result); + +/** + * struct qcomtee_context_data - Clients' or supplicants' context. + * @qtee_objects_idr: QTEE objects in this context. + * @qtee_lock: mutex for @qtee_objects_idr. + * @reqs_idr: requests in this context that hold ID. + * @reqs_list: FIFO for requests in PROCESSING or QUEUED state. + * @reqs_lock: mutex for @reqs_idr, @reqs_list and request states. + * @req_c: completion used when the supplicant is waiting for requests. + * @released: state of this context. + */ +struct qcomtee_context_data { + struct idr qtee_objects_idr; + /* Synchronize access to @qtee_objects_idr. */ + struct mutex qtee_lock; + + struct idr reqs_idr; + struct list_head reqs_list; + /* Synchronize access to @reqs_idr, @reqs_list and updating requests states. */ + struct mutex reqs_lock; + + struct completion req_c; + + bool released; +}; + +int qcomtee_context_add_qtee_object(struct tee_param *param, + struct qcomtee_object *object, + struct tee_context *ctx); +int qcomtee_context_find_qtee_object(struct qcomtee_object **object, + struct tee_param *param, + struct tee_context *ctx); +void qcomtee_context_del_qtee_object(struct tee_param *param, + struct tee_context *ctx); + +int qcomtee_objref_to_arg(struct qcomtee_arg *arg, struct tee_param *param, + struct tee_context *ctx); +int qcomtee_objref_from_arg(struct tee_param *param, struct qcomtee_arg *arg, + struct tee_context *ctx); + +/* OBJECTS: */ + +/* (1) User Object API. */ + +int is_qcomtee_user_object(struct qcomtee_object *object); +void qcomtee_user_object_set_notify(struct qcomtee_object *object, bool notify); +void qcomtee_requests_destroy(struct qcomtee_context_data *ctxdata); +int qcomtee_user_param_to_object(struct qcomtee_object **object, + struct tee_param *param, + struct tee_context *ctx); +int qcomtee_user_param_from_object(struct tee_param *param, + struct qcomtee_object *object, + struct tee_context *ctx); + +/** + * struct qcomtee_user_object_request_data - Data for user object request. + * @id: ID assigned to the request. + * @object_id: Object ID being invoked by QTEE. + * @op: Requested operation on object. + * @np: Number of parameters in the request. + */ +struct qcomtee_user_object_request_data { + int id; + u64 object_id; + u32 op; + int np; +}; + +int qcomtee_user_object_select(struct tee_context *ctx, + struct tee_param *params, int num_params, + void __user *uaddr, size_t size, + struct qcomtee_user_object_request_data *data); +int qcomtee_user_object_submit(struct tee_context *ctx, + struct tee_param *params, int num_params, + int req_id, int errno); + +/* (2) Primordial Object. */ +extern struct qcomtee_object qcomtee_primordial_object; + +/* (3) Memory Object API. */ + +/* Is it a memory object using tee_shm? */ +int is_qcomtee_memobj_object(struct qcomtee_object *object); + +/** + * qcomtee_memobj_param_to_object() - OBJREF parameter to &struct qcomtee_object. + * @object: object returned. + * @param: TEE parameter. + * @ctx: context in which the conversion should happen. + * + * @param is an OBJREF with %QCOMTEE_OBJREF_FLAG_MEM flags. + * + * Return: On success return 0 or <0 on failure. + */ +int qcomtee_memobj_param_to_object(struct qcomtee_object **object, + struct tee_param *param, + struct tee_context *ctx); + +/* Reverse what qcomtee_memobj_param_to_object() does. */ +int qcomtee_memobj_param_from_object(struct tee_param *param, + struct qcomtee_object *object, + struct tee_context *ctx); + +/** + * qcomtee_mem_object_map() - Map a memory object. + * @object: memory object. + * @map_object: created mapping object. + * @mem_paddr: physical address of the memory. + * @mem_size: size of the memory. + * @perms: QTEE access permissions. + * + * Return: On success return 0 or <0 on failure. + */ +int qcomtee_mem_object_map(struct qcomtee_object *object, + struct qcomtee_object **map_object, u64 *mem_paddr, + u64 *mem_size, u32 *perms); + +#endif /* QCOMTEE_H */ |
