File Index
Files and directories
Sign in
FILE

uprobes.c

File preview with syntax highlighting.

linux-6.19.14/arch/arm64/kernel/probes/uprobes.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2014-2016 Pratyush Anand <panand@redhat.com>
  4 */
  5#include <linux/highmem.h>
  6#include <linux/ptrace.h>
  7#include <linux/uprobes.h>
  8#include <asm/cacheflush.h>
  9#include <asm/gcs.h>
 10
 11#include "decode-insn.h"
 12
 13#define UPROBE_INV_FAULT_CODE	UINT_MAX
 14
 15void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
 16		void *src, unsigned long len)
 17{
 18	void *xol_page_kaddr = kmap_atomic(page);
 19	void *dst = xol_page_kaddr + (vaddr & ~PAGE_MASK);
 20
 21	/*
 22	 * Initial cache maintenance of the xol page done via set_pte_at().
 23	 * Subsequent CMOs only needed if the xol slot changes.
 24	 */
 25	if (!memcmp(dst, src, len))
 26		goto done;
 27
 28	/* Initialize the slot */
 29	memcpy(dst, src, len);
 30
 31	/* flush caches (dcache/icache) */
 32	sync_icache_aliases((unsigned long)dst, (unsigned long)dst + len);
 33
 34done:
 35	kunmap_atomic(xol_page_kaddr);
 36}
 37
 38unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
 39{
 40	return instruction_pointer(regs);
 41}
 42
 43int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
 44		unsigned long addr)
 45{
 46	u32 insn;
 47
 48	/* TODO: Currently we do not support AARCH32 instruction probing */
 49	if (mm->context.flags & MMCF_AARCH32)
 50		return -EOPNOTSUPP;
 51	else if (!IS_ALIGNED(addr, AARCH64_INSN_SIZE))
 52		return -EINVAL;
 53
 54	insn = le32_to_cpu(auprobe->insn);
 55
 56	switch (arm_probe_decode_insn(insn, &auprobe->api)) {
 57	case INSN_REJECTED:
 58		return -EINVAL;
 59
 60	case INSN_GOOD_NO_SLOT:
 61		auprobe->simulate = true;
 62		break;
 63
 64	default:
 65		break;
 66	}
 67
 68	return 0;
 69}
 70
 71int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 72{
 73	struct uprobe_task *utask = current->utask;
 74
 75	/* Initialize with an invalid fault code to detect if ol insn trapped */
 76	current->thread.fault_code = UPROBE_INV_FAULT_CODE;
 77
 78	/* Instruction points to execute ol */
 79	instruction_pointer_set(regs, utask->xol_vaddr);
 80
 81	user_enable_single_step(current);
 82
 83	return 0;
 84}
 85
 86int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 87{
 88	struct uprobe_task *utask = current->utask;
 89
 90	WARN_ON_ONCE(current->thread.fault_code != UPROBE_INV_FAULT_CODE);
 91
 92	/* Instruction points to execute next to breakpoint address */
 93	instruction_pointer_set(regs, utask->vaddr + 4);
 94
 95	user_disable_single_step(current);
 96
 97	return 0;
 98}
 99bool arch_uprobe_xol_was_trapped(struct task_struct *t)
100{
101	/*
102	 * Between arch_uprobe_pre_xol and arch_uprobe_post_xol, if an xol
103	 * insn itself is trapped, then detect the case with the help of
104	 * invalid fault code which is being set in arch_uprobe_pre_xol
105	 */
106	if (t->thread.fault_code != UPROBE_INV_FAULT_CODE)
107		return true;
108
109	return false;
110}
111
112bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
113{
114	u32 insn;
115	unsigned long addr;
116
117	if (!auprobe->simulate)
118		return false;
119
120	insn = le32_to_cpu(auprobe->insn);
121	addr = instruction_pointer(regs);
122
123	if (auprobe->api.handler)
124		auprobe->api.handler(insn, addr, regs);
125
126	return true;
127}
128
129void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
130{
131	struct uprobe_task *utask = current->utask;
132
133	/*
134	 * Task has received a fatal signal, so reset back to probed
135	 * address.
136	 */
137	instruction_pointer_set(regs, utask->vaddr);
138
139	user_disable_single_step(current);
140}
141
142bool arch_uretprobe_is_alive(struct return_instance *ret, enum rp_check ctx,
143		struct pt_regs *regs)
144{
145	/*
146	 * If a simple branch instruction (B) was called for retprobed
147	 * assembly label then return true even when regs->sp and ret->stack
148	 * are same. It will ensure that cleanup and reporting of return
149	 * instances corresponding to callee label is done when
150	 * handle_trampoline for called function is executed.
151	 */
152	if (ctx == RP_CHECK_CHAIN_CALL)
153		return regs->sp <= ret->stack;
154	else
155		return regs->sp < ret->stack;
156}
157
158unsigned long
159arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
160				  struct pt_regs *regs)
161{
162	unsigned long orig_ret_vaddr;
163	unsigned long gcs_ret_vaddr;
164	int err = 0;
165	u64 gcspr;
166
167	orig_ret_vaddr = procedure_link_pointer(regs);
168
169	if (task_gcs_el0_enabled(current)) {
170		gcspr = read_sysreg_s(SYS_GCSPR_EL0);
171		gcs_ret_vaddr = get_user_gcs((__force unsigned long __user *)gcspr, &err);
172		if (err) {
173			force_sig(SIGSEGV);
174			goto out;
175		}
176
177		/*
178		 * If the LR and GCS return addr don't match, then some kind of PAC
179		 * signing or control flow occurred since entering the probed function.
180		 * Likely because the user is attempting to retprobe on an instruction
181		 * that isn't a function boundary or inside a leaf function. Explicitly
182		 * abort this retprobe because it will generate a GCS exception.
183		 */
184		if (gcs_ret_vaddr != orig_ret_vaddr) {
185			orig_ret_vaddr = -1;
186			goto out;
187		}
188
189		put_user_gcs(trampoline_vaddr, (__force unsigned long __user *)gcspr, &err);
190		if (err) {
191			force_sig(SIGSEGV);
192			goto out;
193		}
194	}
195
196	/* Replace the return addr with trampoline addr */
197	procedure_link_pointer_set(regs, trampoline_vaddr);
198
199out:
200	return orig_ret_vaddr;
201}
202
203int arch_uprobe_exception_notify(struct notifier_block *self,
204				 unsigned long val, void *data)
205{
206	return NOTIFY_DONE;
207}
208
209int uprobe_brk_handler(struct pt_regs *regs,
210				     unsigned long esr)
211{
212	if (uprobe_pre_sstep_notifier(regs))
213		return DBG_HOOK_HANDLED;
214
215	return DBG_HOOK_ERROR;
216}
217
218int uprobe_single_step_handler(struct pt_regs *regs,
219				      unsigned long esr)
220{
221	struct uprobe_task *utask = current->utask;
222
223	WARN_ON(utask && (instruction_pointer(regs) != utask->xol_vaddr + 4));
224	if (uprobe_post_sstep_notifier(regs))
225		return DBG_HOOK_HANDLED;
226
227	return DBG_HOOK_ERROR;
228}
229
~/ Read-only file preview.