Skip to contentSkip to conversation

uprobes: alloc utask/xol_area cleanups and minor fix

PATCH In review Oleg Nesterov 3 participants 25 messages Dec 31, 2012 – Jan 10, 2013

Subsystems (2)
lore
Share & tools
lore

Oleg Nesterov

author maintainer
#0 lore raw

cover uprobes: alloc utask/xol_area cleanups and minor fix

Hello.

This series fixes the minor bug and cleanups the usage of add_utask() and xol_alloc_area(). Plus it cleanups the initializaion of ->utask in handle_swbp() paths.

Anton, this conflicts with your uretprobe patches, but I think we should do this to avoid the code duplication. IOW, I consider this series as a minor preparations for uretprobes as well.

Oleg.

 kernel/events/uprobes.c |  150 +++++++++++++++++++++--------------------------
 1 files changed, 68 insertions(+), 82 deletions(-)
author maintainer
#1 lore raw

PATCH 1/7 uprobes: Move alloc_page() from xol_add_vma() to xol_alloc_area()

Move alloc_page() from xol_add_vma() to xol_alloc_area() to cleanup the code. This separates the memory allocations and consolidates the -EALREADY cleanups and the error handling.

---
 kernel/events/uprobes.c |   32 +++++++++++++-------------------
 1 files changed, 13 insertions(+), 19 deletions(-)
kernel/events/uprobes.c +13 -19
index f883813..1456f7d 100644
@@ -1041,22 +1041,14 @@ void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned lon
 /* Slot allocation for XOL */
 static int xol_add_vma(struct xol_area *area)
 {
-	struct mm_struct *mm;
-	int ret;
-
-	area->page = alloc_page(GFP_HIGHUSER);
-	if (!area->page)
-		return -ENOMEM;
-
-	ret = -EALREADY;
-	mm = current->mm;
+	struct mm_struct *mm = current->mm;
+	int ret = -EALREADY;
 
 	down_write(&mm->mmap_sem);
 	if (mm->uprobes_state.xol_area)
 		goto fail;
 
 	ret = -ENOMEM;
-
 	/* Try to map as high as possible, this is only a hint. */
 	area->vaddr = get_unmapped_area(NULL, TASK_SIZE - PAGE_SIZE, PAGE_SIZE, 0, 0);
 	if (area->vaddr & ~PAGE_MASK) {
@@ -1072,11 +1064,8 @@ static int xol_add_vma(struct xol_area *area)
 	smp_wmb();	/* pairs with get_xol_area() */
 	mm->uprobes_state.xol_area = area;
 	ret = 0;
-
-fail:
+ fail:
 	up_write(&mm->mmap_sem);
-	if (ret)
-		__free_page(area->page);
 
 	return ret;
 }
@@ -1104,21 +1093,26 @@ static struct xol_area *xol_alloc_area(void)
 
 	area = kzalloc(sizeof(*area), GFP_KERNEL);
 	if (unlikely(!area))
-		return NULL;
+		goto out;
 
 	area->bitmap = kzalloc(BITS_TO_LONGS(UINSNS_PER_PAGE) * sizeof(long), GFP_KERNEL);
-
 	if (!area->bitmap)
-		goto fail;
+		goto free_area;
+
+	area->page = area->page = alloc_page(GFP_HIGHUSER);
+	if (!area->page)
+		goto free_bitmap;
 
 	init_waitqueue_head(&area->wq);
 	if (!xol_add_vma(area))
 		return area;
 
-fail:
+	__free_page(area->page);
+ free_bitmap:
 	kfree(area->bitmap);
+ free_area:
 	kfree(area);
-
+ out:
 	return get_xol_area(current->mm);
 }
 
Signature · 1 line
1.5.5.1
#2 lore raw

Re: [PATCH 1/7] uprobes: Move alloc_page() from xol_add_vma() to xol_alloc_area()

Quoted 17 lines · Oleg Nesterov

Move alloc_page() from xol_add_vma() to xol_alloc_area() to cleanup the code. This separates the memory allocations and consolidates the -EALREADY cleanups and the error handling.

---
 kernel/events/uprobes.c |   32 +++++++++++++-------------------
 1 files changed, 13 insertions(+), 19 deletions(-)
kernel/events/uprobes.c +0 -0
index f883813..1456f7d 100644
@@ -1104,21 +1093,26 @@ static struct xol_area *xol_alloc_area(void)
 
 	area = kzalloc(sizeof(*area), GFP_KERNEL);
 	if (unlikely(!area))

kernel/events/uprobes.c static struct xol_area *xol_alloc_area(void) · line 1102

Quoted 10 lines
kernel/events/uprobes.c +4 -3
-		return NULL;
+		goto out;
 
 	area->bitmap = kzalloc(BITS_TO_LONGS(UINSNS_PER_PAGE) * sizeof(long), GFP_KERNEL);
-
 	if (!area->bitmap)
-		goto fail;
+		goto free_area;
+
+	area->page = area->page = alloc_page(GFP_HIGHUSER);

fyi: didn't review this patch set yet, just caught the string above. ;)

Anton.

author maintainer
#3 lore raw

Re: [PATCH 1/7] uprobes: Move alloc_page() from xol_add_vma() to xol_alloc_area()

Quoted 8 lines · Anton Arapov

kernel/events/uprobes.c static struct xol_area *xol_alloc_area(void) · line 1102

Quoted 4 lines · Oleg Nesterov
kernel/events/uprobes.c +3 -1
-		goto fail;
+		goto free_area;
+
+	area->page = area->page = alloc_page(GFP_HIGHUSER);

fyi: didn't review this patch set yet, just caught the string above. ;)

OOPS, thanks, will fix.

Oleg.

Acked-by on v1 1/7
#4 lore raw

Re: [PATCH 1/7] uprobes: Move alloc_page() from xol_add_vma() to xol_alloc_area()

In reply to Oleg Nesterov
Quoted 5 lines · Oleg Nesterov

Move alloc_page() from xol_add_vma() to xol_alloc_area() to cleanup the code. This separates the memory allocations and consolidates the -EALREADY cleanups and the error handling.

(One simple check below)

Quoted 34 lines
---
 kernel/events/uprobes.c |   32 +++++++++++++-------------------
 1 files changed, 13 insertions(+), 19 deletions(-)
kernel/events/uprobes.c +2 -10
index f883813..1456f7d 100644
@@ -1041,22 +1041,14 @@ void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned lon
 /* Slot allocation for XOL */
 static int xol_add_vma(struct xol_area *area)
 {
-	struct mm_struct *mm;
-	int ret;
-
-	area->page = alloc_page(GFP_HIGHUSER);
-	if (!area->page)
-		return -ENOMEM;
-
-	ret = -EALREADY;
-	mm = current->mm;
+	struct mm_struct *mm = current->mm;
+	int ret = -EALREADY;
 
 	down_write(&mm->mmap_sem);
 	if (mm->uprobes_state.xol_area)
 		goto fail;
 
 	ret = -ENOMEM;
-
 	/* Try to map as high as possible, this is only a hint. */
 	area->vaddr = get_unmapped_area(NULL, TASK_SIZE - PAGE_SIZE, PAGE_SIZE, 0, 0);
 	if (area->vaddr & ~PAGE_MASK) {

kernel/events/uprobes.c static int xol_add_vma(struct xol_area *area) · line 1067

Quoted 6 lines
kernel/events/uprobes.c +1 -2
@@ -1072,11 +1064,8 @@ static int xol_add_vma(struct xol_area *area)
 	smp_wmb();	/* pairs with get_xol_area() */
 	mm->uprobes_state.xol_area = area;
 	ret = 0;
-
-fail:
+ fail:

Not sure if the space before label is intentional? Its true of other labels below also.

Quoted 41 lines
kernel/events/uprobes.c +10 -7
 	up_write(&mm->mmap_sem);
-	if (ret)
-		__free_page(area->page);
 
 	return ret;
 }
@@ -1104,21 +1093,26 @@ static struct xol_area *xol_alloc_area(void)
 
 	area = kzalloc(sizeof(*area), GFP_KERNEL);
 	if (unlikely(!area))
-		return NULL;
+		goto out;
 
 	area->bitmap = kzalloc(BITS_TO_LONGS(UINSNS_PER_PAGE) * sizeof(long), GFP_KERNEL);
-
 	if (!area->bitmap)
-		goto fail;
+		goto free_area;
+
+	area->page = area->page = alloc_page(GFP_HIGHUSER);
+	if (!area->page)
+		goto free_bitmap;
 
 	init_waitqueue_head(&area->wq);
 	if (!xol_add_vma(area))
 		return area;
 
-fail:
+	__free_page(area->page);
+ free_bitmap:
 	kfree(area->bitmap);
+ free_area:
 	kfree(area);
-
+ out:
 	return get_xol_area(current->mm);
 }
 
Signature · 1 line
1.5.5.1
author maintainer
#5 lore raw

Re: [PATCH 1/7] uprobes: Move alloc_page() from xol_add_vma() to xol_alloc_area()

Quoted 4 lines · Srikar Dronamraju

(One simple check below)

Thanks,

Quoted 10 lines

kernel/events/uprobes.c static int xol_add_vma(struct xol_area *area) · line 1067

Quoted 7 lines
kernel/events/uprobes.c +1 -2
@@ -1072,11 +1064,8 @@ static int xol_add_vma(struct xol_area *area)
 	smp_wmb();	/* pairs with get_xol_area() */
 	mm->uprobes_state.xol_area = area;
 	ret = 0;
-
-fail:
+ fail:

Not sure if the space before label is intentional? Its true of other labels below also.

Yes, this was intentional although almost subconscious.

Personally I prefer to add a space before the label, this helps /usr/bin/diff to not confuse this label with the function name.

But I can stop this if you dislike.

Oleg.

#6 lore raw

Re: [PATCH 1/7] uprobes: Move alloc_page() from xol_add_vma() to xol_alloc_area()

Quoted 19 lines · Oleg Nesterov
Quoted 4 lines · Srikar Dronamraju

(One simple check below)

Thanks,

Quoted 10 lines

kernel/events/uprobes.c static int xol_add_vma(struct xol_area *area) · line 1067

Quoted 7 lines
kernel/events/uprobes.c +1 -2
@@ -1072,11 +1064,8 @@ static int xol_add_vma(struct xol_area *area)
 	smp_wmb();	/* pairs with get_xol_area() */
 	mm->uprobes_state.xol_area = area;
 	ret = 0;
-
-fail:
+ fail:

Not sure if the space before label is intentional? Its true of other labels below also.

Quoted 6 lines

Yes, this was intentional although almost subconscious.

Personally I prefer to add a space before the label, this helps /usr/bin/diff to not confuse this label with the function name.

But I can stop this if you dislike.

I dont have any preference. Just wanted to check if it was intentional or accidental. I thought checkpatch.pl cribs for this.
I think somebody while reviewing my patches had pointed out the same to me. I cant remember who and probably my format wasnt just a single space before label.

Quoted 3 lines

Oleg.

author maintainer
#7 lore raw

PATCH 2/7 uprobes: Fold xol_alloc_area() into get_xol_area()

In reply to Oleg Nesterov

Currently only xol_get_insn_slot() does get_xol_area() + xol_alloc_area(), but this will have more users and we do not want to copy-and-paste this code. This patch simply moves xol_alloc_area() into get_xol_area() to simplify the current and future code.

---
 kernel/events/uprobes.c |   38 ++++++++++++++++----------------------
 1 files changed, 16 insertions(+), 22 deletions(-)
kernel/events/uprobes.c +16 -22
index 1456f7d..ef81162 100644
@@ -1070,27 +1070,21 @@ static int xol_add_vma(struct xol_area *area)
 	return ret;
 }
 
-static struct xol_area *get_xol_area(struct mm_struct *mm)
-{
-	struct xol_area *area;
-
-	area = mm->uprobes_state.xol_area;
-	smp_read_barrier_depends();	/* pairs with wmb in xol_add_vma() */
-
-	return area;
-}
-
 /*
- * xol_alloc_area - Allocate process's xol_area.
- * This area will be used for storing instructions for execution out of
- * line.
+ * get_alloc_area - Allocate process's xol_area if necessary.
+ * This area will be used for storing instructions for execution out of line.
  *
  * Returns the allocated area or NULL.
  */
-static struct xol_area *xol_alloc_area(void)
+static struct xol_area *get_xol_area(void)
 {
+	struct mm_struct *mm = current->mm;
 	struct xol_area *area;
 
+	area = mm->uprobes_state.xol_area;
+	if (area)
+		goto ret;
+
 	area = kzalloc(sizeof(*area), GFP_KERNEL);
 	if (unlikely(!area))
 		goto out;
@@ -1113,7 +1107,10 @@ static struct xol_area *xol_alloc_area(void)
  free_area:
 	kfree(area);
  out:
-	return get_xol_area(current->mm);
+	area = mm->uprobes_state.xol_area;
+ ret:
+	smp_read_barrier_depends();     /* pairs with wmb in xol_add_vma() */
+	return area;
 }
 
 /*
@@ -1189,14 +1186,11 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe, unsigned long slot
 	unsigned long offset;
 	void *vaddr;
 
-	area = get_xol_area(current->mm);
-	if (!area) {
-		area = xol_alloc_area();
-		if (!area)
-			return 0;
-	}
-	current->utask->xol_vaddr = xol_take_insn_slot(area);
+	area = get_xol_area();
+	if (!area)
+		return 0;
 
+	current->utask->xol_vaddr = xol_take_insn_slot(area);
 	/*
 	 * Initialize the slot if xol_vaddr points to valid
 	 * instruction slot.
Signature · 1 line
1.5.5.1
Acked-by on v1 2/7
#8 lore raw

Re: [PATCH 2/7] uprobes: Fold xol_alloc_area() into get_xol_area()

Quoted 6 lines · Oleg Nesterov

Currently only xol_get_insn_slot() does get_xol_area() + xol_alloc_area(), but this will have more users and we do not want to copy-and-paste this code. This patch simply moves xol_alloc_area() into get_xol_area() to simplify the current and future code.

Quoted 78 lines
---
 kernel/events/uprobes.c |   38 ++++++++++++++++----------------------
 1 files changed, 16 insertions(+), 22 deletions(-)
kernel/events/uprobes.c +16 -22
index 1456f7d..ef81162 100644
@@ -1070,27 +1070,21 @@ static int xol_add_vma(struct xol_area *area)
 	return ret;
 }
 
-static struct xol_area *get_xol_area(struct mm_struct *mm)
-{
-	struct xol_area *area;
-
-	area = mm->uprobes_state.xol_area;
-	smp_read_barrier_depends();	/* pairs with wmb in xol_add_vma() */
-
-	return area;
-}
-
 /*
- * xol_alloc_area - Allocate process's xol_area.
- * This area will be used for storing instructions for execution out of
- * line.
+ * get_alloc_area - Allocate process's xol_area if necessary.
+ * This area will be used for storing instructions for execution out of line.
  *
  * Returns the allocated area or NULL.
  */
-static struct xol_area *xol_alloc_area(void)
+static struct xol_area *get_xol_area(void)
 {
+	struct mm_struct *mm = current->mm;
 	struct xol_area *area;
 
+	area = mm->uprobes_state.xol_area;
+	if (area)
+		goto ret;
+
 	area = kzalloc(sizeof(*area), GFP_KERNEL);
 	if (unlikely(!area))
 		goto out;
@@ -1113,7 +1107,10 @@ static struct xol_area *xol_alloc_area(void)
  free_area:
 	kfree(area);
  out:
-	return get_xol_area(current->mm);
+	area = mm->uprobes_state.xol_area;
+ ret:
+	smp_read_barrier_depends();     /* pairs with wmb in xol_add_vma() */
+	return area;
 }
 
 /*
@@ -1189,14 +1186,11 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe, unsigned long slot
 	unsigned long offset;
 	void *vaddr;
 
-	area = get_xol_area(current->mm);
-	if (!area) {
-		area = xol_alloc_area();
-		if (!area)
-			return 0;
-	}
-	current->utask->xol_vaddr = xol_take_insn_slot(area);
+	area = get_xol_area();
+	if (!area)
+		return 0;
 
+	current->utask->xol_vaddr = xol_take_insn_slot(area);
 	/*
 	 * Initialize the slot if xol_vaddr points to valid
 	 * instruction slot.
Signature · 1 line
1.5.5.1
#9 lore raw

Re: [PATCH 2/7] uprobes: Fold xol_alloc_area() into get_xol_area()

In reply to Oleg Nesterov
Quoted 27 lines · Oleg Nesterov

Currently only xol_get_insn_slot() does get_xol_area() + xol_alloc_area(), but this will have more users and we do not want to copy-and-paste this code. This patch simply moves xol_alloc_area() into get_xol_area() to simplify the current and future code.

---
 kernel/events/uprobes.c |   38 ++++++++++++++++----------------------
 1 files changed, 16 insertions(+), 22 deletions(-)
kernel/events/uprobes.c +0 -9
index 1456f7d..ef81162 100644
@@ -1070,27 +1070,21 @@ static int xol_add_vma(struct xol_area *area)
 	return ret;
 }
 
-static struct xol_area *get_xol_area(struct mm_struct *mm)
-{
-	struct xol_area *area;
-
-	area = mm->uprobes_state.xol_area;
-	smp_read_barrier_depends();	/* pairs with wmb in xol_add_vma() */
-
-	return area;
-}

kernel/events/uprobes.c static int xol_add_vma(struct xol_area *area) · line 1078

Quoted 10 lines
kernel/events/uprobes.c +2 -4
-
 /*
- * xol_alloc_area - Allocate process's xol_area.
- * This area will be used for storing instructions for execution out of
- * line.
+ * get_alloc_area - Allocate process's xol_area if necessary.
+ * This area will be used for storing instructions for execution out of line.
  *
  * Returns the allocated area or NULL.
  */

+ * get_xol_area ....

Anton.

Quoted 47 lines
kernel/events/uprobes.c +14 -9
-static struct xol_area *xol_alloc_area(void)
+static struct xol_area *get_xol_area(void)
 {
+	struct mm_struct *mm = current->mm;
 	struct xol_area *area;
 
+	area = mm->uprobes_state.xol_area;
+	if (area)
+		goto ret;
+
 	area = kzalloc(sizeof(*area), GFP_KERNEL);
 	if (unlikely(!area))
 		goto out;
@@ -1113,7 +1107,10 @@ static struct xol_area *xol_alloc_area(void)
  free_area:
 	kfree(area);
  out:
-	return get_xol_area(current->mm);
+	area = mm->uprobes_state.xol_area;
+ ret:
+	smp_read_barrier_depends();     /* pairs with wmb in xol_add_vma() */
+	return area;
 }
 
 /*
@@ -1189,14 +1186,11 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe, unsigned long slot
 	unsigned long offset;
 	void *vaddr;
 
-	area = get_xol_area(current->mm);
-	if (!area) {
-		area = xol_alloc_area();
-		if (!area)
-			return 0;
-	}
-	current->utask->xol_vaddr = xol_take_insn_slot(area);
+	area = get_xol_area();
+	if (!area)
+		return 0;
 
+	current->utask->xol_vaddr = xol_take_insn_slot(area);
 	/*
 	 * Initialize the slot if xol_vaddr points to valid
 	 * instruction slot.
Signature · 1 line
1.5.5.1
author maintainer
#10 lore raw

Re: [PATCH 2/7] uprobes: Fold xol_alloc_area() into get_xol_area()

Quoted 9 lines · Anton Arapov
Quoted 5 lines · Oleg Nesterov

+ * get_alloc_area - Allocate process's xol_area if necessary.
+ * This area will be used for storing instructions for execution out of line. *
* Returns the allocated area or NULL.
*/

+ * get_xol_area ....

Ah, comment, yes. Thanks, fixed.

Oleg.

author maintainer
#11 lore raw

PATCH 3/7 uprobes: Turn add_utask() into get_utask()

In reply to Oleg Nesterov

Rename add_utask() into get_utask() and change it to allocate on demand to simplify the caller. Like get_xol_area() it will have more users.

---
 kernel/events/uprobes.c |   27 +++++++++------------------
 1 files changed, 9 insertions(+), 18 deletions(-)
kernel/events/uprobes.c +9 -18
index ef81162..b09b3ba 100644
@@ -1290,23 +1290,18 @@ void uprobe_copy_process(struct task_struct *t)
 }
 
 /*
- * Allocate a uprobe_task object for the task.
- * Called when the thread hits a breakpoint for the first time.
+ * Allocate a uprobe_task object for the task if if necessary.
+ * Called when the thread hits a breakpoint.
  *
  * Returns:
  * - pointer to new uprobe_task on success
  * - NULL otherwise
  */
-static struct uprobe_task *add_utask(void)
+static struct uprobe_task *get_utask(void)
 {
-	struct uprobe_task *utask;
-
-	utask = kzalloc(sizeof *utask, GFP_KERNEL);
-	if (unlikely(!utask))
-		return NULL;
-
-	current->utask = utask;
-	return utask;
+	if (!current->utask)
+		current->utask = kzalloc(sizeof(struct uprobe_task), GFP_KERNEL);
+	return current->utask;
 }
 
 /* Prepare to single-step probed instruction out of line. */
@@ -1505,13 +1500,9 @@ static void handle_swbp(struct pt_regs *regs)
 	if (unlikely(!test_bit(UPROBE_COPY_INSN, &uprobe->flags)))
 		goto out;
 
-	utask = current->utask;
-	if (!utask) {
-		utask = add_utask();
-		/* Cannot allocate; re-execute the instruction. */
-		if (!utask)
-			goto out;
-	}
+	utask = get_utask();
+	if (!utask)
+		goto out;	/* re-execute the instruction. */
 
 	handler_chain(uprobe, regs);
 	if (can_skip_sstep(uprobe, regs))
Signature · 1 line
1.5.5.1
Acked-by on v1 3/7
#12 lore raw

Re: [PATCH 3/7] uprobes: Turn add_utask() into get_utask()

Quoted 5 lines · Oleg Nesterov

Rename add_utask() into get_utask() and change it to allocate on demand to simplify the caller. Like get_xol_area() it will have more users.

Quoted 58 lines
---
 kernel/events/uprobes.c |   27 +++++++++------------------
 1 files changed, 9 insertions(+), 18 deletions(-)
kernel/events/uprobes.c +9 -18
index ef81162..b09b3ba 100644
@@ -1290,23 +1290,18 @@ void uprobe_copy_process(struct task_struct *t)
 }
 
 /*
- * Allocate a uprobe_task object for the task.
- * Called when the thread hits a breakpoint for the first time.
+ * Allocate a uprobe_task object for the task if if necessary.
+ * Called when the thread hits a breakpoint.
  *
  * Returns:
  * - pointer to new uprobe_task on success
  * - NULL otherwise
  */
-static struct uprobe_task *add_utask(void)
+static struct uprobe_task *get_utask(void)
 {
-	struct uprobe_task *utask;
-
-	utask = kzalloc(sizeof *utask, GFP_KERNEL);
-	if (unlikely(!utask))
-		return NULL;
-
-	current->utask = utask;
-	return utask;
+	if (!current->utask)
+		current->utask = kzalloc(sizeof(struct uprobe_task), GFP_KERNEL);
+	return current->utask;
 }
 
 /* Prepare to single-step probed instruction out of line. */
@@ -1505,13 +1500,9 @@ static void handle_swbp(struct pt_regs *regs)
 	if (unlikely(!test_bit(UPROBE_COPY_INSN, &uprobe->flags)))
 		goto out;
 
-	utask = current->utask;
-	if (!utask) {
-		utask = add_utask();
-		/* Cannot allocate; re-execute the instruction. */
-		if (!utask)
-			goto out;
-	}
+	utask = get_utask();
+	if (!utask)
+		goto out;	/* re-execute the instruction. */
 
 	handler_chain(uprobe, regs);
 	if (can_skip_sstep(uprobe, regs))
Signature · 1 line
1.5.5.1
author maintainer
#13 lore raw

PATCH 4/7 uprobes: Do not play with utask in xol_get_insn_slot()

In reply to Oleg Nesterov

pre_ssout()->xol_get_insn_slot() path is confusing and buggy. This patch cleanups the code, the next one fixes the bug.

Change xol_get_insn_slot() to only allocate the slot and do nothing more, move the initialization of utask->xol_vaddr/vaddr into pre_ssout().

---
 kernel/events/uprobes.c |   37 +++++++++++++++++++++----------------
 1 files changed, 21 insertions(+), 16 deletions(-)
kernel/events/uprobes.c +21 -16
index b09b3ba..2ed6239 100644
@@ -1176,30 +1176,26 @@ static unsigned long xol_take_insn_slot(struct xol_area *area)
 }
 
 /*
- * xol_get_insn_slot - If was not allocated a slot, then
- * allocate a slot.
+ * xol_get_insn_slot - allocate a slot for xol.
  * Returns the allocated slot address or 0.
  */
-static unsigned long xol_get_insn_slot(struct uprobe *uprobe, unsigned long slot_addr)
+static unsigned long xol_get_insn_slot(struct uprobe *uprobe)
 {
 	struct xol_area *area;
 	unsigned long offset;
+	unsigned long xol_vaddr;
 	void *vaddr;
 
 	area = get_xol_area();
 	if (!area)
 		return 0;
 
-	current->utask->xol_vaddr = xol_take_insn_slot(area);
-	/*
-	 * Initialize the slot if xol_vaddr points to valid
-	 * instruction slot.
-	 */
-	if (unlikely(!current->utask->xol_vaddr))
+	xol_vaddr = xol_take_insn_slot(area);
+	if (unlikely(!xol_vaddr))
 		return 0;
 
-	current->utask->vaddr = slot_addr;
-	offset = current->utask->xol_vaddr & ~PAGE_MASK;
+	/* Initialize the slot */
+	offset = xol_vaddr & ~PAGE_MASK;
 	vaddr = kmap_atomic(area->page);
 	memcpy(vaddr + offset, uprobe->arch.insn, MAX_UINSN_BYTES);
 	kunmap_atomic(vaddr);
@@ -1209,7 +1205,7 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe, unsigned long slot
 	 */
 	flush_dcache_page(area->page);
 
-	return current->utask->xol_vaddr;
+	return xol_vaddr;
 }
 
 /*
@@ -1306,12 +1302,21 @@ static struct uprobe_task *get_utask(void)
 
 /* Prepare to single-step probed instruction out of line. */
 static int
-pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long vaddr)
+pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
 {
-	if (xol_get_insn_slot(uprobe, vaddr) && !arch_uprobe_pre_xol(&uprobe->arch, regs))
-		return 0;
+	struct uprobe_task *utask;
+	unsigned long xol_vaddr;
+
+	utask = current->utask;
+
+	xol_vaddr = xol_get_insn_slot(uprobe);
+	if (!xol_vaddr)
+		return -ENOMEM;
+
+	utask->xol_vaddr = xol_vaddr;
+	utask->vaddr = bp_vaddr;
 
-	return -EFAULT;
+	return arch_uprobe_pre_xol(&uprobe->arch, regs);
 }
 
 /*
Signature · 1 line
1.5.5.1
Acked-by on v1 4/7
#14 lore raw

Re: [PATCH 4/7] uprobes: Do not play with utask in xol_get_insn_slot()

Quoted 7 lines · Oleg Nesterov

pre_ssout()->xol_get_insn_slot() path is confusing and buggy. This patch cleanups the code, the next one fixes the bug.

Change xol_get_insn_slot() to only allocate the slot and do nothing more, move the initialization of utask->xol_vaddr/vaddr into pre_ssout().

Quoted 84 lines
---
 kernel/events/uprobes.c |   37 +++++++++++++++++++++----------------
 1 files changed, 21 insertions(+), 16 deletions(-)
kernel/events/uprobes.c +21 -16
index b09b3ba..2ed6239 100644
@@ -1176,30 +1176,26 @@ static unsigned long xol_take_insn_slot(struct xol_area *area)
 }
 
 /*
- * xol_get_insn_slot - If was not allocated a slot, then
- * allocate a slot.
+ * xol_get_insn_slot - allocate a slot for xol.
  * Returns the allocated slot address or 0.
  */
-static unsigned long xol_get_insn_slot(struct uprobe *uprobe, unsigned long slot_addr)
+static unsigned long xol_get_insn_slot(struct uprobe *uprobe)
 {
 	struct xol_area *area;
 	unsigned long offset;
+	unsigned long xol_vaddr;
 	void *vaddr;
 
 	area = get_xol_area();
 	if (!area)
 		return 0;
 
-	current->utask->xol_vaddr = xol_take_insn_slot(area);
-	/*
-	 * Initialize the slot if xol_vaddr points to valid
-	 * instruction slot.
-	 */
-	if (unlikely(!current->utask->xol_vaddr))
+	xol_vaddr = xol_take_insn_slot(area);
+	if (unlikely(!xol_vaddr))
 		return 0;
 
-	current->utask->vaddr = slot_addr;
-	offset = current->utask->xol_vaddr & ~PAGE_MASK;
+	/* Initialize the slot */
+	offset = xol_vaddr & ~PAGE_MASK;
 	vaddr = kmap_atomic(area->page);
 	memcpy(vaddr + offset, uprobe->arch.insn, MAX_UINSN_BYTES);
 	kunmap_atomic(vaddr);
@@ -1209,7 +1205,7 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe, unsigned long slot
 	 */
 	flush_dcache_page(area->page);
 
-	return current->utask->xol_vaddr;
+	return xol_vaddr;
 }
 
 /*
@@ -1306,12 +1302,21 @@ static struct uprobe_task *get_utask(void)
 
 /* Prepare to single-step probed instruction out of line. */
 static int
-pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long vaddr)
+pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
 {
-	if (xol_get_insn_slot(uprobe, vaddr) && !arch_uprobe_pre_xol(&uprobe->arch, regs))
-		return 0;
+	struct uprobe_task *utask;
+	unsigned long xol_vaddr;
+
+	utask = current->utask;
+
+	xol_vaddr = xol_get_insn_slot(uprobe);
+	if (!xol_vaddr)
+		return -ENOMEM;
+
+	utask->xol_vaddr = xol_vaddr;
+	utask->vaddr = bp_vaddr;
 
-	return -EFAULT;
+	return arch_uprobe_pre_xol(&uprobe->arch, regs);
 }
 
 /*
Signature · 1 line
1.5.5.1
author maintainer
#15 lore raw

PATCH 5/7 uprobes: Fix utask->xol_vaddr leak in pre_ssout()

In reply to Oleg Nesterov

pre_ssout() should do xol_free_insn_slot() if arch_uprobe_pre_xol() fails, otherwise nobody will free the allocated slot.

---
 kernel/events/uprobes.c |    9 ++++++++-
 1 files changed, 8 insertions(+), 1 deletions(-)
kernel/events/uprobes.c +8 -1
index 2ed6239..bd94d2c 100644
@@ -1306,6 +1306,7 @@ pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
 {
 	struct uprobe_task *utask;
 	unsigned long xol_vaddr;
+	int err;
 
 	utask = current->utask;
 
@@ -1316,7 +1317,13 @@ pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
 	utask->xol_vaddr = xol_vaddr;
 	utask->vaddr = bp_vaddr;
 
-	return arch_uprobe_pre_xol(&uprobe->arch, regs);
+	err = arch_uprobe_pre_xol(&uprobe->arch, regs);
+	if (unlikely(err)) {
+		xol_free_insn_slot(current);
+		return err;
+	}
+
+	return 0;
 }
 
 /*
Signature · 1 line
1.5.5.1
Acked-by on v1 5/7
#16 lore raw

Re: [PATCH 5/7] uprobes: Fix utask->xol_vaddr leak in pre_ssout()

Quoted 4 lines · Oleg Nesterov

pre_ssout() should do xol_free_insn_slot() if arch_uprobe_pre_xol() fails, otherwise nobody will free the allocated slot.

(one nit below)

Quoted 19 lines
---
 kernel/events/uprobes.c |    9 ++++++++-
 1 files changed, 8 insertions(+), 1 deletions(-)
kernel/events/uprobes.c +1 -0
index 2ed6239..bd94d2c 100644
@@ -1306,6 +1306,7 @@ pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
 {
 	struct uprobe_task *utask;
 	unsigned long xol_vaddr;
+	int err;
 
 	utask = current->utask;
 
@@ -1316,7 +1317,13 @@ pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
 	utask->xol_vaddr = xol_vaddr;
 	utask->vaddr = bp_vaddr;

kernel/events/uprobes.c pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr) · line 1327

Quoted 10 lines
kernel/events/uprobes.c +7 -1
 
-	return arch_uprobe_pre_xol(&uprobe->arch, regs);
+	err = arch_uprobe_pre_xol(&uprobe->arch, regs);
+	if (unlikely(err)) {
+		xol_free_insn_slot(current);
+		return err;
+	}
+
+	return 0;
 }

Nit: we could reduce a line or two with

	err = arch_uprobe_pre_xol(&uprobe->arch, regs);
	if (unlikely(err))
		xol_free_insn_slot(current);

	return err;
Quoted 5 lines
kernel/events/uprobes.c +0 -0
 /*
Signature · 1 line
1.5.5.1
author maintainer
#17 lore raw

Re: [PATCH 5/7] uprobes: Fix utask->xol_vaddr leak in pre_ssout()

Quoted 9 lines · Srikar Dronamraju
Quoted 4 lines · Oleg Nesterov

pre_ssout() should do xol_free_insn_slot() if arch_uprobe_pre_xol() fails, otherwise nobody will free the allocated slot.

Thanks!

Quoted 18 lines

(one nit below)
...

kernel/events/uprobes.c pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr) · line 1327

Quoted 13 lines
kernel/events/uprobes.c +7 -1
@@ -1316,7 +1317,13 @@ pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
 	utask->xol_vaddr = xol_vaddr;
 	utask->vaddr = bp_vaddr;
 
-	return arch_uprobe_pre_xol(&uprobe->arch, regs);
+	err = arch_uprobe_pre_xol(&uprobe->arch, regs);
+	if (unlikely(err)) {
+		xol_free_insn_slot(current);
+		return err;
+	}
+
+	return 0;
 }

Nit: we could reduce a line or two with

Quoted 5 lines
	err = arch_uprobe_pre_xol(&uprobe->arch, regs);
	if (unlikely(err))
		xol_free_insn_slot(current);

	return err;

Yes, but this is also preparation for the next patch which adds more code after arch_uprobe_pre_xol() == 0.

Oleg.

#18 lore raw

Re: [PATCH 5/7] uprobes: Fix utask->xol_vaddr leak in pre_ssout()

Quoted 14 lines
Quoted 14 lines

kernel/events/uprobes.c pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr) · line 1327

Quoted 11 lines
kernel/events/uprobes.c +7 -1
 	utask->vaddr = bp_vaddr;
 
-	return arch_uprobe_pre_xol(&uprobe->arch, regs);
+	err = arch_uprobe_pre_xol(&uprobe->arch, regs);
+	if (unlikely(err)) {
+		xol_free_insn_slot(current);
+		return err;
+	}
+
+	return 0;
 }

Nit: we could reduce a line or two with

Quoted 9 lines
Quoted 5 lines
	err = arch_uprobe_pre_xol(&uprobe->arch, regs);
	if (unlikely(err))
		xol_free_insn_slot(current);

	return err;

Yes, but this is also preparation for the next patch which adds more code after arch_uprobe_pre_xol() == 0.

Agree

Signature · 2 lines
Thanks and Regards
Srikar
author maintainer
#19 lore raw

PATCH 6/7 uprobes: Do not allocate current->utask unnecessary

In reply to Oleg Nesterov

handle_swbp() does get_utask() before can_skip_sstep() for no reason, we do not need ->utask if can_skip_sstep() succeeds.

Move get_utask() to pre_ssout() who actually starts to use it. Move the initialization of utask->active_uprobe/state as well. This way the whole initialization is consolidated in pre_ssout().

---
 kernel/events/uprobes.c |   16 ++++++----------
 1 files changed, 6 insertions(+), 10 deletions(-)
kernel/events/uprobes.c +6 -10
index bd94d2c..ad1245d 100644
@@ -1308,7 +1308,9 @@ pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
 	unsigned long xol_vaddr;
 	int err;
 
-	utask = current->utask;
+	utask = get_utask();
+	if (!utask)
+		return -ENOMEM;
 
 	xol_vaddr = xol_get_insn_slot(uprobe);
 	if (!xol_vaddr)
@@ -1323,6 +1325,8 @@ pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
 		return err;
 	}
 
+	utask->active_uprobe = uprobe;
+	utask->state = UTASK_SSTEP;
 	return 0;
 }
 
@@ -1474,7 +1478,6 @@ static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs)
  */
 static void handle_swbp(struct pt_regs *regs)
 {
-	struct uprobe_task *utask;
 	struct uprobe *uprobe;
 	unsigned long bp_vaddr;
 	int uninitialized_var(is_swbp);
@@ -1512,19 +1515,12 @@ static void handle_swbp(struct pt_regs *regs)
 	if (unlikely(!test_bit(UPROBE_COPY_INSN, &uprobe->flags)))
 		goto out;
 
-	utask = get_utask();
-	if (!utask)
-		goto out;	/* re-execute the instruction. */
-
 	handler_chain(uprobe, regs);
 	if (can_skip_sstep(uprobe, regs))
 		goto out;
 
-	if (!pre_ssout(uprobe, regs, bp_vaddr)) {
-		utask->active_uprobe = uprobe;
-		utask->state = UTASK_SSTEP;
+	if (!pre_ssout(uprobe, regs, bp_vaddr))
 		return;
-	}
 
 	/* can_skip_sstep() succeeded, or restart if can't singlestep */
 out:
Signature · 1 line
1.5.5.1
#20 lore raw

Re: [PATCH 6/7] uprobes: Do not allocate current->utask unnecessary

Quoted 45 lines · Oleg Nesterov

handle_swbp() does get_utask() before can_skip_sstep() for no reason, we do not need ->utask if can_skip_sstep() succeeds.

Move get_utask() to pre_ssout() who actually starts to use it. Move the initialization of utask->active_uprobe/state as well. This way the whole initialization is consolidated in pre_ssout().

---
 kernel/events/uprobes.c |   16 ++++++----------
 1 files changed, 6 insertions(+), 10 deletions(-)
kernel/events/uprobes.c +5 -2
index bd94d2c..ad1245d 100644
@@ -1308,7 +1308,9 @@ pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
 	unsigned long xol_vaddr;
 	int err;
 
-	utask = current->utask;
+	utask = get_utask();
+	if (!utask)
+		return -ENOMEM;
 
 	xol_vaddr = xol_get_insn_slot(uprobe);
 	if (!xol_vaddr)
@@ -1323,6 +1325,8 @@ pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
 		return err;
 	}
 
+	utask->active_uprobe = uprobe;
+	utask->state = UTASK_SSTEP;
 	return 0;
 }
 
@@ -1474,7 +1478,6 @@ static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs)
  */
 static void handle_swbp(struct pt_regs *regs)
 {
-	struct uprobe_task *utask;
 	struct uprobe *uprobe;
 	unsigned long bp_vaddr;
 	int uninitialized_var(is_swbp);

kernel/events/uprobes.c static void handle_swbp(struct pt_regs *regs) · line 1518

Quoted 7 lines
kernel/events/uprobes.c +0 -4
@@ -1512,19 +1515,12 @@ static void handle_swbp(struct pt_regs *regs)
 	if (unlikely(!test_bit(UPROBE_COPY_INSN, &uprobe->flags)))
 		goto out;
 
-	utask = get_utask();
-	if (!utask)
-		goto out;	/* re-execute the instruction. */
-

If get_utask fails with the above change, Dont we end up calling handler_chain twice(or more)?. I think this is probably true with previous patch too.

Quoted 16 lines
kernel/events/uprobes.c +1 -4
 	handler_chain(uprobe, regs);
 	if (can_skip_sstep(uprobe, regs))
 		goto out;
 
-	if (!pre_ssout(uprobe, regs, bp_vaddr)) {
-		utask->active_uprobe = uprobe;
-		utask->state = UTASK_SSTEP;
+	if (!pre_ssout(uprobe, regs, bp_vaddr))
 		return;
-	}
 
 	/* can_skip_sstep() succeeded, or restart if can't singlestep */
 out:
Signature · 1 line
1.5.5.1
author maintainer
#21 lore raw

Re: [PATCH 6/7] uprobes: Do not allocate current->utask unnecessary

Quoted 13 lines · Srikar Dronamraju
Quoted 10 lines · Oleg Nesterov

static void handle_swbp(struct pt_regs *regs)
{

kernel/events/uprobes.c +0 -1
-	struct uprobe_task *utask;
 	struct uprobe *uprobe;
 	unsigned long bp_vaddr;
 	int uninitialized_var(is_swbp);
@@ -1512,19 +1515,12 @@ static void handle_swbp(struct pt_regs *regs)
 	if (unlikely(!test_bit(UPROBE_COPY_INSN, &uprobe->flags)))
 		goto out;
 
Quoted 7 lines

kernel/events/uprobes.c static void handle_swbp(struct pt_regs *regs) · line 1518

Quoted 4 lines
kernel/events/uprobes.c +0 -4
-	utask = get_utask();
-	if (!utask)
-		goto out;	/* re-execute the instruction. */
-

If get_utask fails with the above change, Dont we end up calling handler_chain twice(or more)?.

After restart, yes.

Quoted 2 lines

I think this is probably true with
previous patch too.

And this can happen with the current code too, if xol_alloc_area() fails. So I think this is probably fine. Besides, if GFP_KERNEL fails the task should be oom-killed in practice.

Oleg.

author maintainer
#22 lore raw

PATCH 7/7 uprobes: Kill the bogus IS_ERR_VALUE(xol_vaddr) check

In reply to Oleg Nesterov

utask->xol_vaddr is either zero or valid, remove the bogus IS_ERR_VALUE() check in xol_free_insn_slot().

---
 kernel/events/uprobes.c |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)
kernel/events/uprobes.c +1 -2
index ad1245d..ed4fcbe 100644
@@ -1223,8 +1223,7 @@ static void xol_free_insn_slot(struct task_struct *tsk)
 		return;
 
 	slot_addr = tsk->utask->xol_vaddr;
-
-	if (unlikely(!slot_addr || IS_ERR_VALUE(slot_addr)))
+	if (unlikely(!slot_addr))
 		return;
 
 	area = tsk->mm->uprobes_state.xol_area;
Signature · 1 line
1.5.5.1
Acked-by on v1 7/7
#23 lore raw

Re: [PATCH 7/7] uprobes: Kill the bogus IS_ERR_VALUE(xol_vaddr) check

Quoted 4 lines · Oleg Nesterov

utask->xol_vaddr is either zero or valid, remove the bogus IS_ERR_VALUE() check in xol_free_insn_slot().

Quoted 21 lines
---
 kernel/events/uprobes.c |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)
kernel/events/uprobes.c +1 -2
index ad1245d..ed4fcbe 100644
@@ -1223,8 +1223,7 @@ static void xol_free_insn_slot(struct task_struct *tsk)
 		return;
 
 	slot_addr = tsk->utask->xol_vaddr;
-
-	if (unlikely(!slot_addr || IS_ERR_VALUE(slot_addr)))
+	if (unlikely(!slot_addr))
 		return;
 
 	area = tsk->mm->uprobes_state.xol_area;
Signature · 1 line
1.5.5.1
Acked-by on the v1 series
#24 lore raw
In reply to Oleg Nesterov
Quoted 6 lines · Oleg Nesterov

Hello.

This series fixes the minor bug and cleanups the usage of add_utask() and xol_alloc_area(). Plus it cleanups the initializaion of ->utask in handle_swbp() paths.

Quoted 9 lines

Anton, this conflicts with your uretprobe patches, but I think we should do this to avoid the code duplication. IOW, I consider this series as a minor preparations for uretprobes as well.

Oleg.

 kernel/events/uprobes.c |  150 +++++++++++++++++++++--------------------------
 1 files changed, 68 insertions(+), 82 deletions(-)

I haven't found anything other than small style problems, I've pointed to in other mails to this thread.

And this patchset doesn't brake anything on my machine. :)

Anton.