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

Commit 3ab67966 authored by Oleg Nesterov's avatar Oleg Nesterov
Browse files

uprobes: Teach uprobe_copy_process() to handle CLONE_VFORK



uprobe_copy_process() does nothing if the child shares ->mm with
the forking process, but there is a special case: CLONE_VFORK.
In this case it would be more correct to do dup_utask() but avoid
dup_xol(). This is not that important, the child should not unwind
its stack too much, this can corrupt the parent's stack, but at
least we need this to allow to ret-probe __vfork() itself.

Note: in theory, it would be better to check task_pt_regs(p)->sp
instead of CLONE_VFORK, we need to dup_utask() if and only if the
child can return from the function called by the parent. But this
needs the arch-dependant helper, and I think that nobody actually
does clone(same_stack, CLONE_VM).

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>
parent aa59c53f
Loading
Loading
Loading
Loading
+2 −2
Original line number Original line Diff line number Diff line
@@ -117,7 +117,7 @@ extern void uprobe_start_dup_mmap(void);
extern void uprobe_end_dup_mmap(void);
extern void uprobe_end_dup_mmap(void);
extern void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm);
extern void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm);
extern void uprobe_free_utask(struct task_struct *t);
extern void uprobe_free_utask(struct task_struct *t);
extern void uprobe_copy_process(struct task_struct *t);
extern void uprobe_copy_process(struct task_struct *t, unsigned long flags);
extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs);
extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs);
extern int uprobe_post_sstep_notifier(struct pt_regs *regs);
extern int uprobe_post_sstep_notifier(struct pt_regs *regs);
extern int uprobe_pre_sstep_notifier(struct pt_regs *regs);
extern int uprobe_pre_sstep_notifier(struct pt_regs *regs);
@@ -174,7 +174,7 @@ static inline unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
static inline void uprobe_free_utask(struct task_struct *t)
static inline void uprobe_free_utask(struct task_struct *t)
{
{
}
}
static inline void uprobe_copy_process(struct task_struct *t)
static inline void uprobe_copy_process(struct task_struct *t, unsigned long flags)
{
{
}
}
static inline void uprobe_clear_state(struct mm_struct *mm)
static inline void uprobe_clear_state(struct mm_struct *mm)
+8 −2
Original line number Original line Diff line number Diff line
@@ -1415,7 +1415,7 @@ static void dup_xol_work(struct callback_head *work)
/*
/*
 * Called in context of a new clone/fork from copy_process.
 * Called in context of a new clone/fork from copy_process.
 */
 */
void uprobe_copy_process(struct task_struct *t)
void uprobe_copy_process(struct task_struct *t, unsigned long flags)
{
{
	struct uprobe_task *utask = current->utask;
	struct uprobe_task *utask = current->utask;
	struct mm_struct *mm = current->mm;
	struct mm_struct *mm = current->mm;
@@ -1424,7 +1424,10 @@ void uprobe_copy_process(struct task_struct *t)


	t->utask = NULL;
	t->utask = NULL;


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

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


	if (dup_utask(t, utask))
	if (dup_utask(t, utask))
@@ -1435,6 +1438,9 @@ void uprobe_copy_process(struct task_struct *t)
	if (!area)
	if (!area)
		return uprobe_warn(t, "dup xol area");
		return uprobe_warn(t, "dup xol area");


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

	/* TODO: move it into the union in uprobe_task */
	/* TODO: move it into the union in uprobe_task */
	work = kmalloc(sizeof(*work), GFP_KERNEL);
	work = kmalloc(sizeof(*work), GFP_KERNEL);
	if (!work)
	if (!work)
+1 −1
Original line number Original line Diff line number Diff line
@@ -1489,7 +1489,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
	perf_event_fork(p);
	perf_event_fork(p);


	trace_task_newtask(p, clone_flags);
	trace_task_newtask(p, clone_flags);
	uprobe_copy_process(p);
	uprobe_copy_process(p, clone_flags);


	return p;
	return p;