* [PATCH 0/7] uprobes: deuglify xol_get_insn_slot/xol_free_insn_slot paths
@ 2024-09-29 14:42 Oleg Nesterov
2024-09-29 14:42 ` [PATCH 1/7] uprobes: don't abuse get_utask() in pre_ssout() and prepare_uretprobe() Oleg Nesterov
` (7 more replies)
0 siblings, 8 replies; 16+ messages in thread
From: Oleg Nesterov @ 2024-09-29 14:42 UTC (permalink / raw)
To: Andrii Nakryiko, Jiri Olsa, Masami Hiramatsu, Peter Zijlstra
Cc: Liao Chang, linux-kernel, linux-trace-kernel
Hello,
No functional changes, please review.
I'd like to push these cleanups first, then I'll send some optimizations
on top of this series.
Oleg.
---
kernel/events/uprobes.c | 111 ++++++++++++++++--------------------------------
1 file changed, 37 insertions(+), 74 deletions(-)
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 1/7] uprobes: don't abuse get_utask() in pre_ssout() and prepare_uretprobe()
2024-09-29 14:42 [PATCH 0/7] uprobes: deuglify xol_get_insn_slot/xol_free_insn_slot paths Oleg Nesterov
@ 2024-09-29 14:42 ` Oleg Nesterov
2024-10-08 11:05 ` [tip: perf/core] " tip-bot2 for Oleg Nesterov
2024-09-29 14:42 ` [PATCH 2/7] uprobes: sanitiize xol_free_insn_slot() Oleg Nesterov
` (6 subsequent siblings)
7 siblings, 1 reply; 16+ messages in thread
From: Oleg Nesterov @ 2024-09-29 14:42 UTC (permalink / raw)
To: Andrii Nakryiko, Jiri Olsa, Masami Hiramatsu, Peter Zijlstra
Cc: Liao Chang, linux-kernel, linux-trace-kernel
handle_swbp() calls get_utask() before prepare_uretprobe() or pre_ssout()
can be called, they can simply use current->utask which can't be NULL.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
kernel/events/uprobes.c | 14 +++-----------
1 file changed, 3 insertions(+), 11 deletions(-)
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 4b52cb2ae6d6..2a9cdd5c82d7 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1908,18 +1908,14 @@ static void cleanup_return_instances(struct uprobe_task *utask, bool chained,
static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs)
{
- struct return_instance *ri;
- struct uprobe_task *utask;
+ struct uprobe_task *utask = current->utask;
unsigned long orig_ret_vaddr, trampoline_vaddr;
+ struct return_instance *ri;
bool chained;
if (!get_xol_area())
return;
- utask = get_utask();
- if (!utask)
- return;
-
if (utask->depth >= MAX_URETPROBE_DEPTH) {
printk_ratelimited(KERN_INFO "uprobe: omit uretprobe due to"
" nestedness limit pid/tgid=%d/%d\n",
@@ -1980,14 +1976,10 @@ static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs)
static int
pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
{
- struct uprobe_task *utask;
+ struct uprobe_task *utask = current->utask;
unsigned long xol_vaddr;
int err;
- utask = get_utask();
- if (!utask)
- return -ENOMEM;
-
if (!try_get_uprobe(uprobe))
return -EINVAL;
--
2.25.1.362.g51ebf55
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 2/7] uprobes: sanitiize xol_free_insn_slot()
2024-09-29 14:42 [PATCH 0/7] uprobes: deuglify xol_get_insn_slot/xol_free_insn_slot paths Oleg Nesterov
2024-09-29 14:42 ` [PATCH 1/7] uprobes: don't abuse get_utask() in pre_ssout() and prepare_uretprobe() Oleg Nesterov
@ 2024-09-29 14:42 ` Oleg Nesterov
2024-10-08 11:05 ` [tip: perf/core] " tip-bot2 for Oleg Nesterov
2024-09-29 14:42 ` [PATCH 3/7] uprobes: kill the unnecessary put_uprobe/xol_free_insn_slot in uprobe_free_utask() Oleg Nesterov
` (5 subsequent siblings)
7 siblings, 1 reply; 16+ messages in thread
From: Oleg Nesterov @ 2024-09-29 14:42 UTC (permalink / raw)
To: Andrii Nakryiko, Jiri Olsa, Masami Hiramatsu, Peter Zijlstra
Cc: Liao Chang, linux-kernel, linux-trace-kernel
1. Clear utask->xol_vaddr unconditionally, even if this addr is not valid,
xol_free_insn_slot() should never return with utask->xol_vaddr != NULL.
2. Add a comment to explain why do we need to validate slot_addr.
3. Simplify the validation above. We can simply check offset < PAGE_SIZE,
unsigned underflows are fine, it should work if slot_addr < area->vaddr.
4. Kill the unnecessary "slot_nr >= UINSNS_PER_PAGE" check, slot_nr must
be valid if offset < PAGE_SIZE.
The next patches will cleanup this function even more.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
kernel/events/uprobes.c | 21 +++++++++------------
1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 2a9cdd5c82d7..3023714b83f2 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1683,8 +1683,8 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe)
static void xol_free_insn_slot(struct task_struct *tsk)
{
struct xol_area *area;
- unsigned long vma_end;
unsigned long slot_addr;
+ unsigned long offset;
if (!tsk->mm || !tsk->mm->uprobes_state.xol_area || !tsk->utask)
return;
@@ -1693,24 +1693,21 @@ static void xol_free_insn_slot(struct task_struct *tsk)
if (unlikely(!slot_addr))
return;
+ tsk->utask->xol_vaddr = 0;
area = tsk->mm->uprobes_state.xol_area;
- vma_end = area->vaddr + PAGE_SIZE;
- if (area->vaddr <= slot_addr && slot_addr < vma_end) {
- unsigned long offset;
- int slot_nr;
-
- offset = slot_addr - area->vaddr;
- slot_nr = offset / UPROBE_XOL_SLOT_BYTES;
- if (slot_nr >= UINSNS_PER_PAGE)
- return;
+ offset = slot_addr - area->vaddr;
+ /*
+ * slot_addr must fit into [area->vaddr, area->vaddr + PAGE_SIZE).
+ * This check can only fail if the "[uprobes]" vma was mremap'ed.
+ */
+ if (offset < PAGE_SIZE) {
+ int slot_nr = offset / UPROBE_XOL_SLOT_BYTES;
clear_bit(slot_nr, area->bitmap);
atomic_dec(&area->slot_count);
smp_mb__after_atomic(); /* pairs with prepare_to_wait() */
if (waitqueue_active(&area->wq))
wake_up(&area->wq);
-
- tsk->utask->xol_vaddr = 0;
}
}
--
2.25.1.362.g51ebf55
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 3/7] uprobes: kill the unnecessary put_uprobe/xol_free_insn_slot in uprobe_free_utask()
2024-09-29 14:42 [PATCH 0/7] uprobes: deuglify xol_get_insn_slot/xol_free_insn_slot paths Oleg Nesterov
2024-09-29 14:42 ` [PATCH 1/7] uprobes: don't abuse get_utask() in pre_ssout() and prepare_uretprobe() Oleg Nesterov
2024-09-29 14:42 ` [PATCH 2/7] uprobes: sanitiize xol_free_insn_slot() Oleg Nesterov
@ 2024-09-29 14:42 ` Oleg Nesterov
2024-10-08 11:05 ` [tip: perf/core] " tip-bot2 for Oleg Nesterov
2024-09-29 14:42 ` [PATCH 4/7] uprobes: simplify xol_take_insn_slot() and its caller Oleg Nesterov
` (4 subsequent siblings)
7 siblings, 1 reply; 16+ messages in thread
From: Oleg Nesterov @ 2024-09-29 14:42 UTC (permalink / raw)
To: Andrii Nakryiko, Jiri Olsa, Masami Hiramatsu, Peter Zijlstra
Cc: Liao Chang, linux-kernel, linux-trace-kernel
If pre_ssout() succeeds and sets utask->active_uprobe and utask->xol_vaddr
the task must not exit until it calls handle_singlestep() which does the
necessary put_uprobe() and xol_free_insn_slot().
Remove put_uprobe() and xol_free_insn_slot() from uprobe_free_utask(). With
this change xol_free_insn_slot() can't hit xol_area/utask/xol_vaddr == NULL,
we can kill the unnecessary checks checks and simplify this function more.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
kernel/events/uprobes.c | 24 +++++-------------------
1 file changed, 5 insertions(+), 19 deletions(-)
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 3023714b83f2..4619de10772e 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1676,28 +1676,16 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe)
}
/*
- * xol_free_insn_slot - If slot was earlier allocated by
- * @xol_get_insn_slot(), make the slot available for
- * subsequent requests.
+ * xol_free_insn_slot - free the slot allocated by xol_get_insn_slot()
*/
static void xol_free_insn_slot(struct task_struct *tsk)
{
- struct xol_area *area;
- unsigned long slot_addr;
- unsigned long offset;
-
- if (!tsk->mm || !tsk->mm->uprobes_state.xol_area || !tsk->utask)
- return;
-
- slot_addr = tsk->utask->xol_vaddr;
- if (unlikely(!slot_addr))
- return;
+ struct xol_area *area = tsk->mm->uprobes_state.xol_area;
+ unsigned long offset = tsk->utask->xol_vaddr - area->vaddr;
tsk->utask->xol_vaddr = 0;
- area = tsk->mm->uprobes_state.xol_area;
- offset = slot_addr - area->vaddr;
/*
- * slot_addr must fit into [area->vaddr, area->vaddr + PAGE_SIZE).
+ * xol_vaddr must fit into [area->vaddr, area->vaddr + PAGE_SIZE).
* This check can only fail if the "[uprobes]" vma was mremap'ed.
*/
if (offset < PAGE_SIZE) {
@@ -1767,14 +1755,12 @@ void uprobe_free_utask(struct task_struct *t)
if (!utask)
return;
- if (utask->active_uprobe)
- put_uprobe(utask->active_uprobe);
+ WARN_ON_ONCE(utask->active_uprobe || utask->xol_vaddr);
ri = utask->return_instances;
while (ri)
ri = free_ret_instance(ri);
- xol_free_insn_slot(t);
kfree(utask);
t->utask = NULL;
}
--
2.25.1.362.g51ebf55
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 4/7] uprobes: simplify xol_take_insn_slot() and its caller
2024-09-29 14:42 [PATCH 0/7] uprobes: deuglify xol_get_insn_slot/xol_free_insn_slot paths Oleg Nesterov
` (2 preceding siblings ...)
2024-09-29 14:42 ` [PATCH 3/7] uprobes: kill the unnecessary put_uprobe/xol_free_insn_slot in uprobe_free_utask() Oleg Nesterov
@ 2024-09-29 14:42 ` Oleg Nesterov
2024-10-08 11:05 ` [tip: perf/core] " tip-bot2 for Oleg Nesterov
2024-09-29 14:42 ` [PATCH 5/7] uprobes: move the initialization of utask->xol_vaddr from pre_ssout() to xol_get_insn_slot() Oleg Nesterov
` (3 subsequent siblings)
7 siblings, 1 reply; 16+ messages in thread
From: Oleg Nesterov @ 2024-09-29 14:42 UTC (permalink / raw)
To: Andrii Nakryiko, Jiri Olsa, Masami Hiramatsu, Peter Zijlstra
Cc: Liao Chang, linux-kernel, linux-trace-kernel
The do / while (slot_nr >= UINSNS_PER_PAGE) loop in xol_take_insn_slot()
makes no sense, the checked condition is always true. Change this code
to use the "for (;;)" loop, this way we do not need to change slot_nr if
test_and_set_bit() fails.
Also, kill the unnecessary xol_vaddr != NULL check in xol_get_insn_slot(),
xol_take_insn_slot() never returns NULL.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
kernel/events/uprobes.c | 17 ++++-------------
1 file changed, 4 insertions(+), 13 deletions(-)
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 4619de10772e..bfe106ecad38 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1631,25 +1631,20 @@ void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm)
*/
static unsigned long xol_take_insn_slot(struct xol_area *area)
{
- unsigned long slot_addr;
- int slot_nr;
+ unsigned int slot_nr;
- do {
+ for (;;) {
slot_nr = find_first_zero_bit(area->bitmap, UINSNS_PER_PAGE);
if (slot_nr < UINSNS_PER_PAGE) {
if (!test_and_set_bit(slot_nr, area->bitmap))
break;
-
- slot_nr = UINSNS_PER_PAGE;
continue;
}
wait_event(area->wq, (atomic_read(&area->slot_count) < UINSNS_PER_PAGE));
- } while (slot_nr >= UINSNS_PER_PAGE);
+ }
- slot_addr = area->vaddr + (slot_nr * UPROBE_XOL_SLOT_BYTES);
atomic_inc(&area->slot_count);
-
- return slot_addr;
+ return area->vaddr + slot_nr * UPROBE_XOL_SLOT_BYTES;
}
/*
@@ -1666,12 +1661,8 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe)
return 0;
xol_vaddr = xol_take_insn_slot(area);
- if (unlikely(!xol_vaddr))
- return 0;
-
arch_uprobe_copy_ixol(area->page, xol_vaddr,
&uprobe->arch.ixol, sizeof(uprobe->arch.ixol));
-
return xol_vaddr;
}
--
2.25.1.362.g51ebf55
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 5/7] uprobes: move the initialization of utask->xol_vaddr from pre_ssout() to xol_get_insn_slot()
2024-09-29 14:42 [PATCH 0/7] uprobes: deuglify xol_get_insn_slot/xol_free_insn_slot paths Oleg Nesterov
` (3 preceding siblings ...)
2024-09-29 14:42 ` [PATCH 4/7] uprobes: simplify xol_take_insn_slot() and its caller Oleg Nesterov
@ 2024-09-29 14:42 ` Oleg Nesterov
2024-10-08 11:05 ` [tip: perf/core] " tip-bot2 for Oleg Nesterov
2024-09-29 14:42 ` [PATCH 6/7] uprobes: pass utask to xol_get_insn_slot() and xol_free_insn_slot() Oleg Nesterov
` (2 subsequent siblings)
7 siblings, 1 reply; 16+ messages in thread
From: Oleg Nesterov @ 2024-09-29 14:42 UTC (permalink / raw)
To: Andrii Nakryiko, Jiri Olsa, Masami Hiramatsu, Peter Zijlstra
Cc: Liao Chang, linux-kernel, linux-trace-kernel
This simplifies the code and makes xol_get_insn_slot() symmetric with
xol_free_insn_slot() which clears utask->xol_vaddr.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
kernel/events/uprobes.c | 22 ++++++++--------------
1 file changed, 8 insertions(+), 14 deletions(-)
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index bfe106ecad38..a7223be5ac2e 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1649,21 +1649,19 @@ static unsigned long xol_take_insn_slot(struct xol_area *area)
/*
* 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)
+static bool xol_get_insn_slot(struct uprobe *uprobe)
{
- struct xol_area *area;
- unsigned long xol_vaddr;
+ struct uprobe_task *utask = current->utask;
+ struct xol_area *area = get_xol_area();
- area = get_xol_area();
if (!area)
- return 0;
+ return false;
- xol_vaddr = xol_take_insn_slot(area);
- arch_uprobe_copy_ixol(area->page, xol_vaddr,
+ utask->xol_vaddr = xol_take_insn_slot(area);
+ arch_uprobe_copy_ixol(area->page, utask->xol_vaddr,
&uprobe->arch.ixol, sizeof(uprobe->arch.ixol));
- return xol_vaddr;
+ return true;
}
/*
@@ -1951,21 +1949,17 @@ static int
pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
{
struct uprobe_task *utask = current->utask;
- unsigned long xol_vaddr;
int err;
if (!try_get_uprobe(uprobe))
return -EINVAL;
- xol_vaddr = xol_get_insn_slot(uprobe);
- if (!xol_vaddr) {
+ if (!xol_get_insn_slot(uprobe)) {
err = -ENOMEM;
goto err_out;
}
- utask->xol_vaddr = xol_vaddr;
utask->vaddr = bp_vaddr;
-
err = arch_uprobe_pre_xol(&uprobe->arch, regs);
if (unlikely(err)) {
xol_free_insn_slot(current);
--
2.25.1.362.g51ebf55
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 6/7] uprobes: pass utask to xol_get_insn_slot() and xol_free_insn_slot()
2024-09-29 14:42 [PATCH 0/7] uprobes: deuglify xol_get_insn_slot/xol_free_insn_slot paths Oleg Nesterov
` (4 preceding siblings ...)
2024-09-29 14:42 ` [PATCH 5/7] uprobes: move the initialization of utask->xol_vaddr from pre_ssout() to xol_get_insn_slot() Oleg Nesterov
@ 2024-09-29 14:42 ` Oleg Nesterov
2024-10-08 11:05 ` [tip: perf/core] " tip-bot2 for Oleg Nesterov
2024-09-29 14:42 ` [PATCH 7/7] uprobes: deny mremap(xol_vma) Oleg Nesterov
2024-09-30 8:10 ` [PATCH 0/7] uprobes: deuglify xol_get_insn_slot/xol_free_insn_slot paths Peter Zijlstra
7 siblings, 1 reply; 16+ messages in thread
From: Oleg Nesterov @ 2024-09-29 14:42 UTC (permalink / raw)
To: Andrii Nakryiko, Jiri Olsa, Masami Hiramatsu, Peter Zijlstra
Cc: Liao Chang, linux-kernel, linux-trace-kernel
Add the "struct uprobe_task *utask" argument to xol_get_insn_slot() and
xol_free_insn_slot(), their callers already have it so we can avoid the
unnecessary dereference and simplify the code.
Kill the "tsk" argument of xol_free_insn_slot(), it is always current.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
kernel/events/uprobes.c | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index a7223be5ac2e..da45d0e5bcf4 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1650,9 +1650,8 @@ static unsigned long xol_take_insn_slot(struct xol_area *area)
/*
* xol_get_insn_slot - allocate a slot for xol.
*/
-static bool xol_get_insn_slot(struct uprobe *uprobe)
+static bool xol_get_insn_slot(struct uprobe *uprobe, struct uprobe_task *utask)
{
- struct uprobe_task *utask = current->utask;
struct xol_area *area = get_xol_area();
if (!area)
@@ -1667,12 +1666,12 @@ static bool xol_get_insn_slot(struct uprobe *uprobe)
/*
* xol_free_insn_slot - free the slot allocated by xol_get_insn_slot()
*/
-static void xol_free_insn_slot(struct task_struct *tsk)
+static void xol_free_insn_slot(struct uprobe_task *utask)
{
- struct xol_area *area = tsk->mm->uprobes_state.xol_area;
- unsigned long offset = tsk->utask->xol_vaddr - area->vaddr;
+ struct xol_area *area = current->mm->uprobes_state.xol_area;
+ unsigned long offset = utask->xol_vaddr - area->vaddr;
- tsk->utask->xol_vaddr = 0;
+ utask->xol_vaddr = 0;
/*
* xol_vaddr must fit into [area->vaddr, area->vaddr + PAGE_SIZE).
* This check can only fail if the "[uprobes]" vma was mremap'ed.
@@ -1954,7 +1953,7 @@ pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
if (!try_get_uprobe(uprobe))
return -EINVAL;
- if (!xol_get_insn_slot(uprobe)) {
+ if (!xol_get_insn_slot(uprobe, utask)) {
err = -ENOMEM;
goto err_out;
}
@@ -1962,7 +1961,7 @@ pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
utask->vaddr = bp_vaddr;
err = arch_uprobe_pre_xol(&uprobe->arch, regs);
if (unlikely(err)) {
- xol_free_insn_slot(current);
+ xol_free_insn_slot(utask);
goto err_out;
}
@@ -2313,7 +2312,7 @@ static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs)
put_uprobe(uprobe);
utask->active_uprobe = NULL;
utask->state = UTASK_RUNNING;
- xol_free_insn_slot(current);
+ xol_free_insn_slot(utask);
spin_lock_irq(¤t->sighand->siglock);
recalc_sigpending(); /* see uprobe_deny_signal() */
--
2.25.1.362.g51ebf55
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 7/7] uprobes: deny mremap(xol_vma)
2024-09-29 14:42 [PATCH 0/7] uprobes: deuglify xol_get_insn_slot/xol_free_insn_slot paths Oleg Nesterov
` (5 preceding siblings ...)
2024-09-29 14:42 ` [PATCH 6/7] uprobes: pass utask to xol_get_insn_slot() and xol_free_insn_slot() Oleg Nesterov
@ 2024-09-29 14:42 ` Oleg Nesterov
2024-10-08 11:05 ` [tip: perf/core] " tip-bot2 for Oleg Nesterov
2024-09-30 8:10 ` [PATCH 0/7] uprobes: deuglify xol_get_insn_slot/xol_free_insn_slot paths Peter Zijlstra
7 siblings, 1 reply; 16+ messages in thread
From: Oleg Nesterov @ 2024-09-29 14:42 UTC (permalink / raw)
To: Andrii Nakryiko, Jiri Olsa, Masami Hiramatsu, Peter Zijlstra
Cc: Liao Chang, linux-kernel, linux-trace-kernel
kernel/events/uprobes.c assumes that xol_area->vaddr is always correct but
a malicious application can remap its "[uprobes]" vma to another adress to
confuse the kernel. Introduce xol_mremap() to make this impossible.
With this change utask->xol_vaddr in xol_free_insn_slot() can't be invalid,
we can turn the offset check into WARN_ON_ONCE(offset >= PAGE_SIZE).
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
kernel/events/uprobes.c | 30 +++++++++++++++++-------------
1 file changed, 17 insertions(+), 13 deletions(-)
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index da45d0e5bcf4..20c58b6ee1ad 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1475,9 +1475,15 @@ static vm_fault_t xol_fault(const struct vm_special_mapping *sm,
return 0;
}
+static int xol_mremap(const struct vm_special_mapping *sm, struct vm_area_struct *new_vma)
+{
+ return -EPERM;
+}
+
static const struct vm_special_mapping xol_mapping = {
.name = "[uprobes]",
.fault = xol_fault,
+ .mremap = xol_mremap,
};
/* Slot allocation for XOL */
@@ -1670,21 +1676,19 @@ static void xol_free_insn_slot(struct uprobe_task *utask)
{
struct xol_area *area = current->mm->uprobes_state.xol_area;
unsigned long offset = utask->xol_vaddr - area->vaddr;
+ unsigned int slot_nr;
utask->xol_vaddr = 0;
- /*
- * xol_vaddr must fit into [area->vaddr, area->vaddr + PAGE_SIZE).
- * This check can only fail if the "[uprobes]" vma was mremap'ed.
- */
- if (offset < PAGE_SIZE) {
- int slot_nr = offset / UPROBE_XOL_SLOT_BYTES;
-
- clear_bit(slot_nr, area->bitmap);
- atomic_dec(&area->slot_count);
- smp_mb__after_atomic(); /* pairs with prepare_to_wait() */
- if (waitqueue_active(&area->wq))
- wake_up(&area->wq);
- }
+ /* xol_vaddr must fit into [area->vaddr, area->vaddr + PAGE_SIZE) */
+ if (WARN_ON_ONCE(offset >= PAGE_SIZE))
+ return;
+
+ slot_nr = offset / UPROBE_XOL_SLOT_BYTES;
+ clear_bit(slot_nr, area->bitmap);
+ atomic_dec(&area->slot_count);
+ smp_mb__after_atomic(); /* pairs with prepare_to_wait() */
+ if (waitqueue_active(&area->wq))
+ wake_up(&area->wq);
}
void __weak arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
--
2.25.1.362.g51ebf55
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH 0/7] uprobes: deuglify xol_get_insn_slot/xol_free_insn_slot paths
2024-09-29 14:42 [PATCH 0/7] uprobes: deuglify xol_get_insn_slot/xol_free_insn_slot paths Oleg Nesterov
` (6 preceding siblings ...)
2024-09-29 14:42 ` [PATCH 7/7] uprobes: deny mremap(xol_vma) Oleg Nesterov
@ 2024-09-30 8:10 ` Peter Zijlstra
7 siblings, 0 replies; 16+ messages in thread
From: Peter Zijlstra @ 2024-09-30 8:10 UTC (permalink / raw)
To: Oleg Nesterov
Cc: Andrii Nakryiko, Jiri Olsa, Masami Hiramatsu, Liao Chang,
linux-kernel, linux-trace-kernel
On Sun, Sep 29, 2024 at 04:42:01PM +0200, Oleg Nesterov wrote:
> Hello,
>
> No functional changes, please review.
>
> I'd like to push these cleanups first, then I'll send some optimizations
> on top of this series.
They look okay to me, I'll queue them up. Thanks!
^ permalink raw reply [flat|nested] 16+ messages in thread
* [tip: perf/core] uprobes: deny mremap(xol_vma)
2024-09-29 14:42 ` [PATCH 7/7] uprobes: deny mremap(xol_vma) Oleg Nesterov
@ 2024-10-08 11:05 ` tip-bot2 for Oleg Nesterov
0 siblings, 0 replies; 16+ messages in thread
From: tip-bot2 for Oleg Nesterov @ 2024-10-08 11:05 UTC (permalink / raw)
To: linux-tip-commits
Cc: Oleg Nesterov, Peter Zijlstra (Intel), x86, linux-kernel
The following commit has been merged into the perf/core branch of tip:
Commit-ID: c16e2fdd746c78f5b2ce3c2ab8a26a61b6ed09e5
Gitweb: https://git.kernel.org/tip/c16e2fdd746c78f5b2ce3c2ab8a26a61b6ed09e5
Author: Oleg Nesterov <oleg@redhat.com>
AuthorDate: Sun, 29 Sep 2024 16:42:58 +02:00
Committer: Peter Zijlstra <peterz@infradead.org>
CommitterDate: Mon, 07 Oct 2024 09:28:45 +02:00
uprobes: deny mremap(xol_vma)
kernel/events/uprobes.c assumes that xol_area->vaddr is always correct but
a malicious application can remap its "[uprobes]" vma to another adress to
confuse the kernel. Introduce xol_mremap() to make this impossible.
With this change utask->xol_vaddr in xol_free_insn_slot() can't be invalid,
we can turn the offset check into WARN_ON_ONCE(offset >= PAGE_SIZE).
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20240929144258.GA9492@redhat.com
---
kernel/events/uprobes.c | 30 +++++++++++++++++-------------
1 file changed, 17 insertions(+), 13 deletions(-)
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index c9f1e1e..d3538b6 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1472,9 +1472,15 @@ static vm_fault_t xol_fault(const struct vm_special_mapping *sm,
return 0;
}
+static int xol_mremap(const struct vm_special_mapping *sm, struct vm_area_struct *new_vma)
+{
+ return -EPERM;
+}
+
static const struct vm_special_mapping xol_mapping = {
.name = "[uprobes]",
.fault = xol_fault,
+ .mremap = xol_mremap,
};
/* Slot allocation for XOL */
@@ -1667,21 +1673,19 @@ static void xol_free_insn_slot(struct uprobe_task *utask)
{
struct xol_area *area = current->mm->uprobes_state.xol_area;
unsigned long offset = utask->xol_vaddr - area->vaddr;
+ unsigned int slot_nr;
utask->xol_vaddr = 0;
- /*
- * xol_vaddr must fit into [area->vaddr, area->vaddr + PAGE_SIZE).
- * This check can only fail if the "[uprobes]" vma was mremap'ed.
- */
- if (offset < PAGE_SIZE) {
- int slot_nr = offset / UPROBE_XOL_SLOT_BYTES;
-
- clear_bit(slot_nr, area->bitmap);
- atomic_dec(&area->slot_count);
- smp_mb__after_atomic(); /* pairs with prepare_to_wait() */
- if (waitqueue_active(&area->wq))
- wake_up(&area->wq);
- }
+ /* xol_vaddr must fit into [area->vaddr, area->vaddr + PAGE_SIZE) */
+ if (WARN_ON_ONCE(offset >= PAGE_SIZE))
+ return;
+
+ slot_nr = offset / UPROBE_XOL_SLOT_BYTES;
+ clear_bit(slot_nr, area->bitmap);
+ atomic_dec(&area->slot_count);
+ smp_mb__after_atomic(); /* pairs with prepare_to_wait() */
+ if (waitqueue_active(&area->wq))
+ wake_up(&area->wq);
}
void __weak arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [tip: perf/core] uprobes: pass utask to xol_get_insn_slot() and xol_free_insn_slot()
2024-09-29 14:42 ` [PATCH 6/7] uprobes: pass utask to xol_get_insn_slot() and xol_free_insn_slot() Oleg Nesterov
@ 2024-10-08 11:05 ` tip-bot2 for Oleg Nesterov
0 siblings, 0 replies; 16+ messages in thread
From: tip-bot2 for Oleg Nesterov @ 2024-10-08 11:05 UTC (permalink / raw)
To: linux-tip-commits
Cc: Oleg Nesterov, Peter Zijlstra (Intel), x86, linux-kernel
The following commit has been merged into the perf/core branch of tip:
Commit-ID: c5356ab1db28cafc448a50c26ba84442237abb98
Gitweb: https://git.kernel.org/tip/c5356ab1db28cafc448a50c26ba84442237abb98
Author: Oleg Nesterov <oleg@redhat.com>
AuthorDate: Sun, 29 Sep 2024 16:42:53 +02:00
Committer: Peter Zijlstra <peterz@infradead.org>
CommitterDate: Mon, 07 Oct 2024 09:28:45 +02:00
uprobes: pass utask to xol_get_insn_slot() and xol_free_insn_slot()
Add the "struct uprobe_task *utask" argument to xol_get_insn_slot() and
xol_free_insn_slot(), their callers already have it so we can avoid the
unnecessary dereference and simplify the code.
Kill the "tsk" argument of xol_free_insn_slot(), it is always current.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20240929144253.GA9487@redhat.com
---
kernel/events/uprobes.c | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index dfaca30..c9f1e1e 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1647,9 +1647,8 @@ static unsigned long xol_take_insn_slot(struct xol_area *area)
/*
* xol_get_insn_slot - allocate a slot for xol.
*/
-static bool xol_get_insn_slot(struct uprobe *uprobe)
+static bool xol_get_insn_slot(struct uprobe *uprobe, struct uprobe_task *utask)
{
- struct uprobe_task *utask = current->utask;
struct xol_area *area = get_xol_area();
if (!area)
@@ -1664,12 +1663,12 @@ static bool xol_get_insn_slot(struct uprobe *uprobe)
/*
* xol_free_insn_slot - free the slot allocated by xol_get_insn_slot()
*/
-static void xol_free_insn_slot(struct task_struct *tsk)
+static void xol_free_insn_slot(struct uprobe_task *utask)
{
- struct xol_area *area = tsk->mm->uprobes_state.xol_area;
- unsigned long offset = tsk->utask->xol_vaddr - area->vaddr;
+ struct xol_area *area = current->mm->uprobes_state.xol_area;
+ unsigned long offset = utask->xol_vaddr - area->vaddr;
- tsk->utask->xol_vaddr = 0;
+ utask->xol_vaddr = 0;
/*
* xol_vaddr must fit into [area->vaddr, area->vaddr + PAGE_SIZE).
* This check can only fail if the "[uprobes]" vma was mremap'ed.
@@ -1951,7 +1950,7 @@ pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
if (!try_get_uprobe(uprobe))
return -EINVAL;
- if (!xol_get_insn_slot(uprobe)) {
+ if (!xol_get_insn_slot(uprobe, utask)) {
err = -ENOMEM;
goto err_out;
}
@@ -1959,7 +1958,7 @@ pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
utask->vaddr = bp_vaddr;
err = arch_uprobe_pre_xol(&uprobe->arch, regs);
if (unlikely(err)) {
- xol_free_insn_slot(current);
+ xol_free_insn_slot(utask);
goto err_out;
}
@@ -2307,7 +2306,7 @@ static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs)
put_uprobe(uprobe);
utask->active_uprobe = NULL;
utask->state = UTASK_RUNNING;
- xol_free_insn_slot(current);
+ xol_free_insn_slot(utask);
spin_lock_irq(¤t->sighand->siglock);
recalc_sigpending(); /* see uprobe_deny_signal() */
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [tip: perf/core] uprobes: move the initialization of utask->xol_vaddr from pre_ssout() to xol_get_insn_slot()
2024-09-29 14:42 ` [PATCH 5/7] uprobes: move the initialization of utask->xol_vaddr from pre_ssout() to xol_get_insn_slot() Oleg Nesterov
@ 2024-10-08 11:05 ` tip-bot2 for Oleg Nesterov
0 siblings, 0 replies; 16+ messages in thread
From: tip-bot2 for Oleg Nesterov @ 2024-10-08 11:05 UTC (permalink / raw)
To: linux-tip-commits
Cc: Oleg Nesterov, Peter Zijlstra (Intel), x86, linux-kernel
The following commit has been merged into the perf/core branch of tip:
Commit-ID: 1cee988c1d21eabc936d1401811012522083e36f
Gitweb: https://git.kernel.org/tip/1cee988c1d21eabc936d1401811012522083e36f
Author: Oleg Nesterov <oleg@redhat.com>
AuthorDate: Sun, 29 Sep 2024 16:42:48 +02:00
Committer: Peter Zijlstra <peterz@infradead.org>
CommitterDate: Mon, 07 Oct 2024 09:28:45 +02:00
uprobes: move the initialization of utask->xol_vaddr from pre_ssout() to xol_get_insn_slot()
This simplifies the code and makes xol_get_insn_slot() symmetric with
xol_free_insn_slot() which clears utask->xol_vaddr.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20240929144248.GA9483@redhat.com
---
kernel/events/uprobes.c | 22 ++++++++--------------
1 file changed, 8 insertions(+), 14 deletions(-)
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 616b5ff..dfaca30 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1646,21 +1646,19 @@ static unsigned long xol_take_insn_slot(struct xol_area *area)
/*
* 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)
+static bool xol_get_insn_slot(struct uprobe *uprobe)
{
- struct xol_area *area;
- unsigned long xol_vaddr;
+ struct uprobe_task *utask = current->utask;
+ struct xol_area *area = get_xol_area();
- area = get_xol_area();
if (!area)
- return 0;
+ return false;
- xol_vaddr = xol_take_insn_slot(area);
- arch_uprobe_copy_ixol(area->page, xol_vaddr,
+ utask->xol_vaddr = xol_take_insn_slot(area);
+ arch_uprobe_copy_ixol(area->page, utask->xol_vaddr,
&uprobe->arch.ixol, sizeof(uprobe->arch.ixol));
- return xol_vaddr;
+ return true;
}
/*
@@ -1948,21 +1946,17 @@ static int
pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
{
struct uprobe_task *utask = current->utask;
- unsigned long xol_vaddr;
int err;
if (!try_get_uprobe(uprobe))
return -EINVAL;
- xol_vaddr = xol_get_insn_slot(uprobe);
- if (!xol_vaddr) {
+ if (!xol_get_insn_slot(uprobe)) {
err = -ENOMEM;
goto err_out;
}
- utask->xol_vaddr = xol_vaddr;
utask->vaddr = bp_vaddr;
-
err = arch_uprobe_pre_xol(&uprobe->arch, regs);
if (unlikely(err)) {
xol_free_insn_slot(current);
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [tip: perf/core] uprobes: simplify xol_take_insn_slot() and its caller
2024-09-29 14:42 ` [PATCH 4/7] uprobes: simplify xol_take_insn_slot() and its caller Oleg Nesterov
@ 2024-10-08 11:05 ` tip-bot2 for Oleg Nesterov
0 siblings, 0 replies; 16+ messages in thread
From: tip-bot2 for Oleg Nesterov @ 2024-10-08 11:05 UTC (permalink / raw)
To: linux-tip-commits
Cc: Oleg Nesterov, Peter Zijlstra (Intel), x86, linux-kernel
The following commit has been merged into the perf/core branch of tip:
Commit-ID: 6ffe8c7d871b327d16ae6b6f1db4c8ecb0f15c64
Gitweb: https://git.kernel.org/tip/6ffe8c7d871b327d16ae6b6f1db4c8ecb0f15c64
Author: Oleg Nesterov <oleg@redhat.com>
AuthorDate: Sun, 29 Sep 2024 16:42:44 +02:00
Committer: Peter Zijlstra <peterz@infradead.org>
CommitterDate: Mon, 07 Oct 2024 09:28:44 +02:00
uprobes: simplify xol_take_insn_slot() and its caller
The do / while (slot_nr >= UINSNS_PER_PAGE) loop in xol_take_insn_slot()
makes no sense, the checked condition is always true. Change this code
to use the "for (;;)" loop, this way we do not need to change slot_nr if
test_and_set_bit() fails.
Also, kill the unnecessary xol_vaddr != NULL check in xol_get_insn_slot(),
xol_take_insn_slot() never returns NULL.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20240929144244.GA9480@redhat.com
---
kernel/events/uprobes.c | 17 ++++-------------
1 file changed, 4 insertions(+), 13 deletions(-)
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 03035a8..616b5ff 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1628,25 +1628,20 @@ void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm)
*/
static unsigned long xol_take_insn_slot(struct xol_area *area)
{
- unsigned long slot_addr;
- int slot_nr;
+ unsigned int slot_nr;
- do {
+ for (;;) {
slot_nr = find_first_zero_bit(area->bitmap, UINSNS_PER_PAGE);
if (slot_nr < UINSNS_PER_PAGE) {
if (!test_and_set_bit(slot_nr, area->bitmap))
break;
-
- slot_nr = UINSNS_PER_PAGE;
continue;
}
wait_event(area->wq, (atomic_read(&area->slot_count) < UINSNS_PER_PAGE));
- } while (slot_nr >= UINSNS_PER_PAGE);
+ }
- slot_addr = area->vaddr + (slot_nr * UPROBE_XOL_SLOT_BYTES);
atomic_inc(&area->slot_count);
-
- return slot_addr;
+ return area->vaddr + slot_nr * UPROBE_XOL_SLOT_BYTES;
}
/*
@@ -1663,12 +1658,8 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe)
return 0;
xol_vaddr = xol_take_insn_slot(area);
- if (unlikely(!xol_vaddr))
- return 0;
-
arch_uprobe_copy_ixol(area->page, xol_vaddr,
&uprobe->arch.ixol, sizeof(uprobe->arch.ixol));
-
return xol_vaddr;
}
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [tip: perf/core] uprobes: kill the unnecessary put_uprobe/xol_free_insn_slot in uprobe_free_utask()
2024-09-29 14:42 ` [PATCH 3/7] uprobes: kill the unnecessary put_uprobe/xol_free_insn_slot in uprobe_free_utask() Oleg Nesterov
@ 2024-10-08 11:05 ` tip-bot2 for Oleg Nesterov
0 siblings, 0 replies; 16+ messages in thread
From: tip-bot2 for Oleg Nesterov @ 2024-10-08 11:05 UTC (permalink / raw)
To: linux-tip-commits
Cc: Oleg Nesterov, Peter Zijlstra (Intel), x86, linux-kernel
The following commit has been merged into the perf/core branch of tip:
Commit-ID: 430af825ba991730f8acc3c804a4aef82e9f7ff6
Gitweb: https://git.kernel.org/tip/430af825ba991730f8acc3c804a4aef82e9f7ff6
Author: Oleg Nesterov <oleg@redhat.com>
AuthorDate: Sun, 29 Sep 2024 16:42:39 +02:00
Committer: Peter Zijlstra <peterz@infradead.org>
CommitterDate: Mon, 07 Oct 2024 09:28:44 +02:00
uprobes: kill the unnecessary put_uprobe/xol_free_insn_slot in uprobe_free_utask()
If pre_ssout() succeeds and sets utask->active_uprobe and utask->xol_vaddr
the task must not exit until it calls handle_singlestep() which does the
necessary put_uprobe() and xol_free_insn_slot().
Remove put_uprobe() and xol_free_insn_slot() from uprobe_free_utask(). With
this change xol_free_insn_slot() can't hit xol_area/utask/xol_vaddr == NULL,
we can kill the unnecessary checks checks and simplify this function more.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20240929144239.GA9475@redhat.com
---
kernel/events/uprobes.c | 24 +++++-------------------
1 file changed, 5 insertions(+), 19 deletions(-)
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 3f38be1..03035a8 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1673,28 +1673,16 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe)
}
/*
- * xol_free_insn_slot - If slot was earlier allocated by
- * @xol_get_insn_slot(), make the slot available for
- * subsequent requests.
+ * xol_free_insn_slot - free the slot allocated by xol_get_insn_slot()
*/
static void xol_free_insn_slot(struct task_struct *tsk)
{
- struct xol_area *area;
- unsigned long slot_addr;
- unsigned long offset;
-
- if (!tsk->mm || !tsk->mm->uprobes_state.xol_area || !tsk->utask)
- return;
-
- slot_addr = tsk->utask->xol_vaddr;
- if (unlikely(!slot_addr))
- return;
+ struct xol_area *area = tsk->mm->uprobes_state.xol_area;
+ unsigned long offset = tsk->utask->xol_vaddr - area->vaddr;
tsk->utask->xol_vaddr = 0;
- area = tsk->mm->uprobes_state.xol_area;
- offset = slot_addr - area->vaddr;
/*
- * slot_addr must fit into [area->vaddr, area->vaddr + PAGE_SIZE).
+ * xol_vaddr must fit into [area->vaddr, area->vaddr + PAGE_SIZE).
* This check can only fail if the "[uprobes]" vma was mremap'ed.
*/
if (offset < PAGE_SIZE) {
@@ -1764,14 +1752,12 @@ void uprobe_free_utask(struct task_struct *t)
if (!utask)
return;
- if (utask->active_uprobe)
- put_uprobe(utask->active_uprobe);
+ WARN_ON_ONCE(utask->active_uprobe || utask->xol_vaddr);
ri = utask->return_instances;
while (ri)
ri = free_ret_instance(ri);
- xol_free_insn_slot(t);
kfree(utask);
t->utask = NULL;
}
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [tip: perf/core] uprobes: sanitiize xol_free_insn_slot()
2024-09-29 14:42 ` [PATCH 2/7] uprobes: sanitiize xol_free_insn_slot() Oleg Nesterov
@ 2024-10-08 11:05 ` tip-bot2 for Oleg Nesterov
0 siblings, 0 replies; 16+ messages in thread
From: tip-bot2 for Oleg Nesterov @ 2024-10-08 11:05 UTC (permalink / raw)
To: linux-tip-commits
Cc: Oleg Nesterov, Peter Zijlstra (Intel), x86, linux-kernel
The following commit has been merged into the perf/core branch of tip:
Commit-ID: c7b4133c48445dde789ed30b19ccb0448c7593f7
Gitweb: https://git.kernel.org/tip/c7b4133c48445dde789ed30b19ccb0448c7593f7
Author: Oleg Nesterov <oleg@redhat.com>
AuthorDate: Sun, 29 Sep 2024 16:42:35 +02:00
Committer: Peter Zijlstra <peterz@infradead.org>
CommitterDate: Mon, 07 Oct 2024 09:28:44 +02:00
uprobes: sanitiize xol_free_insn_slot()
1. Clear utask->xol_vaddr unconditionally, even if this addr is not valid,
xol_free_insn_slot() should never return with utask->xol_vaddr != NULL.
2. Add a comment to explain why do we need to validate slot_addr.
3. Simplify the validation above. We can simply check offset < PAGE_SIZE,
unsigned underflows are fine, it should work if slot_addr < area->vaddr.
4. Kill the unnecessary "slot_nr >= UINSNS_PER_PAGE" check, slot_nr must
be valid if offset < PAGE_SIZE.
The next patches will cleanup this function even more.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20240929144235.GA9471@redhat.com
---
kernel/events/uprobes.c | 21 +++++++++------------
1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 15e91a3..3f38be1 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1680,8 +1680,8 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe)
static void xol_free_insn_slot(struct task_struct *tsk)
{
struct xol_area *area;
- unsigned long vma_end;
unsigned long slot_addr;
+ unsigned long offset;
if (!tsk->mm || !tsk->mm->uprobes_state.xol_area || !tsk->utask)
return;
@@ -1690,24 +1690,21 @@ static void xol_free_insn_slot(struct task_struct *tsk)
if (unlikely(!slot_addr))
return;
+ tsk->utask->xol_vaddr = 0;
area = tsk->mm->uprobes_state.xol_area;
- vma_end = area->vaddr + PAGE_SIZE;
- if (area->vaddr <= slot_addr && slot_addr < vma_end) {
- unsigned long offset;
- int slot_nr;
-
- offset = slot_addr - area->vaddr;
- slot_nr = offset / UPROBE_XOL_SLOT_BYTES;
- if (slot_nr >= UINSNS_PER_PAGE)
- return;
+ offset = slot_addr - area->vaddr;
+ /*
+ * slot_addr must fit into [area->vaddr, area->vaddr + PAGE_SIZE).
+ * This check can only fail if the "[uprobes]" vma was mremap'ed.
+ */
+ if (offset < PAGE_SIZE) {
+ int slot_nr = offset / UPROBE_XOL_SLOT_BYTES;
clear_bit(slot_nr, area->bitmap);
atomic_dec(&area->slot_count);
smp_mb__after_atomic(); /* pairs with prepare_to_wait() */
if (waitqueue_active(&area->wq))
wake_up(&area->wq);
-
- tsk->utask->xol_vaddr = 0;
}
}
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [tip: perf/core] uprobes: don't abuse get_utask() in pre_ssout() and prepare_uretprobe()
2024-09-29 14:42 ` [PATCH 1/7] uprobes: don't abuse get_utask() in pre_ssout() and prepare_uretprobe() Oleg Nesterov
@ 2024-10-08 11:05 ` tip-bot2 for Oleg Nesterov
0 siblings, 0 replies; 16+ messages in thread
From: tip-bot2 for Oleg Nesterov @ 2024-10-08 11:05 UTC (permalink / raw)
To: linux-tip-commits
Cc: Oleg Nesterov, Peter Zijlstra (Intel), x86, linux-kernel
The following commit has been merged into the perf/core branch of tip:
Commit-ID: b302d5a6fff5dd7ddb1e4752d60c0eaa4cc4f7f3
Gitweb: https://git.kernel.org/tip/b302d5a6fff5dd7ddb1e4752d60c0eaa4cc4f7f3
Author: Oleg Nesterov <oleg@redhat.com>
AuthorDate: Sun, 29 Sep 2024 16:42:30 +02:00
Committer: Peter Zijlstra <peterz@infradead.org>
CommitterDate: Mon, 07 Oct 2024 09:28:44 +02:00
uprobes: don't abuse get_utask() in pre_ssout() and prepare_uretprobe()
handle_swbp() calls get_utask() before prepare_uretprobe() or pre_ssout()
can be called, they can simply use current->utask which can't be NULL.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20240929144230.GA9468@redhat.com
---
kernel/events/uprobes.c | 14 +++-----------
1 file changed, 3 insertions(+), 11 deletions(-)
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 5106dc1..15e91a3 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1905,18 +1905,14 @@ static void cleanup_return_instances(struct uprobe_task *utask, bool chained,
static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs)
{
- struct return_instance *ri;
- struct uprobe_task *utask;
+ struct uprobe_task *utask = current->utask;
unsigned long orig_ret_vaddr, trampoline_vaddr;
+ struct return_instance *ri;
bool chained;
if (!get_xol_area())
return;
- utask = get_utask();
- if (!utask)
- return;
-
if (utask->depth >= MAX_URETPROBE_DEPTH) {
printk_ratelimited(KERN_INFO "uprobe: omit uretprobe due to"
" nestedness limit pid/tgid=%d/%d\n",
@@ -1977,14 +1973,10 @@ fail:
static int
pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr)
{
- struct uprobe_task *utask;
+ struct uprobe_task *utask = current->utask;
unsigned long xol_vaddr;
int err;
- utask = get_utask();
- if (!utask)
- return -ENOMEM;
-
if (!try_get_uprobe(uprobe))
return -EINVAL;
^ permalink raw reply related [flat|nested] 16+ messages in thread
end of thread, other threads:[~2024-10-08 11:05 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-29 14:42 [PATCH 0/7] uprobes: deuglify xol_get_insn_slot/xol_free_insn_slot paths Oleg Nesterov
2024-09-29 14:42 ` [PATCH 1/7] uprobes: don't abuse get_utask() in pre_ssout() and prepare_uretprobe() Oleg Nesterov
2024-10-08 11:05 ` [tip: perf/core] " tip-bot2 for Oleg Nesterov
2024-09-29 14:42 ` [PATCH 2/7] uprobes: sanitiize xol_free_insn_slot() Oleg Nesterov
2024-10-08 11:05 ` [tip: perf/core] " tip-bot2 for Oleg Nesterov
2024-09-29 14:42 ` [PATCH 3/7] uprobes: kill the unnecessary put_uprobe/xol_free_insn_slot in uprobe_free_utask() Oleg Nesterov
2024-10-08 11:05 ` [tip: perf/core] " tip-bot2 for Oleg Nesterov
2024-09-29 14:42 ` [PATCH 4/7] uprobes: simplify xol_take_insn_slot() and its caller Oleg Nesterov
2024-10-08 11:05 ` [tip: perf/core] " tip-bot2 for Oleg Nesterov
2024-09-29 14:42 ` [PATCH 5/7] uprobes: move the initialization of utask->xol_vaddr from pre_ssout() to xol_get_insn_slot() Oleg Nesterov
2024-10-08 11:05 ` [tip: perf/core] " tip-bot2 for Oleg Nesterov
2024-09-29 14:42 ` [PATCH 6/7] uprobes: pass utask to xol_get_insn_slot() and xol_free_insn_slot() Oleg Nesterov
2024-10-08 11:05 ` [tip: perf/core] " tip-bot2 for Oleg Nesterov
2024-09-29 14:42 ` [PATCH 7/7] uprobes: deny mremap(xol_vma) Oleg Nesterov
2024-10-08 11:05 ` [tip: perf/core] " tip-bot2 for Oleg Nesterov
2024-09-30 8:10 ` [PATCH 0/7] uprobes: deuglify xol_get_insn_slot/xol_free_insn_slot paths Peter Zijlstra
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.