kernel/events/uprobes.c

Source file repositories/reference/linux-study-clean/kernel/events/uprobes.c

File Facts

System
Linux kernel
Corpus path
kernel/events/uprobes.c
Extension
.c
Size
75862 bytes
Lines
2915
Domain
Core OS
Bucket
Scheduler, Processes, Timers, Sync, And Syscalls
Inferred role
Core OS: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Core operating-system implementation surface: boot, tasks, memory, VFS, syscall-facing interfaces, synchronization, credentials, and isolation.

Dependency Surface

Detected Declarations

Annotated Snippet

* Called in context of a new clone/fork from copy_process.
 */
void uprobe_copy_process(struct task_struct *t, u64 flags)
{
	struct uprobe_task *utask = current->utask;
	struct mm_struct *mm = current->mm;
	struct xol_area *area;

	t->utask = NULL;

	if (!utask || !utask->return_instances)
		return;

	if (mm == t->mm && !(flags & CLONE_VFORK))
		return;

	if (dup_utask(t, utask))
		return uprobe_warn(t, "dup ret instances");

	/* The task can fork() after dup_xol_work() fails */
	area = mm->uprobes_state.xol_area;
	if (!area)
		return uprobe_warn(t, "dup xol area");

	if (mm == t->mm)
		return;

	t->utask->dup_xol_addr = area->vaddr;
	init_task_work(&t->utask->dup_xol_work, dup_xol_work);
	task_work_add(t, &t->utask->dup_xol_work, TWA_RESUME);
}

/*
 * Current area->vaddr notion assume the trampoline address is always
 * equal area->vaddr.
 *
 * Returns -1 in case the xol_area is not allocated.
 */
unsigned long uprobe_get_trampoline_vaddr(void)
{
	unsigned long trampoline_vaddr = UPROBE_NO_TRAMPOLINE_VADDR;
	struct xol_area *area;

	/* Pairs with xol_add_vma() smp_store_release() */
	area = READ_ONCE(current->mm->uprobes_state.xol_area); /* ^^^ */
	if (area)
		trampoline_vaddr = area->vaddr;

	return trampoline_vaddr;
}

static void cleanup_return_instances(struct uprobe_task *utask, bool chained,
					struct pt_regs *regs)
{
	struct return_instance *ri = utask->return_instances, *ri_next;
	enum rp_check ctx = chained ? RP_CHECK_CHAIN_CALL : RP_CHECK_CALL;

	while (ri && !arch_uretprobe_is_alive(ri, ctx, regs)) {
		ri_next = ri->next;
		rcu_assign_pointer(utask->return_instances, ri_next);
		utask->depth--;

		free_ret_instance(utask, ri, true /* cleanup_hprobe */);
		ri = ri_next;
	}
}

static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs,
			      struct return_instance *ri)
{
	struct uprobe_task *utask = current->utask;
	unsigned long orig_ret_vaddr, trampoline_vaddr;
	bool chained;
	int srcu_idx;

	if (!get_xol_area())
		goto free;

	if (utask->depth >= MAX_URETPROBE_DEPTH) {
		printk_ratelimited(KERN_INFO "uprobe: omit uretprobe due to"
				" nestedness limit pid/tgid=%d/%d\n",
				current->pid, current->tgid);
		goto free;
	}

	trampoline_vaddr = uprobe_get_trampoline_vaddr();
	orig_ret_vaddr = arch_uretprobe_hijack_return_addr(trampoline_vaddr, regs);
	if (orig_ret_vaddr == -1)
		goto free;

Annotation

Implementation Notes