Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 248d3a7b authored by Oleg Nesterov's avatar Oleg Nesterov
Browse files

uprobes: Change uprobe_copy_process() to dup return_instances



uprobe_copy_process() assumes that the new child doesn't need
->utask, it should be allocated by demand.

But this is not true if the forking task has the pending ret-
probes, the child should report them as well and thus it needs
the copy of parent's ->return_instances chain. Otherwise the
child crashes when it returns from the probed function.

Alternatively we could cleanup the child's stack, but this needs
per-arch changes and this is not what we want. At least systemtap
expects a .return in the child too.

Note: this change alone doesn't fix the problem, see the next
change.

Reported-by: default avatarMartin Cermak <mcermak@redhat.com>
Reported-by: default avatarDavid Smith <dsmith@redhat.com>
Signed-off-by: default avatarOleg Nesterov <oleg@redhat.com>
Acked-by: default avatarSrikar Dronamraju <srikar@linux.vnet.ibm.com>
parent af0d95af
Loading
Loading
Loading
Loading
+43 −0
Original line number Diff line number Diff line
@@ -1366,12 +1366,55 @@ static struct uprobe_task *get_utask(void)
	return current->utask;
}

static int dup_utask(struct task_struct *t, struct uprobe_task *o_utask)
{
	struct uprobe_task *n_utask;
	struct return_instance **p, *o, *n;

	n_utask = kzalloc(sizeof(struct uprobe_task), GFP_KERNEL);
	if (!n_utask)
		return -ENOMEM;
	t->utask = n_utask;

	p = &n_utask->return_instances;
	for (o = o_utask->return_instances; o; o = o->next) {
		n = kmalloc(sizeof(struct return_instance), GFP_KERNEL);
		if (!n)
			return -ENOMEM;

		*n = *o;
		atomic_inc(&n->uprobe->ref);
		n->next = NULL;

		*p = n;
		p = &n->next;
		n_utask->depth++;
	}

	return 0;
}

static void uprobe_warn(struct task_struct *t, const char *msg)
{
	pr_warn("uprobe: %s:%d failed to %s\n",
			current->comm, current->pid, msg);
}

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

	t->utask = NULL;

	if (mm == t->mm || !utask || !utask->return_instances)
		return;

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

/*