Skip to contentSkip to conversation

Uprobes patchset with perf probe support

PATCH Superseded Srikar Dronamraju 12 participants 172 messages Sep 20, 2011 – Oct 25, 2011

Subsystems (11)

v5 of 7 Superseded Latest is v7 →

lore
Share & tools
lore
#0 lore raw

v5 cover Uprobes patchset with perf probe support

This patchset implements Uprobes which enables you to dynamically break into any routine in a user space application and collect information non-disruptively.

This patchset resolves most of the comments on the previous posting
(https://lkml.org/lkml/2011/6/7/232) patchset applies on top of tip commit e467f18f945c83e66

Uprobes Patches
This patchset implements inode based uprobes which are specified as <file>:<offset> where offset is the offset from start of the map.

When a uprobe is registered, Uprobes makes a copy of the probed instruction, replaces the first byte(s) of the probed instruction with a breakpoint instruction. (Uprobes uses background page replacement mechanism and ensures that the breakpoint affects only that process.)

When a CPU hits the breakpoint instruction, Uprobes gets notified of trap and finds the associated uprobe. It then executes the associated handler. Uprobes single-steps its copy of the probed instruction and resumes execution of the probed process at the instruction following the probepoint. Instruction copies to be single-stepped are stored in a per-mm "execution out of line (XOL) area". Currently XOL area is allocated as one page vma.

For previous postings: please refer: https://lkml.org/lkml/2011/4/1/176
http://lkml.org/lkml/2011/3/14/171/ http://lkml.org/lkml/2010/12/16/65
http://lkml.org/lkml/2010/8/25/165 http://lkml.org/lkml/2010/7/27/121
http://lkml.org/lkml/2010/7/12/67 http://lkml.org/lkml/2010/7/8/239
http://lkml.org/lkml/2010/6/29/299 http://lkml.org/lkml/2010/6/14/41
http://lkml.org/lkml/2010/3/20/107 and http://lkml.org/lkml/2010/5/18/307

This patchset is a rework based on suggestions from discussions on lkml in September, March and January 2010 (http://lkml.org/lkml/2010/1/11/92,
http://lkml.org/lkml/2010/1/27/19, http://lkml.org/lkml/2010/3/20/107
and http://lkml.org/lkml/2010/3/31/199 ). This implementation of uprobes doesnt depend on utrace.

Advantages of uprobes over conventional debugging include:

1. Non-disruptive.
Unlike current ptrace based mechanisms, uprobes tracing wouldnt involve signals, stopping threads and context switching between the tracer and tracee.

2. Much better handling of multithreaded programs because of XOL. Current ptrace based mechanisms use single stepping inline, i.e they copy back the original instruction on hitting a breakpoint. In such mechanisms tracers have to stop all the threads on a breakpoint hit or tracers will not be able to handle all hits to the location of interest. Uprobes uses execution out of line, where the instruction to be traced is analysed at the time of breakpoint insertion and a copy of instruction is stored at a different location. On breakpoint hit, uprobes jumps to that copied location and singlesteps the same instruction and does the necessary fixups post singlestepping.

3. Multiple tracers for an application.
Multiple uprobes based tracer could work in unison to trace an application. There could one tracer that could be interested in generic events for a particular set of process. While there could be another tracer that is just interested in one specific event of a particular process thats part of the previous set of process.

4. Corelating events from kernels and userspace.
Uprobes could be used with other tools like kprobes, tracepoints or as part of higher level tools like perf to give a consolidated set of events from kernel and userspace. In future we could look at a single backtrace showing application, library and kernel calls.

Changes from last patchset:
- mmap_uprobe doesnt drop mmap_sem anymore
- Introduced uprobes_mmap_mutex to serialize mmap_uprobes.
- Uses i_mutex instead of uprobes_mutex.
- Introduces munmap_uprobes
- Change in perf probe interface as recommended by Masami.
- Doesnt depend on get_user_pages to do the COW.
- Slot allocation changed from per-task to shared slot allocation mechanism.

Here is the list of TODO Items.

- Prefiltering (i.e filtering at the time of probe insertion)
- Return probes.
- Support for other architectures.
- Uprobes booster.
- replace macro W with bits in inat table.

Please refer "[RFC] [PATCH 3.1-rc4-tip 21/26] tracing: tracing: Uprobe tracer documentation" on how to use uprobe_tracer.

Please refer "[RFC] [PATCH 3.1-rc4-tip 25/26] perf: Documentation for perf uprobes" on how to use uprobe_tracer.

Please do provide your valuable comments.

Thanks in advance.
Srikar
---

 0 files changed, 0 insertions(+), 0 deletions(-)

Srikar Dronamraju (26)
0: Uprobes patchset with perf probe support
1: uprobes: Auxillary routines to insert, find, delete uprobes 2: Uprobes: Allow multiple consumers for an uprobe.
3: Uprobes: register/unregister probes.
4: uprobes: Define hooks for mmap/munmap.
5: Uprobes: copy of the original instruction.
6: Uprobes: define fixups.
7: Uprobes: uprobes arch info
8: x86: analyze instruction and determine fixups.
9: Uprobes: Background page replacement.
10: x86: Set instruction pointer.
11: x86: Introduce TIF_UPROBE FLAG.
12: Uprobes: Handle breakpoint and Singlestep
13: x86: define a x86 specific exception notifier.
14: uprobe: register exception notifier
15: x86: Define x86_64 specific uprobe_task_arch_info structure 16: uprobes: Introduce uprobe_task_arch_info structure. 17: x86: arch specific hooks for pre/post singlestep handling. 18: uprobes: slot allocation.
19: tracing: Extract out common code for kprobes/uprobes traceevents. 20: tracing: uprobes trace_event interface
21: tracing: uprobes Documentation
22: perf: rename target_module to target
23: perf: perf interface for uprobes
24: perf: show possible probes in a given executable file or library. 25: perf: Documentation for perf uprobes
26: uprobes: queue signals while thread is singlestepping.

 Documentation/trace/uprobetracer.txt    |   94 ++
 arch/Kconfig                            |    4 +
 arch/x86/Kconfig                        |    3 +
 arch/x86/include/asm/thread_info.h      |    2 +
 arch/x86/include/asm/uprobes.h          |   54 ++
 arch/x86/kernel/Makefile                |    1 +
 arch/x86/kernel/signal.c                |   14 +
 arch/x86/kernel/uprobes.c               |  562 ++++++++++++
 include/linux/mm_types.h                |    5 +
 include/linux/sched.h                   |    3 +
 include/linux/uprobes.h                 |  165 ++++
 kernel/Makefile                         |    1 +
 kernel/fork.c                           |   11 +
 kernel/signal.c                         |   22 +-
 kernel/trace/Kconfig                    |   20 +
 kernel/trace/Makefile                   |    2 +
 kernel/trace/trace.h                    |    5 +
 kernel/trace/trace_kprobe.c             |  894 +------------------
 kernel/trace/trace_probe.c              |  784 ++++++++++++++++
 kernel/trace/trace_probe.h              |  162 ++++
 kernel/trace/trace_uprobe.c             |  770 ++++++++++++++++
 kernel/uprobes.c                        | 1475 +++++++++++++++++++++++++++++++
 mm/memory.c                             |    4 +
 mm/mmap.c                               |    6 +
 tools/perf/Documentation/perf-probe.txt |   14 +
 tools/perf/builtin-probe.c              |   49 +-
 tools/perf/util/probe-event.c           |  410 +++++++--
 tools/perf/util/probe-event.h           |   12 +-
 tools/perf/util/symbol.c                |   10 +-
 tools/perf/util/symbol.h                |    1 +
 30 files changed, 4581 insertions(+), 978 deletions(-)
#1 lore raw

PATCH v5 1/26 uprobes: Auxillary routines to insert, find, delete uprobes

Uprobes are maintained in a rb-tree indexed by inode and offset (offset from the start of the map). For a unique inode, offset combination, there can be one unique uprobe in the rbtree. Provide routines that insert a given uprobe, find a uprobe given a inode and offset.

---
 include/linux/uprobes.h |   35 +++++++++
 kernel/uprobes.c        |  174 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 209 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/uprobes.h
 create mode 100644 kernel/uprobes.c
include/linux/uprobes.h new file +35 -0
new file mode 100644
index 0000000..bfb85c4
@@ -0,0 +1,35 @@
+#ifndef _LINUX_UPROBES_H
+#define _LINUX_UPROBES_H
+/*
+ * Userspace Probes (UProbes)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) IBM Corporation, 2008-2011
+ * Authors:
+ *	Srikar Dronamraju
+ *	Jim Keniston
+ */
+
+#include <linux/rbtree.h>
+
+struct uprobe {
+	struct rb_node		rb_node;	/* node in the rb tree */
+	atomic_t		ref;
+	struct inode		*inode;		/* Also hold a ref to inode */
+	loff_t			offset;
+};
+
+#endif	/* _LINUX_UPROBES_H */
kernel/uprobes.c new file +174 -0

174 lines not on this page · raw message

#2 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 1/26] uprobes: Auxillary routines to insert, find, delete uprobes

kernel/uprobes.c line 173

Quoted 9 lines · Srikar Dronamraju
kernel/uprobes.c new file +9 -0
+static void delete_uprobe(struct uprobe *uprobe)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uprobes_treelock, flags);
+	rb_erase(&uprobe->rb_node, &uprobes_tree);
+	spin_unlock_irqrestore(&uprobes_treelock, flags);
+	put_uprobe(uprobe);
+	iput(uprobe->inode);

Use-after-free when put_uprobe() kfrees() the uprobe?

Stefan

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#3 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 1/26] uprobes: Auxillary routines to insert, find, delete uprobes

Quoted 12 lines · Stefan Hajnoczi

kernel/uprobes.c line 173

Quoted 9 lines · Srikar Dronamraju
kernel/uprobes.c new file +9 -0
+static void delete_uprobe(struct uprobe *uprobe)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uprobes_treelock, flags);
+	rb_erase(&uprobe->rb_node, &uprobes_tree);
+	spin_unlock_irqrestore(&uprobes_treelock, flags);
+	put_uprobe(uprobe);
+	iput(uprobe->inode);

Use-after-free when put_uprobe() kfrees() the uprobe?

I suspect the caller still has one, and this was the reference for being part of the tree. But yes, that could do with a comment.

The comment near atomic_set() in __insert_uprobe() isn't too clear either. /* get access + drop ref */, would naively seem +1 -1 = 0, instead of +1 +1 = 2.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#4 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 1/26] uprobes: Auxillary routines to insert, find, delete uprobes

Quoted 14 lines · Peter Zijlstra
Quoted 12 lines · Stefan Hajnoczi

kernel/uprobes.c line 173

Quoted 9 lines · Srikar Dronamraju
kernel/uprobes.c new file +9 -0
+static void delete_uprobe(struct uprobe *uprobe)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uprobes_treelock, flags);
+	rb_erase(&uprobe->rb_node, &uprobes_tree);
+	spin_unlock_irqrestore(&uprobes_treelock, flags);
+	put_uprobe(uprobe);
+	iput(uprobe->inode);

Use-after-free when put_uprobe() kfrees() the uprobe?

Quoted 3 lines

I suspect the caller still has one, and this was the reference for being part of the tree. But yes, that could do with a comment.

Yes, the caller has a reference, However I went ahead and changed the order of the last two statements.

Quoted 4 lines

The comment near atomic_set() in __insert_uprobe() isn't too clear either. /* get access + drop ref */, would naively seem +1 -1 = 0, instead of +1 +1 = 2.

Okay, Have modified the comment to /* get access + creation ref */

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

Re: [PATCH v5 3.1.0-rc4-tip 1/26] uprobes: Auxillary routines to insert, find, delete uprobes

In reply to Srikar Dronamraju
Quoted 19 lines · Srikar Dronamraju
kernel/uprobes.c new file +19 -0
+static struct uprobe *__insert_uprobe(struct uprobe *uprobe)
+{
+       struct rb_node **p = &uprobes_tree.rb_node;
+       struct rb_node *parent = NULL;
+       struct uprobe *u;
+       int match;
+
+       while (*p) {
+               parent = *p;
+               u = rb_entry(parent, struct uprobe, rb_node);
+               match = match_uprobe(uprobe, u);
+               if (!match) {
+                       atomic_inc(&u->ref);
+                       return u;
+               }
+
+               if (match < 0)
+                       p = &parent->rb_left;
+               else

kernel/uprobes.c line 115

Quoted 10 lines
kernel/uprobes.c new file +10 -0
+                       p = &parent->rb_right;
+
+       }
+       u = NULL;
+       rb_link_node(&uprobe->rb_node, parent, p);
+       rb_insert_color(&uprobe->rb_node, &uprobes_tree);
+       /* get access + drop ref */
+       atomic_set(&uprobe->ref, 2);
+       return u;
+} 

If you ever want to make a 'lockless' lookup work you need to set the refcount of the new object before its fully visible, instead of after.

Now much of a problem now since its fully serialized by that uprobes_treelock thing.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#6 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 1/26] uprobes: Auxillary routines to insert, find, delete uprobes

Quoted 25 lines · Peter Zijlstra
Quoted 24 lines · Srikar Dronamraju
kernel/uprobes.c new file +24 -0
+static struct uprobe *__insert_uprobe(struct uprobe *uprobe)
+{
+       struct rb_node **p = &uprobes_tree.rb_node;
+       struct rb_node *parent = NULL;
+       struct uprobe *u;
+       int match;
+
+       while (*p) {
+               parent = *p;
+               u = rb_entry(parent, struct uprobe, rb_node);
+               match = match_uprobe(uprobe, u);
+               if (!match) {
+                       atomic_inc(&u->ref);
+                       return u;
+               }
+
+               if (match < 0)
+                       p = &parent->rb_left;
+               else
+                       p = &parent->rb_right;
+
+       }
+       u = NULL;
+       rb_link_node(&uprobe->rb_node, parent, p);
Quoted 9 lines

kernel/uprobes.c line 115

Quoted 5 lines
kernel/uprobes.c new file +5 -0
+       rb_insert_color(&uprobe->rb_node, &uprobes_tree);
+       /* get access + drop ref */
+       atomic_set(&uprobe->ref, 2);
+       return u;
+} 

If you ever want to make a 'lockless' lookup work you need to set the refcount of the new object before its fully visible, instead of after.

Agree,

Quoted 3 lines

Now much of a problem now since its fully serialized by that uprobes_treelock thing.

Will stick with this for now; If and when we do a lockless lookup we could fix this.

Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#7 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 1/26] uprobes: Auxillary routines to insert, find, delete uprobes

In reply to Srikar Dronamraju
Quoted 1 line · Srikar Dronamraju

+static struct uprobe *__find_uprobe(struct inode * inode, loff_t offset)

Here and elsewhere, your whitespace is off, it should read:

struct inode *inode

I think checkpatch will inform you of this, but I didn't check.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#8 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 1/26] uprobes: Auxillary routines to insert, find, delete uprobes

Quoted 9 lines · Peter Zijlstra
Quoted 1 line · Srikar Dronamraju

+static struct uprobe *__find_uprobe(struct inode * inode, loff_t offset)

Here and elsewhere, your whitespace is off, it should read:

struct inode *inode

I think checkpatch will inform you of this, but I didn't check.

I have run checkpatch.pl --strict on all the patches and it doesnt report them.

However I do see these whitespace in three places definitions for write_opcode, __find_uprobe, and find_uprobe.

Signature · 2 lines
Thanks and Regards
Srikar
#9 lore raw

PATCH v5 2/26 Uprobes: Allow multiple consumers for an uprobe.

In reply to Srikar Dronamraju

Since there is a unique uprobe for a inode, offset combination, provide an ability for users to have more than one consumer for a uprobe.

Each consumer will define a handler and a filter. Handler specifies the routine to run on hitting a probepoint. Filter allows to selectively run the handler on hitting the probepoint. Handler/Filter will be relevant when we handle probehit.

---
 include/linux/uprobes.h |   13 +++++++++++++
 kernel/uprobes.c        |   41 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 54 insertions(+), 0 deletions(-)
include/linux/uprobes.h +13 -0
index bfb85c4..bf31f7c 100644
@@ -25,9 +25,22 @@
 
 #include <linux/rbtree.h>
 
+struct uprobe_consumer {
+	int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs);
+	/*
+	 * filter is optional; If a filter exists, handler is run
+	 * if and only if filter returns true.
+	 */
+	bool (*filter)(struct uprobe_consumer *self, struct task_struct *task);
+
+	struct uprobe_consumer *next;
+};
+
 struct uprobe {
 	struct rb_node		rb_node;	/* node in the rb tree */
 	atomic_t		ref;
+	struct rw_semaphore	consumer_rwsem;
+	struct uprobe_consumer	*consumers;
 	struct inode		*inode;		/* Also hold a ref to inode */
 	loff_t			offset;
 };
kernel/uprobes.c +41 -0

53 lines not on this page · raw message

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#10 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 2/26] Uprobes: Allow multiple consumers for an uprobe.

kernel/uprobes.c static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset) · line 201

Quoted 13 lines · Srikar Dronamraju
kernel/uprobes.c +13 -0
+       con = uprobe->consumers;
+       if (consumer == con) {
+               uprobe->consumers = con->next;
+               ret = true;
+       } else {
+               for (; con; con = con->next) {
+                       if (con->next == consumer) {
+                               con->next = consumer->next;
+                               ret = true;
+                               break;
+                       }
+               }
+       } 
	struct uprobe_consumer **next = &uprobe->consumers;

	for (; *next; next = &(*next)->next) {
		if (*next == consumer) {
			*next = (*next)->next;
			ret = true;
			break;
		}
	}

Wouldn't something like that work?

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#11 lore raw

PATCH v5 3/26 Uprobes: register/unregister probes.

In reply to Srikar Dronamraju

A probe is specified by a file:offset. While registering, a breakpoint is inserted for the first consumer, On subsequent probes, the consumer gets appended to the existing consumers. While unregistering a breakpoint is removed if the consumer happens to be the last consumer. All other unregisterations, the consumer is deleted from the list of consumers.

Probe specifications are maintained in a rb tree. A probe specification is converted into a uprobe before store in a rb tree. A uprobe can be shared by many consumers.

Given a inode, we get a list of mm's that have mapped the inode. However we want to limit the probes to certain processes/threads. The filtering should be at thread level. To limit the probes to a certain processes/threads, we would want to walk through the list of threads whose mm member refer to a given mm.

---
 arch/Kconfig            |    9 ++
 include/linux/uprobes.h |   16 +++
 kernel/Makefile         |    1 
 kernel/uprobes.c        |  263 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 289 insertions(+), 0 deletions(-)
arch/Kconfig +9 -0
index 4b0669c..dedd489 100644
@@ -61,6 +61,15 @@ config OPTPROBES
 	depends on KPROBES && HAVE_OPTPROBES
 	depends on !PREEMPT
 
+config UPROBES
+	bool "User-space probes (EXPERIMENTAL)"
+	help
+	  Uprobes enables kernel subsystems to establish probepoints
+	  in user applications and execute handler functions when
+	  the probepoints are hit.
+
+	  If in doubt, say "N".
+
 config HAVE_EFFICIENT_UNALIGNED_ACCESS
 	bool
 	help
include/linux/uprobes.h +16 -0
index bf31f7c..6d5a3fe 100644
@@ -45,4 +45,20 @@ struct uprobe {
 	loff_t			offset;
 };
 
+#ifdef CONFIG_UPROBES
+extern int register_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe_consumer *consumer);
+extern void unregister_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe_consumer *consumer);
+#else /* CONFIG_UPROBES is not defined */
+static inline int register_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe_consumer *consumer)
+{
+	return -ENOSYS;
+}
+static inline void unregister_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe_consumer *consumer)
+{
+}
+#endif /* CONFIG_UPROBES */
 #endif	/* _LINUX_UPROBES_H */
kernel/Makefile +1 -0
index eca595e..aa810c8 100644
@@ -108,6 +108,7 @@ obj-$(CONFIG_USER_RETURN_NOTIFIER) += user-return-notifier.o
 obj-$(CONFIG_PADATA) += padata.o
 obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
 obj-$(CONFIG_JUMP_LABEL) += jump_label.o
+obj-$(CONFIG_UPROBES) += uprobes.o
 
 ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y)
 # According to Alan Modra <[email protected]>, the -fno-omit-frame-pointer is
kernel/uprobes.c +263 -0

283 lines not on this page · raw message

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#12 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 3/26] Uprobes: register/unregister probes.

Quoted 7 lines · Srikar Dronamraju
kernel/uprobes.c +7 -0
+int register_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe_consumer *consumer)
+{
+	struct uprobe *uprobe;
+	int ret = 0;
+
+	inode = igrab(inode);

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 430

Quoted 10 lines
kernel/uprobes.c +10 -0
+	if (!inode || !consumer || consumer->next)
+		return -EINVAL;
+
+	if (offset > inode->i_size)
+		return -EINVAL;
+
+	mutex_lock(&inode->i_mutex);
+	uprobe = alloc_uprobe(inode, offset);
+	if (!uprobe)
+		return -ENOMEM;

The error returns above don't iput(inode). And inode->i_mutex stays locked on this return.

Quoted 10 lines
kernel/uprobes.c +10 -0
+void unregister_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe_consumer *consumer)
+{
+	struct uprobe *uprobe;
+
+	inode = igrab(inode);
+	if (!inode || !consumer)
+		return;
+
+	if (offset > inode->i_size)

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 469

Quoted 10 lines
kernel/uprobes.c +10 -0
+		return;
+
+	uprobe = find_uprobe(inode, offset);
+	if (!uprobe)
+		return;
+
+	if (!del_consumer(uprobe, consumer)) {
+		put_uprobe(uprobe);
+		return;
+	}

More returns that do not iput(inode).

Stefan

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#13 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 3/26] Uprobes: register/unregister probes.

Quoted 13 lines · Stefan Hajnoczi
Quoted 12 lines · Srikar Dronamraju
kernel/uprobes.c +12 -0
+int register_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe_consumer *consumer)
+{
+	struct uprobe *uprobe;
+	int ret = 0;
+
+	inode = igrab(inode);
+	if (!inode || !consumer || consumer->next)
+		return -EINVAL;
+
+	if (offset > inode->i_size)
+		return -EINVAL;
Quoted 8 lines

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 430

Quoted 5 lines
kernel/uprobes.c +5 -0
+
+	mutex_lock(&inode->i_mutex);
+	uprobe = alloc_uprobe(inode, offset);
+	if (!uprobe)
+		return -ENOMEM;

The error returns above don't iput(inode). And inode->i_mutex stays locked on this return.

Yes will fix this .. by clubbing the !uprobe with the next condition. Thanks for pointing this.

Quoted 15 lines
Quoted 15 lines
kernel/uprobes.c +14 -0
+void unregister_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe_consumer *consumer)
+{
+	struct uprobe *uprobe;
+
+	inode = igrab(inode);
+	if (!inode || !consumer)
+		return;
+
+	if (offset > inode->i_size)
+		return;
+
+	uprobe = find_uprobe(inode, offset);
+	if (!uprobe)
Quoted 8 lines

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 469

Quoted 6 lines
kernel/uprobes.c +6 -0
+		return;
+
+	if (!del_consumer(uprobe, consumer)) {
+		put_uprobe(uprobe);
+		return;
+	}

More returns that do not iput(inode).

Yes. will fix these too.

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

Re: [PATCH v5 3.1.0-rc4-tip 3/26] Uprobes: register/unregister probes.

In reply to Srikar Dronamraju
Quoted 20 lines · Srikar Dronamraju
kernel/uprobes.c +20 -0
+static struct vma_info *__find_next_vma_info(struct list_head *head,
+			loff_t offset, struct address_space *mapping,
+			struct vma_info *vi)
+{
+	struct prio_tree_iter iter;
+	struct vm_area_struct *vma;
+	struct vma_info *tmpvi;
+	loff_t vaddr;
+	unsigned long pgoff = offset >> PAGE_SHIFT;
+	int existing_vma;
+
+	vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) {
+		if (!vma || !valid_vma(vma))
+			return NULL;
+
+		existing_vma = 0;
+		vaddr = vma->vm_start + offset;
+		vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+		list_for_each_entry(tmpvi, head, probe_list) {
+			if (tmpvi->mm == vma->vm_mm && tmpvi->vaddr == vaddr) {

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 287

Quoted 10 lines
kernel/uprobes.c +10 -0
+				existing_vma = 1;
+				break;
+			}
+		}
+		if (!existing_vma &&
+				atomic_inc_not_zero(&vma->vm_mm->mm_users)) {
+			vi->mm = vma->vm_mm;
+			vi->vaddr = vaddr;
+			list_add(&vi->probe_list, head);
+			return vi;

The the sole purpose of actually having that list is the above linear was to test if we've already had this one?

Does that really matter? After all, if the probe is already installed installing it again will return with -EEXIST, which should be easy enough to deal with.

Quoted 8 lines
kernel/uprobes.c +8 -0
+		}
+	}
+	return NULL;
+}
+
+/*
+ * Iterate in the rmap prio tree  and find a vma where a probe has not
+ * yet been inserted.

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 305

Quoted 10 lines
kernel/uprobes.c +10 -0
+ */
+static struct vma_info *find_next_vma_info(struct list_head *head,
+			loff_t offset, struct address_space *mapping)
+{
+	struct vma_info *vi, *retvi;
+	vi = kzalloc(sizeof(struct vma_info), GFP_KERNEL);
+	if (!vi)
+		return ERR_PTR(-ENOMEM);
+
+	INIT_LIST_HEAD(&vi->probe_list);

weird place for the INIT_LIST_HEAD, I would have expected it near where the rest of vi is initialized, although it looks to be superfluous anyway, since list_add() can handle an uninitialized entry.

Quoted 17 lines
kernel/uprobes.c +17 -0
+	mutex_lock(&mapping->i_mmap_mutex);
+	retvi = __find_next_vma_info(head, offset, mapping, vi);
+	mutex_unlock(&mapping->i_mmap_mutex);
+
+	if (!retvi)
+		kfree(vi);
+	return retvi;
+}
+
+static int __register_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe *uprobe)
+{
+	struct list_head try_list;
+	struct vm_area_struct *vma;
+	struct address_space *mapping;
+	struct vma_info *vi, *tmpvi;
+	struct mm_struct *mm;

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 332

Quoted 10 lines
kernel/uprobes.c +10 -0
+	int ret = 0;
+
+	mapping = inode->i_mapping;
+	INIT_LIST_HEAD(&try_list);
+	while ((vi = find_next_vma_info(&try_list, offset,
+							mapping)) != NULL) {
+		if (IS_ERR(vi)) {
+			ret = -ENOMEM;
+			break;
+		}

Here we hold neither i_mmap_mutex nor mmap_sem, so everything can change under our feet. See below..

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 336

Quoted 4 lines
kernel/uprobes.c +4 -0
+		mm = vi->mm;
+		down_read(&mm->mmap_sem);
+		vma = find_vma(mm, (unsigned long) vi->vaddr);
+		if (!vma || !valid_vma(vma)) {

No validation if its indeed the same vma you found earlier? At the very least we should validate the vma returned from find_vma() is indeed a mapping of the inode we're after and that the offset is still to be found at vaddr.

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 348

Quoted 12 lines
kernel/uprobes.c +12 -0
+			list_del(&vi->probe_list);
+			kfree(vi);
+			up_read(&mm->mmap_sem);
+			mmput(mm);
+			continue;
+		}
+		ret = install_breakpoint(mm);
+		if (ret && (ret != -ESRCH || ret != -EEXIST)) {
+			up_read(&mm->mmap_sem);
+			mmput(mm);
+			break;
+		}

Right, so you already deal with -EEXIST, so why do we need that list at all then?

Aah, its to make fwd progress, without it we would keep retrying the same vma over and over,.. hmm?

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 358

Quoted 10 lines
kernel/uprobes.c +10 -0
+		ret = 0;
+		up_read(&mm->mmap_sem);
+		mmput(mm);
+	}
+	list_for_each_entry_safe(vi, tmpvi, &try_list, probe_list) {
+		list_del(&vi->probe_list);
+		kfree(vi);
+	}
+	return ret;
+}

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#15 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 3/26] Uprobes: register/unregister probes.

Quoted 36 lines · Peter Zijlstra
Quoted 20 lines · Srikar Dronamraju
kernel/uprobes.c +20 -0
+static struct vma_info *__find_next_vma_info(struct list_head *head,
+			loff_t offset, struct address_space *mapping,
+			struct vma_info *vi)
+{
+	struct prio_tree_iter iter;
+	struct vm_area_struct *vma;
+	struct vma_info *tmpvi;
+	loff_t vaddr;
+	unsigned long pgoff = offset >> PAGE_SHIFT;
+	int existing_vma;
+
+	vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) {
+		if (!vma || !valid_vma(vma))
+			return NULL;
+
+		existing_vma = 0;
+		vaddr = vma->vm_start + offset;
+		vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+		list_for_each_entry(tmpvi, head, probe_list) {
+			if (tmpvi->mm == vma->vm_mm && tmpvi->vaddr == vaddr) {

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 287

Quoted 10 lines
kernel/uprobes.c +10 -0
+				existing_vma = 1;
+				break;
+			}
+		}
+		if (!existing_vma &&
+				atomic_inc_not_zero(&vma->vm_mm->mm_users)) {
+			vi->mm = vma->vm_mm;
+			vi->vaddr = vaddr;
+			list_add(&vi->probe_list, head);
+			return vi;

The the sole purpose of actually having that list is the above linear was to test if we've already had this one?

Quoted 4 lines

Does that really matter? After all, if the probe is already installed installing it again will return with -EEXIST, which should be easy enough to deal with.

No, There is a possibility of going in a forever loop. Since the the priotree can change when we drop the mapping->mutex, we dont pass the hint to vma_prio_tree_foreach.
So we might keep getting the same vma again and again.

Quoted 55 lines
Quoted 8 lines
kernel/uprobes.c +8 -0
+		}
+	}
+	return NULL;
+}
+
+/*
+ * Iterate in the rmap prio tree  and find a vma where a probe has not
+ * yet been inserted.

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 305

Quoted 10 lines
kernel/uprobes.c +10 -0
+ */
+static struct vma_info *find_next_vma_info(struct list_head *head,
+			loff_t offset, struct address_space *mapping)
+{
+	struct vma_info *vi, *retvi;
+	vi = kzalloc(sizeof(struct vma_info), GFP_KERNEL);
+	if (!vi)
+		return ERR_PTR(-ENOMEM);
+
+	INIT_LIST_HEAD(&vi->probe_list);

weird place for the INIT_LIST_HEAD, I would have expected it near where the rest of vi is initialized, although it looks to be superfluous anyway, since list_add() can handle an uninitialized entry.

Quoted 17 lines
kernel/uprobes.c +17 -0
+	mutex_lock(&mapping->i_mmap_mutex);
+	retvi = __find_next_vma_info(head, offset, mapping, vi);
+	mutex_unlock(&mapping->i_mmap_mutex);
+
+	if (!retvi)
+		kfree(vi);
+	return retvi;
+}
+
+static int __register_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe *uprobe)
+{
+	struct list_head try_list;
+	struct vm_area_struct *vma;
+	struct address_space *mapping;
+	struct vma_info *vi, *tmpvi;
+	struct mm_struct *mm;

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 332

Quoted 10 lines
kernel/uprobes.c +10 -0
+	int ret = 0;
+
+	mapping = inode->i_mapping;
+	INIT_LIST_HEAD(&try_list);
+	while ((vi = find_next_vma_info(&try_list, offset,
+							mapping)) != NULL) {
+		if (IS_ERR(vi)) {
+			ret = -ENOMEM;
+			break;
+		}

Here we hold neither i_mmap_mutex nor mmap_sem, so everything can change under our feet. See below..

Quoted 10 lines

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 336

Quoted 4 lines
kernel/uprobes.c +4 -0
+		mm = vi->mm;
+		down_read(&mm->mmap_sem);
+		vma = find_vma(mm, (unsigned long) vi->vaddr);
+		if (!vma || !valid_vma(vma)) {

No validation if its indeed the same vma you found earlier? At the very least we should validate the vma returned from find_vma() is indeed a mapping of the inode we're after and that the offset is still to be found at vaddr.

Yes, this can be done.

Quoted 16 lines

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 348

Quoted 12 lines
kernel/uprobes.c +12 -0
+			list_del(&vi->probe_list);
+			kfree(vi);
+			up_read(&mm->mmap_sem);
+			mmput(mm);
+			continue;
+		}
+		ret = install_breakpoint(mm);
+		if (ret && (ret != -ESRCH || ret != -EEXIST)) {
+			up_read(&mm->mmap_sem);
+			mmput(mm);
+			break;
+		}

Right, so you already deal with -EEXIST, so why do we need that list at all then?

Quoted 3 lines

Aah, its to make fwd progress, without it we would keep retrying the same vma over and over,.. hmm?

Yes.

Quoted 11 lines
Quoted 10 lines
kernel/uprobes.c +10 -0
+		ret = 0;
+		up_read(&mm->mmap_sem);
+		mmput(mm);
+	}
+	list_for_each_entry_safe(vi, tmpvi, &try_list, probe_list) {
+		list_del(&vi->probe_list);
+		kfree(vi);
+	}
+	return ret;
+}
Signature · 2 lines
Thanks and Regards
Srikar
maintainer
#16 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 3/26] Uprobes: register/unregister probes.

In reply to Srikar Dronamraju
Quoted 5 lines · Srikar Dronamraju
kernel/uprobes.c +4 -0
+static struct vma_info *__find_next_vma_info(struct list_head *head,
+			loff_t offset, struct address_space *mapping,
+			struct vma_info *vi)
+{

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 271

Quoted 10 lines
kernel/uprobes.c +10 -0
+	struct prio_tree_iter iter;
+	struct vm_area_struct *vma;
+	struct vma_info *tmpvi;
+	loff_t vaddr;
+	unsigned long pgoff = offset >> PAGE_SHIFT;
+	int existing_vma;
+
+	vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) {
+		if (!vma || !valid_vma(vma))
+			return NULL;

!vma is not possible.

But I can't understand the !valid_vma(vma) check... We shouldn't return, we should ignore this vma and continue, no? Otherwise, I can't see how this can work if someone does, say, mmap(PROT_READ).

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 283

Quoted 11 lines
kernel/uprobes.c +11 -0
+		existing_vma = 0;
+		vaddr = vma->vm_start + offset;
+		vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+		list_for_each_entry(tmpvi, head, probe_list) {
+			if (tmpvi->mm == vma->vm_mm && tmpvi->vaddr == vaddr) {
+				existing_vma = 1;
+				break;
+			}
+		}
+		if (!existing_vma &&
+				atomic_inc_not_zero(&vma->vm_mm->mm_users)) {

This looks suspicious. If atomic_inc_not_zero() can fail, iow if we can see ->mm_users == 0, then why it is safe to touch this counter/memory? How we can know ->mm_count != 0 ?

I _think_ this is probably correct, ->mm_users == 0 means we are racing mmput(), ->i_mmap_mutex and the fact we found this vma guarantees that mmput() can't pass unlink_file_vma() and thus mmdrop() is not possible. May be needs a comment...

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 305

Quoted 9 lines
kernel/uprobes.c +9 -0
+static struct vma_info *find_next_vma_info(struct list_head *head,
+			loff_t offset, struct address_space *mapping)
+{
+	struct vma_info *vi, *retvi;
+	vi = kzalloc(sizeof(struct vma_info), GFP_KERNEL);
+	if (!vi)
+		return ERR_PTR(-ENOMEM);
+
+	INIT_LIST_HEAD(&vi->probe_list);

Looks unneeded.

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 308

Quoted 3 lines
kernel/uprobes.c +3 -0
+	mutex_lock(&mapping->i_mmap_mutex);
+	retvi = __find_next_vma_info(head, offset, mapping, vi);
+	mutex_unlock(&mapping->i_mmap_mutex);

It is not clear why we can't race with mmap() after find_next_vma_info() returns NULL. I guess this is solved by the next patches.

Quoted 11 lines
kernel/uprobes.c +11 -0
+static int __register_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe *uprobe)
+{
+	struct list_head try_list;
+	struct vm_area_struct *vma;
+	struct address_space *mapping;
+	struct vma_info *vi, *tmpvi;
+	struct mm_struct *mm;
+	int ret = 0;
+
+	mapping = inode->i_mapping;

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 335

Quoted 10 lines
kernel/uprobes.c +10 -0
+	INIT_LIST_HEAD(&try_list);
+	while ((vi = find_next_vma_info(&try_list, offset,
+							mapping)) != NULL) {
+		if (IS_ERR(vi)) {
+			ret = -ENOMEM;
+			break;
+		}
+		mm = vi->mm;
+		down_read(&mm->mmap_sem);
+		vma = find_vma(mm, (unsigned long) vi->vaddr);

But we can't trust find_vma? The original vma found by find_next_vma_info() could go away, at least we should verify vi->vaddr >= vm_start.

And worse, I do not understand how we can trust ->vaddr. Can't we race with sys_mremap() ?

Quoted 8 lines
kernel/uprobes.c +8 -0
+static void __unregister_uprobe(struct inode *inode, loff_t offset,
+						struct uprobe *uprobe)
+{
+	struct list_head try_list;
+	struct address_space *mapping;
+	struct vma_info *vi, *tmpvi;
+	struct vm_area_struct *vma;
+	struct mm_struct *mm;

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 377

Quoted 10 lines
kernel/uprobes.c +10 -0
+
+	mapping = inode->i_mapping;
+	INIT_LIST_HEAD(&try_list);
+	while ((vi = find_next_vma_info(&try_list, offset,
+							mapping)) != NULL) {
+		if (IS_ERR(vi))
+			break;
+		mm = vi->mm;
+		down_read(&mm->mmap_sem);
+		vma = find_vma(mm, (unsigned long) vi->vaddr);

Same problems...

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 342

Quoted 7 lines
kernel/uprobes.c +7 -0
+		if (!vma || !valid_vma(vma)) {
+			list_del(&vi->probe_list);
+			kfree(vi);
+			up_read(&mm->mmap_sem);
+			mmput(mm);
+			continue;
+		}

Not sure about !valid_vma() (and note that __find_next_vma_info does() this check too).

Suppose that register_uprobe() succeeds. After that unregister_ should work even if user-space does mprotect() which can make valid_vma() == F, right?

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 425

Quoted 12 lines
kernel/uprobes.c +12 -0
+int register_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe_consumer *consumer)
+{
+	struct uprobe *uprobe;
+	int ret = 0;
+
+	inode = igrab(inode);
+	if (!inode || !consumer || consumer->next)
+		return -EINVAL;
+
+	if (offset > inode->i_size)
+		return -EINVAL;

I guess this needs i_size_read().

And every "return" in register/unregister leaks something.

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 428

Quoted 3 lines
kernel/uprobes.c +3 -0
+
+	mutex_lock(&inode->i_mutex);
+	uprobe = alloc_uprobe(inode, offset);

Looks like, alloc_uprobe() doesn't need ->i_mutex.

OTOH,

Quoted 14 lines
kernel/uprobes.c +14 -0
+void unregister_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe_consumer *consumer)
+{
+	struct uprobe *uprobe;
+
+	inode = igrab(inode);
+	if (!inode || !consumer)
+		return;
+
+	if (offset > inode->i_size)
+		return;
+
+	uprobe = find_uprobe(inode, offset);
+	if (!uprobe)

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 473

Quoted 10 lines
kernel/uprobes.c +10 -0
+		return;
+
+	if (!del_consumer(uprobe, consumer)) {
+		put_uprobe(uprobe);
+		return;
+	}
+
+	mutex_lock(&inode->i_mutex);
+	if (!uprobe->consumers)
+		__unregister_uprobe(inode, offset, uprobe);

It seemes that del_consumer() should be done under ->i_mutex. If it removes the last consumer, we can race with register_uprobe() which takes ->i_mutex before us and does another __register_uprobe(), no?

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#17 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 3/26] Uprobes: register/unregister probes.

Quoted 19 lines · Oleg Nesterov
Quoted 5 lines · Srikar Dronamraju
kernel/uprobes.c +4 -0
+static struct vma_info *__find_next_vma_info(struct list_head *head,
+			loff_t offset, struct address_space *mapping,
+			struct vma_info *vi)
+{

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 271

Quoted 10 lines
kernel/uprobes.c +10 -0
+	struct prio_tree_iter iter;
+	struct vm_area_struct *vma;
+	struct vma_info *tmpvi;
+	loff_t vaddr;
+	unsigned long pgoff = offset >> PAGE_SHIFT;
+	int existing_vma;
+
+	vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) {
+		if (!vma || !valid_vma(vma))
+			return NULL;

!vma is not possible.

Quoted 3 lines

But I can't understand the !valid_vma(vma) check... We shouldn't return, we should ignore this vma and continue, no? Otherwise, I can't see how this can work if someone does, say, mmap(PROT_READ).

Agree. Infact I encountered this problem last week and had fixed it. In mycase, I had mapped the file read and write while trying to insert probes.
The changed code looks like this

	if (!vma) 
		return NULL;

	if (!valid_vma(vma))
		continue;
Quoted 34 lines

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 283

Quoted 12 lines
kernel/uprobes.c +11 -0
+		existing_vma = 0;
+		vaddr = vma->vm_start + offset;
+		vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+		list_for_each_entry(tmpvi, head, probe_list) {
+			if (tmpvi->mm == vma->vm_mm && tmpvi->vaddr == vaddr) {
+				existing_vma = 1;
+				break;
+			}
+		}
+		if (!existing_vma &&
+				atomic_inc_not_zero(&vma->vm_mm->mm_users)) {

This looks suspicious. If atomic_inc_not_zero() can fail, iow if we can see ->mm_users == 0, then why it is safe to touch this counter/memory? How we can know ->mm_count != 0 ?

I _think_ this is probably correct, ->mm_users == 0 means we are racing mmput(), ->i_mmap_mutex and the fact we found this vma guarantees that mmput() can't pass unlink_file_vma() and thus mmdrop() is not possible. May be needs a comment...

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 305

Quoted 9 lines
kernel/uprobes.c +9 -0
+static struct vma_info *find_next_vma_info(struct list_head *head,
+			loff_t offset, struct address_space *mapping)
+{
+	struct vma_info *vi, *retvi;
+	vi = kzalloc(sizeof(struct vma_info), GFP_KERNEL);
+	if (!vi)
+		return ERR_PTR(-ENOMEM);
+
+	INIT_LIST_HEAD(&vi->probe_list);

Looks unneeded.

Quoted 6 lines

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 308

Quoted 3 lines
kernel/uprobes.c +3 -0
+	mutex_lock(&mapping->i_mmap_mutex);
+	retvi = __find_next_vma_info(head, offset, mapping, vi);
+	mutex_unlock(&mapping->i_mmap_mutex);

It is not clear why we can't race with mmap() after find_next_vma_info() returns NULL. I guess this is solved by the next patches.

I assume mmap_uprobe() solves this.

Quoted 17 lines
Quoted 17 lines
kernel/uprobes.c +16 -0
+static int __register_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe *uprobe)
+{
+	struct list_head try_list;
+	struct vm_area_struct *vma;
+	struct address_space *mapping;
+	struct vma_info *vi, *tmpvi;
+	struct mm_struct *mm;
+	int ret = 0;
+
+	mapping = inode->i_mapping;
+	INIT_LIST_HEAD(&try_list);
+	while ((vi = find_next_vma_info(&try_list, offset,
+							mapping)) != NULL) {
+		if (IS_ERR(vi)) {
+			ret = -ENOMEM;
Quoted 8 lines

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 335

Quoted 5 lines
kernel/uprobes.c +5 -0
+			break;
+		}
+		mm = vi->mm;
+		down_read(&mm->mmap_sem);
+		vma = find_vma(mm, (unsigned long) vi->vaddr);

But we can't trust find_vma? The original vma found by find_next_vma_info() could go away, at least we should verify vi->vaddr >= vm_start.

Yes, Peter has already pointed this out and I have fixed this too. Should be fixed in the next iteration.

Quoted 36 lines

And worse, I do not understand how we can trust ->vaddr. Can't we race with sys_mremap() ?

Quoted 8 lines
kernel/uprobes.c +8 -0
+static void __unregister_uprobe(struct inode *inode, loff_t offset,
+						struct uprobe *uprobe)
+{
+	struct list_head try_list;
+	struct address_space *mapping;
+	struct vma_info *vi, *tmpvi;
+	struct vm_area_struct *vma;
+	struct mm_struct *mm;

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 377

Quoted 10 lines
kernel/uprobes.c +10 -0
+
+	mapping = inode->i_mapping;
+	INIT_LIST_HEAD(&try_list);
+	while ((vi = find_next_vma_info(&try_list, offset,
+							mapping)) != NULL) {
+		if (IS_ERR(vi))
+			break;
+		mm = vi->mm;
+		down_read(&mm->mmap_sem);
+		vma = find_vma(mm, (unsigned long) vi->vaddr);

Same problems...

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 342

Quoted 7 lines
kernel/uprobes.c +7 -0
+		if (!vma || !valid_vma(vma)) {
+			list_del(&vi->probe_list);
+			kfree(vi);
+			up_read(&mm->mmap_sem);
+			mmput(mm);
+			continue;
+		}

Not sure about !valid_vma() (and note that __find_next_vma_info does() this check too).

Quoted 3 lines

Suppose that register_uprobe() succeeds. After that unregister_ should work even if user-space does mprotect() which can make valid_vma() == F, right?

Agree, If we want __find_next_vma_info() also to not worry about valid_vma() while unregistering, then we would have to pass an additional parameter.

Quoted 6 lines
Quoted 6 lines
kernel/uprobes.c +6 -0
+int register_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe_consumer *consumer)
+{
+	struct uprobe *uprobe;
+	int ret = 0;
+
Quoted 8 lines

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 425

Quoted 6 lines
kernel/uprobes.c +6 -0
+	inode = igrab(inode);
+	if (!inode || !consumer || consumer->next)
+		return -EINVAL;
+
+	if (offset > inode->i_size)
+		return -EINVAL;

I guess this needs i_size_read().

Okay,

Quoted 2 lines

And every "return" in register/unregister leaks something.

Yes, this has been pointed out by Stefan Hajnoczi earlier. Have taken care of this.

Quoted 6 lines

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 428

Quoted 4 lines
kernel/uprobes.c +3 -0
+
+	mutex_lock(&inode->i_mutex);
+	uprobe = alloc_uprobe(inode, offset);

Looks like, alloc_uprobe() doesn't need ->i_mutex.

Actually this was pointed out by you in the last review.
https://lkml.org/lkml/2011/7/24/91

So if we alloc_uprobe() without a lock and succeed but while we contend on the lock , if the unregister can erase the uprobe from the rbtree. We end up with a valid uprobe but that is no more in the rbtree. right?

Quoted 23 lines

OTOH,

Quoted 20 lines
kernel/uprobes.c +20 -0
+void unregister_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe_consumer *consumer)
+{
+	struct uprobe *uprobe;
+
+	inode = igrab(inode);
+	if (!inode || !consumer)
+		return;
+
+	if (offset > inode->i_size)
+		return;
+
+	uprobe = find_uprobe(inode, offset);
+	if (!uprobe)
+		return;
+
+	if (!del_consumer(uprobe, consumer)) {
+		put_uprobe(uprobe);
+		return;
+	}
Quoted 8 lines

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 473

Quoted 4 lines
kernel/uprobes.c +4 -0
+
+	mutex_lock(&inode->i_mutex);
+	if (!uprobe->consumers)
+		__unregister_uprobe(inode, offset, uprobe);

It seemes that del_consumer() should be done under ->i_mutex. If it removes the last consumer, we can race with register_uprobe() which takes ->i_mutex before us and does another __register_uprobe(), no?

We should still be okay, because we check for the consumers before we do the actual unregister in form of __unregister_uprobe. since the consumer is again added by the time we get the lock, we dont do the actual unregistration and go as if del_consumer deleted one consumer but not the last.

or Am I missing something?

Signature · 2 lines
Thanks and Regards
Srikar
Quoted 3 lines

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#18 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 3/26] Uprobes: register/unregister probes.

Quoted 8 lines · Srikar Dronamraju

Agree. Infact I encountered this problem last week and had fixed it. In mycase, I had mapped the file read and write while trying to insert probes.
The changed code looks like this

	if (!vma)
		return NULL;

This is unneeded, vma_prio_tree_foreach() stops when vma_prio_tree_next() returns NULL. IOW, you can never see vma == NULL.

Quoted 2 lines
	if (!valid_vma(vma))
		continue;

Yes.

Quoted 8 lines
Quoted 4 lines

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 428

Quoted 2 lines
kernel/uprobes.c +2 -0
+	mutex_lock(&inode->i_mutex);
+	uprobe = alloc_uprobe(inode, offset);

Looks like, alloc_uprobe() doesn't need ->i_mutex.

Actually this was pointed out by you in the last review.
https://lkml.org/lkml/2011/7/24/91

OOPS ;) may be deserves a comment...

Quoted 29 lines
Quoted 20 lines
Quoted 20 lines
kernel/uprobes.c +20 -0
+void unregister_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe_consumer *consumer)
+{
+	struct uprobe *uprobe;
+
+	inode = igrab(inode);
+	if (!inode || !consumer)
+		return;
+
+	if (offset > inode->i_size)
+		return;
+
+	uprobe = find_uprobe(inode, offset);
+	if (!uprobe)
+		return;
+
+	if (!del_consumer(uprobe, consumer)) {
+		put_uprobe(uprobe);
+		return;
+	}
Quoted 8 lines

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 473

Quoted 4 lines
kernel/uprobes.c +4 -0
+
+	mutex_lock(&inode->i_mutex);
+	if (!uprobe->consumers)
+		__unregister_uprobe(inode, offset, uprobe);

It seemes that del_consumer() should be done under ->i_mutex. If it removes the last consumer, we can race with register_uprobe() which takes ->i_mutex before us and does another __register_uprobe(), no?

Quoted 5 lines

We should still be okay, because we check for the consumers before we do the actual unregister in form of __unregister_uprobe. since the consumer is again added by the time we get the lock, we dont do the actual unregistration and go as if del_consumer deleted one consumer but not the last.

Yes, but I meant in this case register_uprobe() does the unnecessary __register_uprobe() because it sees ->consumers == NULL (add_consumer() returns NULL).

I guess this is probably harmless because of is_bkpt_insn/-EEXIST logic, but still.

Btw. __register_uprobe() does

		ret = install_breakpoint(mm, uprobe, vma, vi->vaddr);
		if (ret && (ret != -ESRCH || ret != -EEXIST)) {
			up_read(&mm->mmap_sem);
			mmput(mm);
			break;
		}
		ret = 0;
		up_read(&mm->mmap_sem);
		mmput(mm);

Yes, this is cosmetic, but why do we duplicate up_read/mmput ?

Up to you, but

		ret = install_breakpoint(mm, uprobe, vma, vi->vaddr);
		up_read(&mm->mmap_sem);
		mmput(mm);

		if (ret) {
			if (ret != -ESRCH && ret != -EEXIST)
				break;
			ret = 0;
		}

Looks a bit simpler.

Oh, wait. I just noticed that the original code does

	(ret != -ESRCH || ret != -EEXIST)

this expression is always true ;)

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#19 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 3/26] Uprobes: register/unregister probes.

Quoted 7 lines · Oleg Nesterov
Quoted 6 lines · Srikar Dronamraju

Agree. Infact I encountered this problem last week and had fixed it. In mycase, I had mapped the file read and write while trying to insert probes.
The changed code looks like this

Quoted 5 lines
Quoted 2 lines
	if (!vma)
		return NULL;

This is unneeded, vma_prio_tree_foreach() stops when vma_prio_tree_next() returns NULL. IOW, you can never see vma == NULL.

Agree.

Quoted 12 lines
Quoted 3 lines
	if (!valid_vma(vma))
		continue;

Yes.

Quoted 6 lines
Quoted 4 lines

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 428

Quoted 2 lines
kernel/uprobes.c +2 -0
+	mutex_lock(&inode->i_mutex);
+	uprobe = alloc_uprobe(inode, offset);

Looks like, alloc_uprobe() doesn't need ->i_mutex.

Quoted 4 lines
Quoted 2 lines

Actually this was pointed out by you in the last review.
https://lkml.org/lkml/2011/7/24/91

OOPS ;) may be deserves a comment...

will add a comment.

Quoted 36 lines
Quoted 30 lines
Quoted 21 lines
Quoted 21 lines
kernel/uprobes.c +20 -0
+void unregister_uprobe(struct inode *inode, loff_t offset,
+				struct uprobe_consumer *consumer)
+{
+	struct uprobe *uprobe;
+
+	inode = igrab(inode);
+	if (!inode || !consumer)
+		return;
+
+	if (offset > inode->i_size)
+		return;
+
+	uprobe = find_uprobe(inode, offset);
+	if (!uprobe)
+		return;
+
+	if (!del_consumer(uprobe, consumer)) {
+		put_uprobe(uprobe);
+		return;
+	}
Quoted 8 lines

kernel/uprobes.c static void delete_uprobe(struct uprobe *uprobe) · line 473

Quoted 4 lines
kernel/uprobes.c +4 -0
+
+	mutex_lock(&inode->i_mutex);
+	if (!uprobe->consumers)
+		__unregister_uprobe(inode, offset, uprobe);

It seemes that del_consumer() should be done under ->i_mutex. If it removes the last consumer, we can race with register_uprobe() which takes ->i_mutex before us and does another __register_uprobe(), no?

Quoted 5 lines

We should still be okay, because we check for the consumers before we do the actual unregister in form of __unregister_uprobe. since the consumer is again added by the time we get the lock, we dont do the actual unregistration and go as if del_consumer deleted one consumer but not the last.

Quoted 3 lines

Yes, but I meant in this case register_uprobe() does the unnecessary __register_uprobe() because it sees ->consumers == NULL (add_consumer() returns NULL).

yes we might be doing an unnecessary __register_uprobe() but because it raced with unregister_uprobe() and got the lock, we would avoid doing a __unregister_uprobe().

However I am okay to move the lock before del_consumer(). Please let me know how you prefer this.

Quoted 4 lines

I guess this is probably harmless because of is_bkpt_insn/-EEXIST logic, but still.

Agree.

Quoted 27 lines

Btw. __register_uprobe() does

		ret = install_breakpoint(mm, uprobe, vma, vi->vaddr);
		if (ret && (ret != -ESRCH || ret != -EEXIST)) {
			up_read(&mm->mmap_sem);
			mmput(mm);
			break;
		}
		ret = 0;
		up_read(&mm->mmap_sem);
		mmput(mm);

Yes, this is cosmetic, but why do we duplicate up_read/mmput ?

Up to you, but

		ret = install_breakpoint(mm, uprobe, vma, vi->vaddr);
		up_read(&mm->mmap_sem);
		mmput(mm);

		if (ret) {
			if (ret != -ESRCH && ret != -EEXIST)
				break;
			ret = 0;
		}
Quoted 1 line

Looks a bit simpler.

Okay, will do.

Quoted 6 lines

Oh, wait. I just noticed that the original code does

	(ret != -ESRCH || ret != -EEXIST)

this expression is always true ;)

Right, will correct this.

Quoted 3 lines

Oleg.

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

Re: [PATCH v5 3.1.0-rc4-tip 3/26] Uprobes: register/unregister probes.

Quoted 8 lines · Srikar Dronamraju

* Oleg Nesterov <[email protected]> [2011-10-05 20:50:08]:

yes we might be doing an unnecessary __register_uprobe() but because it raced with unregister_uprobe() and got the lock, we would avoid doing a __unregister_uprobe().

However I am okay to move the lock before del_consumer().

To me this looks a bit "safer" even if currently __register is idempotent.

But,

Quoted 2 lines

Please let me
know how you prefer this.

No, no, Srikar. Please do what you prefer. You are the author.

And btw I forgot to mention that initially I wrongly thought this is buggy.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#21 lore raw

PATCH v5 4/26 uprobes: Define hooks for mmap/munmap.

In reply to Srikar Dronamraju

If an executable vma is getting mapped, search and insert corresponding probes. On unmap, make sure the per-mm count is decremented by appropriate amount.

---
 include/linux/mm_types.h |    3 +
 include/linux/uprobes.h  |   12 +++
 kernel/fork.c            |    5 +
 kernel/uprobes.c         |  174 +++++++++++++++++++++++++++++++++++++++++++---
 mm/memory.c              |    4 +
 mm/mmap.c                |    6 ++
 6 files changed, 194 insertions(+), 10 deletions(-)
include/linux/mm_types.h +3 -0
index 774b895..9aeb64f 100644
@@ -349,6 +349,9 @@ struct mm_struct {
 #ifdef CONFIG_CPUMASK_OFFSTACK
 	struct cpumask cpumask_allocation;
 #endif
+#ifdef CONFIG_UPROBES
+	atomic_t mm_uprobes_count;
+#endif
 };
 
 static inline void mm_init_cpumask(struct mm_struct *mm)
include/linux/uprobes.h +12 -0
index 6d5a3fe..b4de058 100644
@@ -25,6 +25,8 @@
 
 #include <linux/rbtree.h>
 
+struct vm_area_struct;
+
 struct uprobe_consumer {
 	int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs);
 	/*
@@ -40,6 +42,7 @@ struct uprobe {
 	struct rb_node		rb_node;	/* node in the rb tree */
 	atomic_t		ref;
 	struct rw_semaphore	consumer_rwsem;
+	struct list_head	pending_list;
 	struct uprobe_consumer	*consumers;
 	struct inode		*inode;		/* Also hold a ref to inode */
 	loff_t			offset;
@@ -50,6 +53,8 @@ extern int register_uprobe(struct inode *inode, loff_t offset,
 				struct uprobe_consumer *consumer);
 extern void unregister_uprobe(struct inode *inode, loff_t offset,
 				struct uprobe_consumer *consumer);
+extern int mmap_uprobe(struct vm_area_struct *vma);
+extern void munmap_uprobe(struct vm_area_struct *vma);
 #else /* CONFIG_UPROBES is not defined */
 static inline int register_uprobe(struct inode *inode, loff_t offset,
 				struct uprobe_consumer *consumer)
@@ -60,5 +65,12 @@ static inline void unregister_uprobe(struct inode *inode, loff_t offset,
 				struct uprobe_consumer *consumer)
 {
 }
+static inline int mmap_uprobe(struct vm_area_struct *vma)
+{
+	return 0;
+}
+static inline void munmap_uprobe(struct vm_area_struct *vma)
+{
+}
 #endif /* CONFIG_UPROBES */
 #endif	/* _LINUX_UPROBES_H */
kernel/fork.c +5 -0
index 8e6b6f4..7cc0b51 100644
@@ -66,6 +66,7 @@
 #include <linux/user-return-notifier.h>
 #include <linux/oom.h>
 #include <linux/khugepaged.h>
+#include <linux/uprobes.h>
 
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
@@ -739,6 +740,10 @@ struct mm_struct *dup_mm(struct task_struct *tsk)
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 	mm->pmd_huge_pte = NULL;
 #endif
+#ifdef CONFIG_UPROBES
+	atomic_set(&mm->mm_uprobes_count,
+			atomic_read(&oldmm->mm_uprobes_count));
+#endif
 
 	if (!mm_init(mm, tsk))
 		goto fail_nomem;
kernel/uprobes.c +164 -10

251 lines not on this page · raw message

mm/memory.c +4 -0
index a56e3ba..a65fd1f 100644
@@ -57,6 +57,7 @@
 #include <linux/swapops.h>
 #include <linux/elf.h>
 #include <linux/gfp.h>
+#include <linux/uprobes.h>
 
 #include <asm/io.h>
 #include <asm/pgalloc.h>
@@ -1337,6 +1338,9 @@ unsigned long unmap_vmas(struct mmu_gather *tlb,
 		if (unlikely(is_pfn_mapping(vma)))
 			untrack_pfn_vma(vma, 0, 0);
 
+		if (vma->vm_file)
+			munmap_uprobe(vma);
+
 		while (start != end) {
 			if (unlikely(is_vm_hugetlb_page(vma))) {
 				/*
mm/mmap.c +6 -0
index a65efd4..f51d482 100644
@@ -30,6 +30,7 @@
 #include <linux/perf_event.h>
 #include <linux/audit.h>
 #include <linux/khugepaged.h>
+#include <linux/uprobes.h>
 
 #include <asm/uaccess.h>
 #include <asm/cacheflush.h>
@@ -1329,6 +1330,11 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
 			mm->locked_vm += (len >> PAGE_SHIFT);
 	} else if ((flags & MAP_POPULATE) && !(flags & MAP_NONBLOCK))
 		make_pages_present(addr, addr + len);
+
+	if (file && mmap_uprobe(vma))
+		/* matching probes but cannot insert */
+		goto unmap_and_free_vma;
+
 	return addr;
 
 unmap_and_free_vma:

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#22 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 4/26] uprobes: Define hooks for mmap/munmap.

kernel/uprobes.c void unregister_uprobe(struct inode *inode, loff_t offset, · line 512

Quoted 8 lines · Srikar Dronamraju
kernel/uprobes.c +8 -0
+static void build_probe_list(struct inode *inode, struct list_head *head)
+{
+	struct uprobe *uprobe;
+	struct rb_node *n;
+	unsigned long flags;
+
+	n = uprobes_tree.rb_node;
+	spin_lock_irqsave(&uprobes_treelock, flags);

Not sure whether grabbing root.rb_node outside the spinlock is safe? If the tree is rotated on another CPU you could catch and out-of-date node?

kernel/uprobes.c void unregister_uprobe(struct inode *inode, loff_t offset, · line 588

Quoted 9 lines
kernel/uprobes.c +9 -0
+static void dec_mm_uprobes_count(struct vm_area_struct *vma,
+		struct inode *inode)
+{
+	struct uprobe *uprobe;
+	struct rb_node *n;
+	unsigned long flags;
+
+	n = uprobes_tree.rb_node;
+	spin_lock_irqsave(&uprobes_treelock, flags);

Same here.

Stefan

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#23 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 4/26] uprobes: Define hooks for mmap/munmap.

Quoted 12 lines · Stefan Hajnoczi

kernel/uprobes.c void unregister_uprobe(struct inode *inode, loff_t offset, · line 512

Quoted 8 lines · Srikar Dronamraju
kernel/uprobes.c +8 -0
+static void build_probe_list(struct inode *inode, struct list_head *head)
+{
+	struct uprobe *uprobe;
+	struct rb_node *n;
+	unsigned long flags;
+
+	n = uprobes_tree.rb_node;
+	spin_lock_irqsave(&uprobes_treelock, flags);

Not sure whether grabbing root.rb_node outside the spinlock is safe? If the tree is rotated on another CPU you could catch and out-of-date node?

Agree that its better to access the node in the spinlock. Shall correct this.

Quoted 11 lines

kernel/uprobes.c void unregister_uprobe(struct inode *inode, loff_t offset, · line 588

Quoted 9 lines
kernel/uprobes.c +9 -0
+static void dec_mm_uprobes_count(struct vm_area_struct *vma,
+		struct inode *inode)
+{
+	struct uprobe *uprobe;
+	struct rb_node *n;
+	unsigned long flags;
+
+	n = uprobes_tree.rb_node;
+	spin_lock_irqsave(&uprobes_treelock, flags);

Same here.

Okay.

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

Re: [PATCH v5 3.1.0-rc4-tip 4/26] uprobes: Define hooks for mmap/munmap.

In reply to Srikar Dronamraju
Quoted 32 lines
kernel/uprobes.c +13 -2
-static int match_uprobe(struct uprobe *l, struct uprobe *r)
+static int match_uprobe(struct uprobe *l, struct uprobe *r, int *match_inode)
 {
+	/*
+	 * if match_inode is non NULL then indicate if the
+	 * inode atleast match.
+	 */
+	if (match_inode)
+		*match_inode = 0;
+
 	if (l->inode < r->inode)
 		return -1;
 	if (l->inode > r->inode)
 		return 1;
 	else {
+		if (match_inode)
+			*match_inode = 1;
+
 		if (l->offset < r->offset)
 			return -1;
@@ -75,16 +86,20 @@ static int match_uprobe(struct uprobe *l, struct uprobe *r)
 	return 0;
 }
 
-static struct uprobe *__find_uprobe(struct inode * inode, loff_t offset)
+static struct uprobe *__find_uprobe(struct inode * inode, loff_t offset,
+					struct rb_node **close_match)
 {
 	struct uprobe u = { .inode = inode, .offset = offset };
 	struct rb_node *n = uprobes_tree.rb_node;

kernel/uprobes.c static int match_uprobe(struct uprobe *l, struct uprobe *r) · line 101

Quoted 10 lines
kernel/uprobes.c +4 -2
 	struct uprobe *uprobe;
-	int match;
+	int match, match_inode;
 
 	while (n) {
 		uprobe = rb_entry(n, struct uprobe, rb_node);
-		match = match_uprobe(&u, uprobe);
+		match = match_uprobe(&u, uprobe, &match_inode);
+		if (close_match && match_inode)
+			*close_match = n;

Because:

		if (close_match && uprobe->inode == inode)

Isn't good enough? Also, returning an rb_node just seems iffy..

kernel/uprobes.c static int match_uprobe(struct uprobe *l, struct uprobe *r) · line 105

Quoted 3 lines
kernel/uprobes.c +0 -0
 		if (!match) {
 			atomic_inc(&uprobe->ref);
 			return uprobe;

Why not something like:

+static struct uprobe *__find_uprobe(struct inode * inode, loff_t offset,
bool inode_only)

+{
        struct uprobe u = { .inode = inode, .offset = inode_only ? 0 : offset };
+       struct rb_node *n = uprobes_tree.rb_node;
+       struct uprobe *uprobe;
	struct uprobe *ret = NULL;
+       int match;
+
+       while (n) {
+               uprobe = rb_entry(n, struct uprobe, rb_node);
+               match = match_uprobe(&u, uprobe);
+               if (!match) {
			if (!inode_only)
	                       atomic_inc(&uprobe->ref);
+                       return uprobe;
+               }
		if (inode_only && uprobe->inode == inode)
			ret = uprobe;

+ if (match < 0)
+ n = n->rb_left;
+ else
+ n = n->rb_right;
+
+ }

        return ret;

+}

Quoted 10 lines
kernel/uprobes.c +10 -0
+/*
+ * For a given inode, build a list of probes that need to be inserted.
+ */
+static void build_probe_list(struct inode *inode, struct list_head *head)
+{
+	struct uprobe *uprobe;
+	struct rb_node *n;
+	unsigned long flags;
+
+	n = uprobes_tree.rb_node;

kernel/uprobes.c void unregister_uprobe(struct inode *inode, loff_t offset, · line 519

Quoted 10 lines
kernel/uprobes.c +8 -0
+	spin_lock_irqsave(&uprobes_treelock, flags);
+	uprobe = __find_uprobe(inode, 0, &n);
 
 
+	/*
+	 * If indeed there is a probe for the inode and with offset zero,
+	 * then lets release its reference. (ref got thro __find_uprobe)
+	 */
+	if (uprobe)
+		put_uprobe(uprobe);

The above would make this ^ unneeded.

	n = &uprobe->rb_node;

kernel/uprobes.c void unregister_uprobe(struct inode *inode, loff_t offset, · line 528

Quoted 9 lines
kernel/uprobes.c +9 -0
+	for (; n; n = rb_next(n)) {
+		uprobe = rb_entry(n, struct uprobe, rb_node);
+		if (uprobe->inode != inode)
+			break;
+		list_add(&uprobe->pending_list, head);
+		atomic_inc(&uprobe->ref);
+	}
+	spin_unlock_irqrestore(&uprobes_treelock, flags);
+}

If this ever gets to be a latency issue (linear lookup under spinlock) you can use a double lock (mutex+spinlock) and require that modification acquires both but lookups can get away with either.

That way you can do the linear search using a mutex instead of the spinlock.

Quoted 94 lines
kernel/uprobes.c +94 -0
+
+/*
+ * Called from mmap_region.
+ * called with mm->mmap_sem acquired.
+ *
+ * Return -ve no if we fail to insert probes and we cannot
+ * bail-out.
+ * Return 0 otherwise. i.e :
+ *	- successful insertion of probes
+ *	- (or) no possible probes to be inserted.
+ *	- (or) insertion of probes failed but we can bail-out.
+ */
+int mmap_uprobe(struct vm_area_struct *vma)
+{
+	struct list_head tmp_list;
+	struct uprobe *uprobe, *u;
+	struct inode *inode;
+	int ret = 0;
+
+	if (!valid_vma(vma))
+		return ret;	/* Bail-out */
+
+	inode = igrab(vma->vm_file->f_mapping->host);
+	if (!inode)
+		return ret;
+
+	INIT_LIST_HEAD(&tmp_list);
+	mutex_lock(&uprobes_mmap_mutex);
+	build_probe_list(inode, &tmp_list);
+	list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) {
+		loff_t vaddr;
+
+		list_del(&uprobe->pending_list);
+		if (!ret && uprobe->consumers) {
+			vaddr = vma->vm_start + uprobe->offset;
+			vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+			if (vaddr < vma->vm_start || vaddr >= vma->vm_end)
+				continue;
+			ret = install_breakpoint(vma->vm_mm, uprobe);
+
+			if (ret && (ret == -ESRCH || ret == -EEXIST))
+				ret = 0;
+		}
+		put_uprobe(uprobe);
+	}
+
+	mutex_unlock(&uprobes_mmap_mutex);
+	iput(inode);
+	return ret;
+}
+
+static void dec_mm_uprobes_count(struct vm_area_struct *vma,
+		struct inode *inode)
+{
+	struct uprobe *uprobe;
+	struct rb_node *n;
+	unsigned long flags;
+
+	n = uprobes_tree.rb_node;
+	spin_lock_irqsave(&uprobes_treelock, flags);
+	uprobe = __find_uprobe(inode, 0, &n);
+
+	/*
+	 * If indeed there is a probe for the inode and with offset zero,
+	 * then lets release its reference. (ref got thro __find_uprobe)
+	 */
+	if (uprobe)
+		put_uprobe(uprobe);
+	for (; n; n = rb_next(n)) {
+		loff_t vaddr;
+
+		uprobe = rb_entry(n, struct uprobe, rb_node);
+		if (uprobe->inode != inode)
+			break;
+		vaddr = vma->vm_start + uprobe->offset;
+		vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+		if (vaddr < vma->vm_start || vaddr >= vma->vm_end)
+			continue;
+		atomic_dec(&vma->vm_mm->mm_uprobes_count);
+	}
+	spin_unlock_irqrestore(&uprobes_treelock, flags);
+}
+
+/*
+ * Called in context of a munmap of a vma.
+ */
+void munmap_uprobe(struct vm_area_struct *vma)
+{
+	struct inode *inode;
+
+	if (!valid_vma(vma))
+		return;		/* Bail-out */
+
+	if (!atomic_read(&vma->vm_mm->mm_uprobes_count))

kernel/uprobes.c void unregister_uprobe(struct inode *inode, loff_t offset, · line 632

Quoted 10 lines
kernel/uprobes.c +10 -0
+		return;
+
+	inode = igrab(vma->vm_file->f_mapping->host);
+	if (!inode)
+		return;
+
+	dec_mm_uprobes_count(vma, inode);
+	iput(inode);
+	return;
+}

One has to wonder why mmap_uprobe() can be one function but munmap_uprobe() cannot.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#25 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 4/26] uprobes: Define hooks for mmap/munmap.

Quoted 20 lines
Quoted 7 lines
kernel/uprobes.c +2 -1
-static struct uprobe *__find_uprobe(struct inode * inode, loff_t offset)
+static struct uprobe *__find_uprobe(struct inode * inode, loff_t offset,
+					struct rb_node **close_match)
 {
 	struct uprobe u = { .inode = inode, .offset = offset };
 	struct rb_node *n = uprobes_tree.rb_node;

kernel/uprobes.c static int match_uprobe(struct uprobe *l, struct uprobe *r) · line 101

Quoted 10 lines
kernel/uprobes.c +4 -2
 	struct uprobe *uprobe;
-	int match;
+	int match, match_inode;
 
 	while (n) {
 		uprobe = rb_entry(n, struct uprobe, rb_node);
-		match = match_uprobe(&u, uprobe);
+		match = match_uprobe(&u, uprobe, &match_inode);
+		if (close_match && match_inode)
+			*close_match = n;

Because:

Quoted 3 lines
		if (close_match && uprobe->inode == inode)

Isn't good enough? Also, returning an rb_node just seems iffy..

yup this can be done. can you please elaborate on why passing back an rb_node is an issue?

Quoted 29 lines

kernel/uprobes.c static int match_uprobe(struct uprobe *l, struct uprobe *r) · line 105

Quoted 4 lines
kernel/uprobes.c +0 -0
 		if (!match) {
 			atomic_inc(&uprobe->ref);
 			return uprobe;

Why not something like:

+static struct uprobe *__find_uprobe(struct inode * inode, loff_t offset,
bool inode_only)

kernel/uprobes.c +3 -0
+{
        struct uprobe u = { .inode = inode, .offset = inode_only ? 0 : offset };
+       struct rb_node *n = uprobes_tree.rb_node;
+       struct uprobe *uprobe;
	struct uprobe *ret = NULL;
(quoted diff) +6 -0
+       int match;
+
+       while (n) {
+               uprobe = rb_entry(n, struct uprobe, rb_node);
+               match = match_uprobe(&u, uprobe);
+               if (!match) {
			if (!inode_only)
	                       atomic_inc(&uprobe->ref);
(quoted diff) +2 -0
+                       return uprobe;
+               }
		if (inode_only && uprobe->inode == inode)
			ret = uprobe;

Review

Quoted 8 lines
(quoted diff) +7 -0
+               if (match < 0)
+                       n = n->rb_left;
+               else
+                       n = n->rb_right;
+
+       }
        return ret;
+}

I am not comfortable with this change.
find_uprobe() was suppose to return back a uprobe if and only if the inode and offset match, However with your approach, we end up returning a uprobe that isnt matching and one that isnt refcounted. Moreover if even if we have a matching uprobe, we end up sending a unrefcounted uprobe back.

Quoted 40 lines
Quoted 11 lines
kernel/uprobes.c +10 -0
+/*
+ * For a given inode, build a list of probes that need to be inserted.
+ */
+static void build_probe_list(struct inode *inode, struct list_head *head)
+{
+	struct uprobe *uprobe;
+	struct rb_node *n;
+	unsigned long flags;
+
+	n = uprobes_tree.rb_node;

kernel/uprobes.c void unregister_uprobe(struct inode *inode, loff_t offset, · line 519

Quoted 10 lines
kernel/uprobes.c +8 -0
+	spin_lock_irqsave(&uprobes_treelock, flags);
+	uprobe = __find_uprobe(inode, 0, &n);
 
 
+	/*
+	 * If indeed there is a probe for the inode and with offset zero,
+	 * then lets release its reference. (ref got thro __find_uprobe)
+	 */
+	if (uprobe)
+		put_uprobe(uprobe);

The above would make this ^ unneeded.

	n = &uprobe->rb_node;

kernel/uprobes.c void unregister_uprobe(struct inode *inode, loff_t offset, · line 528

Quoted 9 lines
kernel/uprobes.c +9 -0
+	for (; n; n = rb_next(n)) {
+		uprobe = rb_entry(n, struct uprobe, rb_node);
+		if (uprobe->inode != inode)
+			break;
+		list_add(&uprobe->pending_list, head);
+		atomic_inc(&uprobe->ref);
+	}
+	spin_unlock_irqrestore(&uprobes_treelock, flags);
+}

If this ever gets to be a latency issue (linear lookup under spinlock) you can use a double lock (mutex+spinlock) and require that modification acquires both but lookups can get away with either.

Quoted 3 lines

That way you can do the linear search using a mutex instead of the spinlock.

Okay,

Quoted 99 lines
Quoted 99 lines
kernel/uprobes.c +99 -0
+
+/*
+ * Called from mmap_region.
+ * called with mm->mmap_sem acquired.
+ *
+ * Return -ve no if we fail to insert probes and we cannot
+ * bail-out.
+ * Return 0 otherwise. i.e :
+ *	- successful insertion of probes
+ *	- (or) no possible probes to be inserted.
+ *	- (or) insertion of probes failed but we can bail-out.
+ */
+int mmap_uprobe(struct vm_area_struct *vma)
+{
+	struct list_head tmp_list;
+	struct uprobe *uprobe, *u;
+	struct inode *inode;
+	int ret = 0;
+
+	if (!valid_vma(vma))
+		return ret;	/* Bail-out */
+
+	inode = igrab(vma->vm_file->f_mapping->host);
+	if (!inode)
+		return ret;
+
+	INIT_LIST_HEAD(&tmp_list);
+	mutex_lock(&uprobes_mmap_mutex);
+	build_probe_list(inode, &tmp_list);
+	list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) {
+		loff_t vaddr;
+
+		list_del(&uprobe->pending_list);
+		if (!ret && uprobe->consumers) {
+			vaddr = vma->vm_start + uprobe->offset;
+			vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+			if (vaddr < vma->vm_start || vaddr >= vma->vm_end)
+				continue;
+			ret = install_breakpoint(vma->vm_mm, uprobe);
+
+			if (ret && (ret == -ESRCH || ret == -EEXIST))
+				ret = 0;
+		}
+		put_uprobe(uprobe);
+	}
+
+	mutex_unlock(&uprobes_mmap_mutex);
+	iput(inode);
+	return ret;
+}
+
+static void dec_mm_uprobes_count(struct vm_area_struct *vma,
+		struct inode *inode)
+{
+	struct uprobe *uprobe;
+	struct rb_node *n;
+	unsigned long flags;
+
+	n = uprobes_tree.rb_node;
+	spin_lock_irqsave(&uprobes_treelock, flags);
+	uprobe = __find_uprobe(inode, 0, &n);
+
+	/*
+	 * If indeed there is a probe for the inode and with offset zero,
+	 * then lets release its reference. (ref got thro __find_uprobe)
+	 */
+	if (uprobe)
+		put_uprobe(uprobe);
+	for (; n; n = rb_next(n)) {
+		loff_t vaddr;
+
+		uprobe = rb_entry(n, struct uprobe, rb_node);
+		if (uprobe->inode != inode)
+			break;
+		vaddr = vma->vm_start + uprobe->offset;
+		vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+		if (vaddr < vma->vm_start || vaddr >= vma->vm_end)
+			continue;
+		atomic_dec(&vma->vm_mm->mm_uprobes_count);
+	}
+	spin_unlock_irqrestore(&uprobes_treelock, flags);
+}
+
+/*
+ * Called in context of a munmap of a vma.
+ */
+void munmap_uprobe(struct vm_area_struct *vma)
+{
+	struct inode *inode;
+
+	if (!valid_vma(vma))
+		return;		/* Bail-out */
+
+	if (!atomic_read(&vma->vm_mm->mm_uprobes_count))
+		return;
+
+	inode = igrab(vma->vm_file->f_mapping->host);
+	if (!inode)
+		return;
Quoted 9 lines

kernel/uprobes.c void unregister_uprobe(struct inode *inode, loff_t offset, · line 632

Quoted 5 lines
kernel/uprobes.c +5 -0
+
+	dec_mm_uprobes_count(vma, inode);
+	iput(inode);
+	return;
+}

One has to wonder why mmap_uprobe() can be one function but munmap_uprobe() cannot.

I didnt understand this comment, Can you please elaborate? mmap_uprobe uses build_probe_list and munmap_uprobe uses dec_mm_uprobes_count.

Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#26 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 4/26] uprobes: Define hooks for mmap/munmap.

Quoted 110 lines · Srikar Dronamraju
Quoted 100 lines
Quoted 100 lines
kernel/uprobes.c +99 -0
+
+/*
+ * Called from mmap_region.
+ * called with mm->mmap_sem acquired.
+ *
+ * Return -ve no if we fail to insert probes and we cannot
+ * bail-out.
+ * Return 0 otherwise. i.e :
+ * - successful insertion of probes
+ * - (or) no possible probes to be inserted.
+ * - (or) insertion of probes failed but we can bail-out.
+ */
+int mmap_uprobe(struct vm_area_struct *vma)
+{
+   struct list_head tmp_list;
+   struct uprobe *uprobe, *u;
+   struct inode *inode;
+   int ret = 0;
+
+   if (!valid_vma(vma))
+           return ret;     /* Bail-out */
+
+   inode = igrab(vma->vm_file->f_mapping->host);
+   if (!inode)
+           return ret;
+
+   INIT_LIST_HEAD(&tmp_list);
+   mutex_lock(&uprobes_mmap_mutex);
+   build_probe_list(inode, &tmp_list);
+   list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) {
+           loff_t vaddr;
+
+           list_del(&uprobe->pending_list);
+           if (!ret && uprobe->consumers) {
+                   vaddr = vma->vm_start + uprobe->offset;
+                   vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+                   if (vaddr < vma->vm_start || vaddr >= vma->vm_end)
+                           continue;
+                   ret = install_breakpoint(vma->vm_mm, uprobe);
+
+                   if (ret && (ret == -ESRCH || ret == -EEXIST))
+                           ret = 0;
+           }
+           put_uprobe(uprobe);
+   }
+
+   mutex_unlock(&uprobes_mmap_mutex);
+   iput(inode);
+   return ret;
+}
+
+static void dec_mm_uprobes_count(struct vm_area_struct *vma,
+           struct inode *inode)
+{
+   struct uprobe *uprobe;
+   struct rb_node *n;
+   unsigned long flags;
+
+   n = uprobes_tree.rb_node;
+   spin_lock_irqsave(&uprobes_treelock, flags);
+   uprobe = __find_uprobe(inode, 0, &n);
+
+   /*
+    * If indeed there is a probe for the inode and with offset zero,
+    * then lets release its reference. (ref got thro __find_uprobe)
+    */
+   if (uprobe)
+           put_uprobe(uprobe);
+   for (; n; n = rb_next(n)) {
+           loff_t vaddr;
+
+           uprobe = rb_entry(n, struct uprobe, rb_node);
+           if (uprobe->inode != inode)
+                   break;
+           vaddr = vma->vm_start + uprobe->offset;
+           vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+           if (vaddr < vma->vm_start || vaddr >= vma->vm_end)
+                   continue;
+           atomic_dec(&vma->vm_mm->mm_uprobes_count);
+   }
+   spin_unlock_irqrestore(&uprobes_treelock, flags);
+}
+
+/*
+ * Called in context of a munmap of a vma.
+ */
+void munmap_uprobe(struct vm_area_struct *vma)
+{
+   struct inode *inode;
+
+   if (!valid_vma(vma))
+           return;         /* Bail-out */
+
+   if (!atomic_read(&vma->vm_mm->mm_uprobes_count))
+           return;
+
+   inode = igrab(vma->vm_file->f_mapping->host);
+   if (!inode)
+           return;
Quoted 9 lines

kernel/uprobes.c void unregister_uprobe(struct inode *inode, loff_t offset, · line 632

Quoted 5 lines
kernel/uprobes.c +5 -0
+
+   dec_mm_uprobes_count(vma, inode);
+   iput(inode);
+   return;
+}

One has to wonder why mmap_uprobe() can be one function but munmap_uprobe() cannot.

Quoted 3 lines

I didnt understand this comment, Can you please elaborate? mmap_uprobe uses build_probe_list and munmap_uprobe uses dec_mm_uprobes_count.

Ah, I missed build_probe_list(), but I didn't see a reason for the existence of dec_mm_uprobe_count(), the name doesn't make sense and the content is 'small' enough to just put in munmap_uprobe.

To me it looks similar to the list iteration you have in mmap_uprobe(), you didn't split that out into another function either.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#27 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 4/26] uprobes: Define hooks for mmap/munmap.

Quoted 115 lines · Peter Zijlstra
Quoted 110 lines · Srikar Dronamraju
Quoted 100 lines
Quoted 100 lines
kernel/uprobes.c +99 -0
+
+/*
+ * Called from mmap_region.
+ * called with mm->mmap_sem acquired.
+ *
+ * Return -ve no if we fail to insert probes and we cannot
+ * bail-out.
+ * Return 0 otherwise. i.e :
+ * - successful insertion of probes
+ * - (or) no possible probes to be inserted.
+ * - (or) insertion of probes failed but we can bail-out.
+ */
+int mmap_uprobe(struct vm_area_struct *vma)
+{
+   struct list_head tmp_list;
+   struct uprobe *uprobe, *u;
+   struct inode *inode;
+   int ret = 0;
+
+   if (!valid_vma(vma))
+           return ret;     /* Bail-out */
+
+   inode = igrab(vma->vm_file->f_mapping->host);
+   if (!inode)
+           return ret;
+
+   INIT_LIST_HEAD(&tmp_list);
+   mutex_lock(&uprobes_mmap_mutex);
+   build_probe_list(inode, &tmp_list);
+   list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) {
+           loff_t vaddr;
+
+           list_del(&uprobe->pending_list);
+           if (!ret && uprobe->consumers) {
+                   vaddr = vma->vm_start + uprobe->offset;
+                   vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+                   if (vaddr < vma->vm_start || vaddr >= vma->vm_end)
+                           continue;
+                   ret = install_breakpoint(vma->vm_mm, uprobe);
+
+                   if (ret && (ret == -ESRCH || ret == -EEXIST))
+                           ret = 0;
+           }
+           put_uprobe(uprobe);
+   }
+
+   mutex_unlock(&uprobes_mmap_mutex);
+   iput(inode);
+   return ret;
+}
+
+static void dec_mm_uprobes_count(struct vm_area_struct *vma,
+           struct inode *inode)
+{
+   struct uprobe *uprobe;
+   struct rb_node *n;
+   unsigned long flags;
+
+   n = uprobes_tree.rb_node;
+   spin_lock_irqsave(&uprobes_treelock, flags);
+   uprobe = __find_uprobe(inode, 0, &n);
+
+   /*
+    * If indeed there is a probe for the inode and with offset zero,
+    * then lets release its reference. (ref got thro __find_uprobe)
+    */
+   if (uprobe)
+           put_uprobe(uprobe);
+   for (; n; n = rb_next(n)) {
+           loff_t vaddr;
+
+           uprobe = rb_entry(n, struct uprobe, rb_node);
+           if (uprobe->inode != inode)
+                   break;
+           vaddr = vma->vm_start + uprobe->offset;
+           vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+           if (vaddr < vma->vm_start || vaddr >= vma->vm_end)
+                   continue;
+           atomic_dec(&vma->vm_mm->mm_uprobes_count);
+   }
+   spin_unlock_irqrestore(&uprobes_treelock, flags);
+}
+
+/*
+ * Called in context of a munmap of a vma.
+ */
+void munmap_uprobe(struct vm_area_struct *vma)
+{
+   struct inode *inode;
+
+   if (!valid_vma(vma))
+           return;         /* Bail-out */
+
+   if (!atomic_read(&vma->vm_mm->mm_uprobes_count))
+           return;
+
+   inode = igrab(vma->vm_file->f_mapping->host);
+   if (!inode)
+           return;
Quoted 9 lines

kernel/uprobes.c void unregister_uprobe(struct inode *inode, loff_t offset, · line 632

Quoted 5 lines
kernel/uprobes.c +5 -0
+
+   dec_mm_uprobes_count(vma, inode);
+   iput(inode);
+   return;
+}

One has to wonder why mmap_uprobe() can be one function but munmap_uprobe() cannot.

Quoted 3 lines

I didnt understand this comment, Can you please elaborate? mmap_uprobe uses build_probe_list and munmap_uprobe uses dec_mm_uprobes_count.

Quoted 6 lines

Ah, I missed build_probe_list(), but I didn't see a reason for the existence of dec_mm_uprobe_count(), the name doesn't make sense and the content is 'small' enough to just put in munmap_uprobe.

To me it looks similar to the list iteration you have in mmap_uprobe(), you didn't split that out into another function either.

Hmm, For me whats done in dec_mm_uprobe_count() is similar to whats done in build_probe_list(), just that build_probe_list does a atomic_inc
+ list_add while dec_mm_uprobe_count() does a atomic_dec.

When I kept dec_mm_uprobe_count() inside munmap_uprobe(), I found most of the code nested too deep. Hence carved it out as a separate function.

I open to suggestions for dec_mm_uprobe_count()

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

Re: [PATCH v5 3.1.0-rc4-tip 4/26] uprobes: Define hooks for mmap/munmap.

In reply to Srikar Dronamraju
Quoted 27 lines · Srikar Dronamraju
Quoted 27 lines

Why not something like:

(quoted diff) +17 -0
+static struct uprobe *__find_uprobe(struct inode * inode, loff_t offset,
                                      bool inode_only)
+{
        struct uprobe u = { .inode = inode, .offset = inode_only ? 0 : offset };
+       struct rb_node *n = uprobes_tree.rb_node;
+       struct uprobe *uprobe;
      struct uprobe *ret = NULL;
+       int match;
+
+       while (n) {
+               uprobe = rb_entry(n, struct uprobe, rb_node);
+               match = match_uprobe(&u, uprobe);
+               if (!match) {
                      if (!inode_only)
                             atomic_inc(&uprobe->ref);
+                       return uprobe;
+               }
              if (inode_only && uprobe->inode == inode)
                      ret = uprobe;
+               if (match < 0)
+                       n = n->rb_left;
+               else
+                       n = n->rb_right;
+
Quoted 8 lines

include/linux/uprobes.h static inline void unregister_uprobe(struct inode *inode, loff_t offset, · line 74

Quoted 4 lines
include/linux/uprobes.h +2 -0
+       }
        return ret;
+}

I am not comfortable with this change.
find_uprobe() was suppose to return back a uprobe if and only if the inode and offset match,

And it will, because find_uprobe() will never expose that third argument.

Quoted 4 lines

However with your approach, we end up
returning a uprobe that isnt matching and one that isnt refcounted. Moreover if even if we have a matching uprobe, we end up sending a unrefcounted uprobe back.

Because the matching isn't the important part, you want to return the leftmost node matching the specified inode. Also, in that case you explicitly don't want the ref, since the first thing you do on the call-site is drop the ref if there was a match. You don't care about inode:0 in particular, you want a place to start iterating all of inode:*.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#29 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 4/26] uprobes: Define hooks for mmap/munmap.

Quoted 45 lines · Peter Zijlstra
Quoted 27 lines · Srikar Dronamraju
Quoted 27 lines

Why not something like:

(quoted diff) +17 -0
+static struct uprobe *__find_uprobe(struct inode * inode, loff_t offset,
                                      bool inode_only)
+{
        struct uprobe u = { .inode = inode, .offset = inode_only ? 0 : offset };
+       struct rb_node *n = uprobes_tree.rb_node;
+       struct uprobe *uprobe;
      struct uprobe *ret = NULL;
+       int match;
+
+       while (n) {
+               uprobe = rb_entry(n, struct uprobe, rb_node);
+               match = match_uprobe(&u, uprobe);
+               if (!match) {
                      if (!inode_only)
                             atomic_inc(&uprobe->ref);
+                       return uprobe;
+               }
              if (inode_only && uprobe->inode == inode)
                      ret = uprobe;
+               if (match < 0)
+                       n = n->rb_left;
+               else
+                       n = n->rb_right;
+
Quoted 8 lines

include/linux/uprobes.h static inline void unregister_uprobe(struct inode *inode, loff_t offset, · line 74

Quoted 4 lines
include/linux/uprobes.h +2 -0
+       }
        return ret;
+}

I am not comfortable with this change.
find_uprobe() was suppose to return back a uprobe if and only if the inode and offset match,

And it will, because find_uprobe() will never expose that third argument.

Quoted 4 lines

However with your approach, we end up
returning a uprobe that isnt matching and one that isnt refcounted. Moreover if even if we have a matching uprobe, we end up sending a unrefcounted uprobe back.

Quoted 7 lines

Because the matching isn't the important part, you want to return the leftmost node matching the specified inode. Also, in that case you explicitly don't want the ref, since the first thing you do on the call-site is drop the ref if there was a match. You don't care about inode:0 in particular, you want a place to start iterating all of inode:*.

The case of we taking a ref and dropping it would arise if and only if there is a matching uprobe i.e inode: and 0 offset. I dont think that would be the common case.

If you arent comfortable passing the rb_node as the third argument, then we could pass the reference to uprobe itself. But that would mean we do a redundant dereference everytime.

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

Re: [PATCH v5 3.1.0-rc4-tip 4/26] uprobes: Define hooks for mmap/munmap.

In reply to Srikar Dronamraju
Quoted 4 lines · Srikar Dronamraju
Quoted 1 line

Isn't good enough? Also, returning an rb_node just seems iffy..

yup this can be done. can you please elaborate on why passing back an rb_node is an issue?

Just seems ugly to me, why return a pointer inside the object the function name deals with.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#31 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 4/26] uprobes: Define hooks for mmap/munmap.

In reply to Srikar Dronamraju

kernel/fork.c struct mm_struct *dup_mm(struct task_struct *tsk) · line 745

Quoted 8 lines · Srikar Dronamraju
kernel/fork.c +3 -0
@@ -739,6 +740,10 @@ struct mm_struct *dup_mm(struct task_struct *tsk)
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 	mm->pmd_huge_pte = NULL;
 #endif
+#ifdef CONFIG_UPROBES
+	atomic_set(&mm->mm_uprobes_count,
+			atomic_read(&oldmm->mm_uprobes_count));

Hmm. Why this can't race with install_breakpoint/remove_breakpoint between _read and _set ?

What about VM_DONTCOPY vma's with breakpoints ?

Quoted 8 lines
kernel/uprobes.c +6 -1
-static int match_uprobe(struct uprobe *l, struct uprobe *r)
+static int match_uprobe(struct uprobe *l, struct uprobe *r, int *match_inode)
 {
+	/*
+	 * if match_inode is non NULL then indicate if the
+	 * inode atleast match.
+	 */
+	if (match_inode)

kernel/uprobes.c static bool valid_vma(struct vm_area_struct *vma) · line 78

Quoted 10 lines
kernel/uprobes.c +5 -0
+		*match_inode = 0;
+
 	if (l->inode < r->inode)
 		return -1;
 	if (l->inode > r->inode)
 		return 1;
 	else {
+		if (match_inode)
+			*match_inode = 1;
+

It is very possible I missed something, but imho this looks confusing.

This close_match logic is only needed for build_probe_list() and dec_mm_uprobes_count(), and both do not actually need the returned uprobe.

Instead of complicating match_uprobe() and __find_uprobe(), perhaps it makes sense to add "struct rb_node *__find_close_rb_node(inode)" ?

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 256

Quoted 5 lines
kernel/uprobes.c +3 -0
+static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe)
 {
 	/* Placeholder: Yet to be implemented */
+	if (!uprobe->consumers)
+		return 0;

How it is possible to see ->consumers == NULL?

OK, afaics it _is_ possible, but only because unregister does del_consumer() without ->i_mutex, but this is bug afaics (see the previous email).

Another user is mmap_uprobe() and it checks ->consumers != NULL itself (but see below).

Quoted 17 lines
kernel/uprobes.c +17 -0
+int mmap_uprobe(struct vm_area_struct *vma)
+{
+	struct list_head tmp_list;
+	struct uprobe *uprobe, *u;
+	struct inode *inode;
+	int ret = 0;
+
+	if (!valid_vma(vma))
+		return ret;	/* Bail-out */
+
+	inode = igrab(vma->vm_file->f_mapping->host);
+	if (!inode)
+		return ret;
+
+	INIT_LIST_HEAD(&tmp_list);
+	mutex_lock(&uprobes_mmap_mutex);
+	build_probe_list(inode, &tmp_list);

kernel/uprobes.c void unregister_uprobe(struct inode *inode, loff_t offset, · line 567

Quoted 10 lines
kernel/uprobes.c +10 -0
+	list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) {
+		loff_t vaddr;
+
+		list_del(&uprobe->pending_list);
+		if (!ret && uprobe->consumers) {
+			vaddr = vma->vm_start + uprobe->offset;
+			vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+			if (vaddr < vma->vm_start || vaddr >= vma->vm_end)
+				continue;
+			ret = install_breakpoint(vma->vm_mm, uprobe);

So. We are adding the new mapping, we should find all breakpoints this file has in the start/end range.

We are holding ->mmap_sem... this seems enough to protect against the races with register/unregister. Except, what if __register_uprobe() fails? In this case __unregister_uprobe() does delete_uprobe() at the very end. What if mmap mmap_uprobe() is called right before delete_?

Quoted 18 lines
kernel/uprobes.c +18 -0
+static void dec_mm_uprobes_count(struct vm_area_struct *vma,
+		struct inode *inode)
+{
+	struct uprobe *uprobe;
+	struct rb_node *n;
+	unsigned long flags;
+
+	n = uprobes_tree.rb_node;
+	spin_lock_irqsave(&uprobes_treelock, flags);
+	uprobe = __find_uprobe(inode, 0, &n);
+
+	/*
+	 * If indeed there is a probe for the inode and with offset zero,
+	 * then lets release its reference. (ref got thro __find_uprobe)
+	 */
+	if (uprobe)
+		put_uprobe(uprobe);
+	for (; n; n = rb_next(n)) {

kernel/uprobes.c void unregister_uprobe(struct inode *inode, loff_t offset, · line 607

Quoted 10 lines
kernel/uprobes.c +10 -0
+		loff_t vaddr;
+
+		uprobe = rb_entry(n, struct uprobe, rb_node);
+		if (uprobe->inode != inode)
+			break;
+		vaddr = vma->vm_start + uprobe->offset;
+		vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+		if (vaddr < vma->vm_start || vaddr >= vma->vm_end)
+			continue;
+		atomic_dec(&vma->vm_mm->mm_uprobes_count);

So, this does atomic_dec() for each bp in this vma?

And the caller is

mm/memory.c unsigned long unmap_vmas(struct mmu_gather *tlb, · line 1342

Quoted 6 lines
mm/memory.c +2 -0
@@ -1337,6 +1338,9 @@ unsigned long unmap_vmas(struct mmu_gather *tlb,
 		if (unlikely(is_pfn_mapping(vma)))
 			untrack_pfn_vma(vma, 0, 0);
 
+		if (vma->vm_file)
+			munmap_uprobe(vma);

Doesn't look right...

munmap_uprobe() assumes that the whole region goes away. This is true in munmap() case afaics, it does __split_vma() if necessary.

But what about truncate() ? In this case this vma is not unmapped, but unmap_vmas() is called anyway and [start, end) can be different. IOW, unless I missed something (this is very possible) we can do more atomic_dec's then needed.

Also, truncate() obviously changes ->i_size. Doesn't this mean unregister_uprobe() should return if offset > i_size ? We need to free uprobes anyway.

MADV_DONTNEED? It calls unmap_vmas() too. And application can do madvise(DONTNEED) in a loop.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#32 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 4/26] uprobes: Define hooks for mmap/munmap.

Quoted 12 lines · Oleg Nesterov

kernel/fork.c struct mm_struct *dup_mm(struct task_struct *tsk) · line 745

Quoted 8 lines · Srikar Dronamraju
kernel/fork.c +3 -0
@@ -739,6 +740,10 @@ struct mm_struct *dup_mm(struct task_struct *tsk)
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 	mm->pmd_huge_pte = NULL;
 #endif
+#ifdef CONFIG_UPROBES
+	atomic_set(&mm->mm_uprobes_count,
+			atomic_read(&oldmm->mm_uprobes_count));

Hmm. Why this can't race with install_breakpoint/remove_breakpoint between _read and _set ?

At this time the child vmas are not yet created, so I dont see a install_breakpoints/remove_breakpoints from child affecting.

However if install_breakpoints/remove_breakpoints happen from a parent
context, from now on till we do a vma_prio_tree_add (actually down_write(oldmm->mmap_sem) in dup_mmap()), then the count in the child may not be the right one. If you are pointing to this race, then its probably bigger than just between read and set.

or are you talking of some other issue?

Quoted 2 lines

What about VM_DONTCOPY vma's with breakpoints ?

Ah... I have missed this.

One solution could be to call mmap_uprobe() like routine just before we release the mmap_sem of the child but after we do a vma_prio_tree_add.

This should also solve the problem of install_breakpoints/remove breakpoints called in parent context that we talked about above.

Quoted 22 lines
Quoted 9 lines
kernel/uprobes.c +6 -1
-static int match_uprobe(struct uprobe *l, struct uprobe *r)
+static int match_uprobe(struct uprobe *l, struct uprobe *r, int *match_inode)
 {
+	/*
+	 * if match_inode is non NULL then indicate if the
+	 * inode atleast match.
+	 */
+	if (match_inode)

kernel/uprobes.c static bool valid_vma(struct vm_area_struct *vma) · line 78

Quoted 10 lines
kernel/uprobes.c +5 -0
+		*match_inode = 0;
+
 	if (l->inode < r->inode)
 		return -1;
 	if (l->inode > r->inode)
 		return 1;
 	else {
+		if (match_inode)
+			*match_inode = 1;
+

It is very possible I missed something, but imho this looks confusing.

Quoted 6 lines

This close_match logic is only needed for build_probe_list() and dec_mm_uprobes_count(), and both do not actually need the returned uprobe.

Instead of complicating match_uprobe() and __find_uprobe(), perhaps it makes sense to add "struct rb_node *__find_close_rb_node(inode)" ?

Yes, we do this too.

Quoted 9 lines

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 256

Quoted 6 lines
kernel/uprobes.c +3 -0
+static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe)
 {
 	/* Placeholder: Yet to be implemented */
+	if (!uprobe->consumers)
+		return 0;

How it is possible to see ->consumers == NULL?

consumers == NULL check is mostly for the mmap_uprobe path. _register_uprobe and _unregister_uprobe() use the same lock to serialize so they can check consumers after taking the lock.

Quoted 2 lines

OK, afaics it _is_ possible, but only because unregister does del_consumer() without ->i_mutex, but this is bug afaics (see the previous email).

We have discussed this in the other thread.

Quoted 35 lines

Another user is mmap_uprobe() and it checks ->consumers != NULL itself (but see below).

Quoted 17 lines
kernel/uprobes.c +17 -0
+int mmap_uprobe(struct vm_area_struct *vma)
+{
+	struct list_head tmp_list;
+	struct uprobe *uprobe, *u;
+	struct inode *inode;
+	int ret = 0;
+
+	if (!valid_vma(vma))
+		return ret;	/* Bail-out */
+
+	inode = igrab(vma->vm_file->f_mapping->host);
+	if (!inode)
+		return ret;
+
+	INIT_LIST_HEAD(&tmp_list);
+	mutex_lock(&uprobes_mmap_mutex);
+	build_probe_list(inode, &tmp_list);

kernel/uprobes.c void unregister_uprobe(struct inode *inode, loff_t offset, · line 567

Quoted 10 lines
kernel/uprobes.c +10 -0
+	list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) {
+		loff_t vaddr;
+
+		list_del(&uprobe->pending_list);
+		if (!ret && uprobe->consumers) {
+			vaddr = vma->vm_start + uprobe->offset;
+			vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+			if (vaddr < vma->vm_start || vaddr >= vma->vm_end)
+				continue;
+			ret = install_breakpoint(vma->vm_mm, uprobe);

So. We are adding the new mapping, we should find all breakpoints this file has in the start/end range.

Quoted 5 lines

We are holding ->mmap_sem... this seems enough to protect against the races with register/unregister. Except, what if __register_uprobe() fails? In this case __unregister_uprobe() does delete_uprobe() at the very end. What if mmap mmap_uprobe() is called right before delete_?

Because consumers would be NULL before _unregister_uprobe kicks in, we shouldnt have a problem here.

_unregister_uprobe and mmap_uprobe() would race for down_read(&mm->mmap_sem), if _unregister_uprobe() gets the read_lock, then by the time mmap_uprobe() gets to run, consumers would be NULL and we are fine since we dont go ahead an insert.

If mmap_uprobe() were to get the write_lock, _unregister_uprobe would do the necessary cleanup.

we are checking consumers twice, but thats just being conservative. we should able to do with just one check too.

Am I missing something?

Quoted 22 lines
Quoted 22 lines
kernel/uprobes.c +22 -0
+static void dec_mm_uprobes_count(struct vm_area_struct *vma,
+		struct inode *inode)
+{
+	struct uprobe *uprobe;
+	struct rb_node *n;
+	unsigned long flags;
+
+	n = uprobes_tree.rb_node;
+	spin_lock_irqsave(&uprobes_treelock, flags);
+	uprobe = __find_uprobe(inode, 0, &n);
+
+	/*
+	 * If indeed there is a probe for the inode and with offset zero,
+	 * then lets release its reference. (ref got thro __find_uprobe)
+	 */
+	if (uprobe)
+		put_uprobe(uprobe);
+	for (; n; n = rb_next(n)) {
+		loff_t vaddr;
+
+		uprobe = rb_entry(n, struct uprobe, rb_node);
+		if (uprobe->inode != inode)
Quoted 8 lines

kernel/uprobes.c void unregister_uprobe(struct inode *inode, loff_t offset, · line 607

Quoted 6 lines
kernel/uprobes.c +6 -0
+			break;
+		vaddr = vma->vm_start + uprobe->offset;
+		vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+		if (vaddr < vma->vm_start || vaddr >= vma->vm_end)
+			continue;
+		atomic_dec(&vma->vm_mm->mm_uprobes_count);

So, this does atomic_dec() for each bp in this vma?

yes.

Quoted 12 lines

And the caller is

mm/memory.c unsigned long unmap_vmas(struct mmu_gather *tlb, · line 1342

Quoted 6 lines
mm/memory.c +2 -0
@@ -1337,6 +1338,9 @@ unsigned long unmap_vmas(struct mmu_gather *tlb,
 		if (unlikely(is_pfn_mapping(vma)))
 			untrack_pfn_vma(vma, 0, 0);
 
+		if (vma->vm_file)
+			munmap_uprobe(vma);

Doesn't look right...

Quoted 8 lines

munmap_uprobe() assumes that the whole region goes away. This is true in munmap() case afaics, it does __split_vma() if necessary.

But what about truncate() ? In this case this vma is not unmapped, but unmap_vmas() is called anyway and [start, end) can be different. IOW, unless I missed something (this is very possible) we can do more atomic_dec's then needed.

would unlink_file_vma be a good place to call munmap_uprobe().

The other idea could be to call munmap_uprobe in unmap_region() just before free_pgtables() and call atomic_set to set the count to 0 in exit_mmap() (again before free_pgtables.).

One other thing that we probably need to do at mmap_uprobe() is cache the number of probes mmap_uprobe installed successfully and then substract the same from mm_uprobes_count if and only if mmap_uprobe() were to return -ve number.

Quoted 3 lines

Also, truncate() obviously changes ->i_size. Doesn't this mean unregister_uprobe() should return if offset > i_size ? We need to free uprobes anyway.

Do you mean we shouldnt check for the offset in unregister_uprobe() and just search in the rbtree for the matching uprobe?
Thats also possible to do.

Quoted 4 lines

MADV_DONTNEED? It calls unmap_vmas() too. And application can do madvise(DONTNEED) in a loop.

I think this would be taken care of if we move the munmap_uprobe() hook from unmap_vmas to unlink_file_vma().

The other thing that I need to investigate a bit more is if I have handle all cases of mremap correctly.

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

Re: [PATCH v5 3.1.0-rc4-tip 4/26] uprobes: Define hooks for mmap/munmap.

Quoted 16 lines · Srikar Dronamraju
Quoted 12 lines · Oleg Nesterov

kernel/fork.c struct mm_struct *dup_mm(struct task_struct *tsk) · line 745

Quoted 8 lines · Srikar Dronamraju
kernel/fork.c +3 -0
@@ -739,6 +740,10 @@ struct mm_struct *dup_mm(struct task_struct *tsk)
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 	mm->pmd_huge_pte = NULL;
 #endif
+#ifdef CONFIG_UPROBES
+	atomic_set(&mm->mm_uprobes_count,
+			atomic_read(&oldmm->mm_uprobes_count));

Hmm. Why this can't race with install_breakpoint/remove_breakpoint between _read and _set ?

Quoted 2 lines

At this time the child vmas are not yet created, so I dont see a install_breakpoints/remove_breakpoints from child affecting.

I meant oldmm.

Quoted 4 lines

However if install_breakpoints/remove_breakpoints happen from a parent context, from now on till we do a vma_prio_tree_add (actually down_write(oldmm->mmap_sem) in dup_mmap()), then the count in the child may not be the right one.
If you are pointing to this race, then its probably bigger than just between read and set.

Yes, this too. IOW, atomic_read/set(mm_uprobes_count) looks always wrong without down_write(mmap_sem).

Quoted 9 lines
Quoted 7 lines

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 256

Quoted 5 lines
kernel/uprobes.c +3 -0
+static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe)
 {
 	/* Placeholder: Yet to be implemented */
+	if (!uprobe->consumers)
+		return 0;

How it is possible to see ->consumers == NULL?

consumers == NULL check is mostly for the mmap_uprobe path.

mmap_uprobe() explicitely checks ->consumers != NULL before install_breakpoint().

Quoted 2 lines

_register_uprobe and _unregister_uprobe() use the same lock to serialize so they can check consumers after taking the lock.

Yes,

Quoted 4 lines
Quoted 2 lines

OK, afaics it _is_ possible, but only because unregister does del_consumer() without ->i_mutex, but this is bug afaics (see the previous email).

We have discussed this in the other thread.

Yes. So afaics we can remove this check if unregister() does del_consumer() under mutex.

Note: I am not saying you should do this ;) I just tried to understand this code.

Quoted 31 lines
Quoted 31 lines
Quoted 17 lines
kernel/uprobes.c +17 -0
+int mmap_uprobe(struct vm_area_struct *vma)
+{
+	struct list_head tmp_list;
+	struct uprobe *uprobe, *u;
+	struct inode *inode;
+	int ret = 0;
+
+	if (!valid_vma(vma))
+		return ret;	/* Bail-out */
+
+	inode = igrab(vma->vm_file->f_mapping->host);
+	if (!inode)
+		return ret;
+
+	INIT_LIST_HEAD(&tmp_list);
+	mutex_lock(&uprobes_mmap_mutex);
+	build_probe_list(inode, &tmp_list);

kernel/uprobes.c void unregister_uprobe(struct inode *inode, loff_t offset, · line 567

Quoted 10 lines
kernel/uprobes.c +10 -0
+	list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) {
+		loff_t vaddr;
+
+		list_del(&uprobe->pending_list);
+		if (!ret && uprobe->consumers) {
+			vaddr = vma->vm_start + uprobe->offset;
+			vaddr -= vma->vm_pgoff << PAGE_SHIFT;
+			if (vaddr < vma->vm_start || vaddr >= vma->vm_end)
+				continue;
+			ret = install_breakpoint(vma->vm_mm, uprobe);

So. We are adding the new mapping, we should find all breakpoints this file has in the start/end range.

Quoted 8 lines
Quoted 5 lines

We are holding ->mmap_sem... this seems enough to protect against the races with register/unregister. Except, what if __register_uprobe() fails? In this case __unregister_uprobe() does delete_uprobe() at the very end. What if mmap mmap_uprobe() is called right before delete_?

Because consumers would be NULL before _unregister_uprobe kicks in, we shouldnt have a problem here.

Hmm. But it is not NULL.

Once again, I didn't mean unregister_uprobe(). I meant register_uprobe(). In this case, if __register_uprobe() fails, we are doing __unregister but uprobe->consumer != NULL.

Just suppose that the caller of register_uprobe() gets a (long) preemption right before __unregister_uprobe()->delete_uprobe(). What if mmap() is called at this time?

Quoted 1 line

Am I missing something?

May be you, may be me. Please recheck ;)

Quoted 3 lines
Quoted 3 lines

Also, truncate() obviously changes ->i_size. Doesn't this mean unregister_uprobe() should return if offset > i_size ? We need to free uprobes anyway.

Argh, I meant "should NOT return if offset > i_size".

Quoted 3 lines

Do you mean we shouldnt check for the offset in unregister_uprobe() and just search in the rbtree for the matching uprobe?
Thats also possible to do.

Yes, we can't trust this check afaics.

Quoted 2 lines

I think this would be taken care of if we move the munmap_uprobe() hook from unmap_vmas to unlink_file_vma().

Probably yes, we should rely on prio_tree locking/changes.

Quoted 2 lines

The other thing that I need to investigate a bit more is if I have handle all cases of mremap correctly.

Yes. May be mmap_uprobe() should be "closer" to vma_prio_tree_add/insert too, but I am not sure.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#34 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 4/26] uprobes: Define hooks for mmap/munmap.

Quoted 10 lines
Quoted 10 lines
Quoted 9 lines

So. We are adding the new mapping, we should find all breakpoints this file has in the start/end range.

We are holding ->mmap_sem... this seems enough to protect against the races with register/unregister. Except, what if __register_uprobe() fails? In this case __unregister_uprobe() does delete_uprobe() at the very end. What if mmap mmap_uprobe() is called right before delete_?

Quoted 8 lines
Quoted 2 lines

Because consumers would be NULL before _unregister_uprobe kicks in, we shouldnt have a problem here.

Hmm. But it is not NULL.

Once again, I didn't mean unregister_uprobe(). I meant register_uprobe(). In this case, if __register_uprobe() fails, we are doing __unregister but uprobe->consumer != NULL.

Oh Okay, I missed setting uprobe->consumer = NULL once __register_uprobe fails.
I shall go ahead and set uprobe->consumer = NULL; (the other option is calling del_consumer() but I dont see a need for calling this.) just before calling __unregister_uprobe() if and only if __register_uprobe fails.

Quoted 8 lines

Just suppose that the caller of register_uprobe() gets a (long) preemption right before __unregister_uprobe()->delete_uprobe(). What if mmap() is called at this time?

Quoted 1 line

Am I missing something?

May be you, may be me. Please recheck ;)

Rechecked and found the issue. Thanks.

Quoted 11 lines
Quoted 3 lines

I think this would be taken care of if we move the munmap_uprobe() hook from unmap_vmas to unlink_file_vma().

Probably yes, we should rely on prio_tree locking/changes.

Quoted 2 lines

The other thing that I need to investigate a bit more is if I have handle all cases of mremap correctly.

Yes. May be mmap_uprobe() should be "closer" to vma_prio_tree_add/insert too, but I am not sure.

Okay, that seems like a good idea.

Quoted 3 lines

Oleg.

Signature · 2 lines
Thanks and Regards
Srikar
#35 lore raw

PATCH v5 5/26 Uprobes: copy of the original instruction.

In reply to Srikar Dronamraju

When inserting the first probepoint, save a copy of the original instruction. This copy is later used for fixup analysis, copied to the slot on probe-hit and for restoring the original instruction.

---
 arch/Kconfig            |    1 
 include/linux/uprobes.h |   12 ++++
 kernel/uprobes.c        |  142 +++++++++++++++++++++++++++++++++++++++++++----
 3 files changed, 143 insertions(+), 12 deletions(-)
arch/Kconfig +1 -0
index dedd489..d6a4e1d 100644
@@ -63,6 +63,7 @@ config OPTPROBES
 
 config UPROBES
 	bool "User-space probes (EXPERIMENTAL)"
+	select MM_OWNER
 	help
 	  Uprobes enables kernel subsystems to establish probepoints
 	  in user applications and execute handler functions when
include/linux/uprobes.h +12 -0
index b4de058..50a8c67 100644
@@ -26,6 +26,12 @@
 #include <linux/rbtree.h>
 
 struct vm_area_struct;
+#ifdef CONFIG_ARCH_SUPPORTS_UPROBES
+#include <asm/uprobes.h>
+#else
+
+#define MAX_UINSN_BYTES 4
+#endif
 
 struct uprobe_consumer {
 	int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs);
@@ -46,9 +52,15 @@ struct uprobe {
 	struct uprobe_consumer	*consumers;
 	struct inode		*inode;		/* Also hold a ref to inode */
 	loff_t			offset;
+	int			copy;
+	u8			insn[MAX_UINSN_BYTES];
 };
 
 #ifdef CONFIG_UPROBES
+extern int __weak set_bkpt(struct task_struct *tsk, struct uprobe *uprobe,
+							unsigned long vaddr);
+extern int __weak set_orig_insn(struct task_struct *tsk,
+		struct uprobe *uprobe, unsigned long vaddr, bool verify);
 extern int register_uprobe(struct inode *inode, loff_t offset,
 				struct uprobe_consumer *consumer);
 extern void unregister_uprobe(struct inode *inode, loff_t offset,
kernel/uprobes.c +130 -12

186 lines not on this page · raw message

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#36 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 5/26] Uprobes: copy of the original instruction.

Quoted 11 lines · Srikar Dronamraju
kernel/uprobes.c +10 -0
+static int __copy_insn(struct address_space *mapping,
+			struct vm_area_struct *vma, char *insn,
+			unsigned long nbytes, unsigned long offset)
+{
+	struct file *filp = vma->vm_file;
+	struct page *page;
+	void *vaddr;
+	unsigned long off1;
+	unsigned long idx;
+

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 285

Quoted 10 lines
kernel/uprobes.c +10 -0
+	if (!filp)
+		return -EINVAL;
+
+	idx = (unsigned long) (offset >> PAGE_CACHE_SHIFT);
+	off1 = offset &= ~PAGE_MASK;
+
+	/*
+	 * Ensure that the page that has the original instruction is
+	 * populated and in page-cache.
+	 */

Hmm. But how we can ensure?

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 286

Quoted 1 line
kernel/uprobes.c +1 -0
+	page_cache_sync_readahead(mapping, &filp->f_ra, filp, idx, 1);

This schedules the i/o,

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 287

Quoted 1 line
kernel/uprobes.c +1 -0
+	page = grab_cache_page(mapping, idx);

This finds/locks the page in the page-cache,

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 292

Quoted 5 lines
kernel/uprobes.c +5 -0
+	if (!page)
+		return -ENOMEM;
+
+	vaddr = kmap_atomic(page);
+	memcpy(insn, vaddr + off1, nbytes);

What if this page is not PageUptodate() ?

Somehow this assumes that the i/o was already completed, I don't understand this.

But I am starting to think I simply do not understand this change. To the point, I do not underestand why do we need copy_insn() at all. We are going to replace this page, can't we save/analyze ->insn later when we copy the content of the old page? Most probably I missed something simple...

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 336

Quoted 11 lines
kernel/uprobes.c +11 -0
+static struct task_struct *get_mm_owner(struct mm_struct *mm)
+{
+	struct task_struct *tsk;
+
+	rcu_read_lock();
+	tsk = rcu_dereference(mm->owner);
+	if (tsk)
+		get_task_struct(tsk);
+	rcu_read_unlock();
+	return tsk;
+}

Hmm. Do we really need task_struct?

Quoted 10 lines
kernel/uprobes.c +6 -2
-static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe)
+static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe,
+				struct vm_area_struct *vma, loff_t vaddr)
 {
-	/* Placeholder: Yet to be implemented */
+	struct task_struct *tsk;
+	unsigned long addr;
+	int ret = -EINVAL;
+
 	if (!uprobe->consumers)

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 353

Quoted 10 lines
kernel/uprobes.c +6 -2
 		return 0;
 
-	atomic_inc(&mm->mm_uprobes_count);
-	return 0;
+	tsk = get_mm_owner(mm);
+	if (!tsk)	/* task is probably exiting; bail-out */
+		return -ESRCH;
+
+	if (vaddr > TASK_SIZE_OF(tsk))
+		goto put_return;

But this should not be possible, no? How it can map this vaddr above TASK_SIZE ?

get_user_pages(tsk => NULL) is fine. Why else do we need mm->owner ?

Probably used by the next patches... Say, is_32bit_app(tsk). This can use mm->context.ia32_compat (hopefully will be replaced with MMF_COMPAT).

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#37 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 5/26] Uprobes: copy of the original instruction.

Quoted 35 lines · Oleg Nesterov
Quoted 11 lines · Srikar Dronamraju
kernel/uprobes.c +10 -0
+static int __copy_insn(struct address_space *mapping,
+			struct vm_area_struct *vma, char *insn,
+			unsigned long nbytes, unsigned long offset)
+{
+	struct file *filp = vma->vm_file;
+	struct page *page;
+	void *vaddr;
+	unsigned long off1;
+	unsigned long idx;
+

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 285

Quoted 10 lines
kernel/uprobes.c +10 -0
+	if (!filp)
+		return -EINVAL;
+
+	idx = (unsigned long) (offset >> PAGE_CACHE_SHIFT);
+	off1 = offset &= ~PAGE_MASK;
+
+	/*
+	 * Ensure that the page that has the original instruction is
+	 * populated and in page-cache.
+	 */

Hmm. But how we can ensure?

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 286

Quoted 1 line
kernel/uprobes.c +1 -0
+	page_cache_sync_readahead(mapping, &filp->f_ra, filp, idx, 1);

This schedules the i/o,

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 287

Quoted 1 line
kernel/uprobes.c +1 -0
+	page = grab_cache_page(mapping, idx);

This finds/locks the page in the page-cache,

Quoted 7 lines

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 292

Quoted 5 lines
kernel/uprobes.c +5 -0
+	if (!page)
+		return -ENOMEM;
+
+	vaddr = kmap_atomic(page);
+	memcpy(insn, vaddr + off1, nbytes);

What if this page is not PageUptodate() ?

Since we do a synchronous read ahead, I thought the page would be populated and upto date.

would these two lines after grab_cache_page help?

	if (!PageUptodate(page)) 
		mapping->a_ops->readpage(filp, page);
Quoted 49 lines

Somehow this assumes that the i/o was already completed, I don't understand this.

But I am starting to think I simply do not understand this change. To the point, I do not underestand why do we need copy_insn() at all. We are going to replace this page, can't we save/analyze ->insn later when we copy the content of the old page? Most probably I missed something simple...

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 336

Quoted 11 lines
kernel/uprobes.c +11 -0
+static struct task_struct *get_mm_owner(struct mm_struct *mm)
+{
+	struct task_struct *tsk;
+
+	rcu_read_lock();
+	tsk = rcu_dereference(mm->owner);
+	if (tsk)
+		get_task_struct(tsk);
+	rcu_read_unlock();
+	return tsk;
+}

Hmm. Do we really need task_struct?

Quoted 10 lines
kernel/uprobes.c +6 -2
-static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe)
+static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe,
+				struct vm_area_struct *vma, loff_t vaddr)
 {
-	/* Placeholder: Yet to be implemented */
+	struct task_struct *tsk;
+	unsigned long addr;
+	int ret = -EINVAL;
+
 	if (!uprobe->consumers)

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 353

Quoted 10 lines
kernel/uprobes.c +6 -2
 		return 0;
 
-	atomic_inc(&mm->mm_uprobes_count);
-	return 0;
+	tsk = get_mm_owner(mm);
+	if (!tsk)	/* task is probably exiting; bail-out */
+		return -ESRCH;
+
+	if (vaddr > TASK_SIZE_OF(tsk))
+		goto put_return;

But this should not be possible, no? How it can map this vaddr above TASK_SIZE ?

Quoted 7 lines

get_user_pages(tsk => NULL) is fine. Why else do we need mm->owner ?

Probably used by the next patches... Say, is_32bit_app(tsk). This can use mm->context.ia32_compat (hopefully will be replaced with MMF_COMPAT).

We used the tsk struct for checking if the application was 32 bit and for calling get_user_pages. Since we can pass NULL to get_user_pages and since we can use mm->context.ia32_compat or MMF_COMPAT, I will remove get_mm_owner, that way we dont need to be dependent on CONFIG_MM_OWNER.

Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#38 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 5/26] Uprobes: copy of the original instruction.

Srikar, warning.

I am going to discuss the things I do not really understand ;) Hopefully someone will correct me if I am wrong.

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

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 286

Quoted 1 line
kernel/uprobes.c +1 -0
+	page_cache_sync_readahead(mapping, &filp->f_ra, filp, idx, 1);

This schedules the i/o,

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 287

Quoted 1 line
kernel/uprobes.c +1 -0
+	page = grab_cache_page(mapping, idx);

This finds/locks the page in the page-cache,

Quoted 7 lines

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 292

Quoted 5 lines
kernel/uprobes.c +5 -0
+	if (!page)
+		return -ENOMEM;
+
+	vaddr = kmap_atomic(page);
+	memcpy(insn, vaddr + off1, nbytes);

What if this page is not PageUptodate() ?

Quoted 2 lines

Since we do a synchronous read ahead, I thought the page would be populated and upto date.

What does this "synchronous" actually mean?

First of all, page_cache_sync_readahead() can simply return. Or __do_page_cache_readahead() can "skip" the page if it is already in the page cache.

IOW, we do not even know if ->readpage() was called. But even if it was called, afaics (in general) the page will be unlocked and marked Uptodate when I/O completes, not when ->readpage() returns.

Quoted 4 lines

would these two lines after grab_cache_page help?

	if (!PageUptodate(page))
		mapping->a_ops->readpage(filp, page);

This doesn't look right. At least you need lock_page().

Anyway. Why you can't simply use read_mapping_page() or even kernel_read() ?

But the real question is:

Quoted 5 lines
Quoted 5 lines

But I am starting to think I simply do not understand this change. To the point, I do not underestand why do we need copy_insn() at all. We are going to replace this page, can't we save/analyze ->insn later when we copy the content of the old page? Most probably I missed something simple...

Could you please explain?

Quoted 11 lines
Quoted 10 lines

But this should not be possible, no? How it can map this vaddr above TASK_SIZE ?

get_user_pages(tsk => NULL) is fine. Why else do we need mm->owner ?

Probably used by the next patches... Say, is_32bit_app(tsk). This can use mm->context.ia32_compat (hopefully will be replaced with MMF_COMPAT).

Quoted 4 lines

We used the tsk struct for checking if the application was 32 bit and for calling get_user_pages. Since we can pass NULL to get_user_pages and since we can use mm->context.ia32_compat or MMF_COMPAT, I will remove get_mm_owner, that way we dont need to be dependent on CONFIG_MM_OWNER.

Great!

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#39 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 5/26] Uprobes: copy of the original instruction.

In reply to Oleg Nesterov
Quoted 41 lines · Oleg Nesterov
Quoted 11 lines · Srikar Dronamraju
kernel/uprobes.c +10 -0
+static int __copy_insn(struct address_space *mapping,
+			struct vm_area_struct *vma, char *insn,
+			unsigned long nbytes, unsigned long offset)
+{
+	struct file *filp = vma->vm_file;
+	struct page *page;
+	void *vaddr;
+	unsigned long off1;
+	unsigned long idx;
+

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 285

Quoted 10 lines
kernel/uprobes.c +10 -0
+	if (!filp)
+		return -EINVAL;
+
+	idx = (unsigned long) (offset >> PAGE_CACHE_SHIFT);
+	off1 = offset &= ~PAGE_MASK;
+
+	/*
+	 * Ensure that the page that has the original instruction is
+	 * populated and in page-cache.
+	 */

Hmm. But how we can ensure?

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 286

Quoted 1 line
kernel/uprobes.c +1 -0
+	page_cache_sync_readahead(mapping, &filp->f_ra, filp, idx, 1);

This schedules the i/o,

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 287

Quoted 1 line
kernel/uprobes.c +1 -0
+	page = grab_cache_page(mapping, idx);

This finds/locks the page in the page-cache,

kernel/uprobes.c static bool del_consumer(struct uprobe *uprobe, · line 292

Quoted 5 lines
kernel/uprobes.c +5 -0
+	if (!page)
+		return -ENOMEM;
+
+	vaddr = kmap_atomic(page);
+	memcpy(insn, vaddr + off1, nbytes);

What if this page is not PageUptodate() ?

Quoted 9 lines

Somehow this assumes that the i/o was already completed, I don't understand this.

But I am starting to think I simply do not understand this change. To the point, I do not underestand why do we need copy_insn() at all. We are going to replace this page, can't we save/analyze ->insn later when we copy the content of the old page? Most probably I missed something simple...

Copying the instruction at the time we replace the original instruction would have been ideal. However there are a few irritants to handle.

- While inserting the breakpoint, we might find that the original instruction to be the breakpoint instruction itself. (This could happen if mmap_uprobe were to race with register_uprobe() or somebody else like gdb inserted a breakpoint). How do we distinguish if the breakpoint instruction was around in the text or somebody inserted a breakpoint in that address-space? Since we read from the page-cache, we can easily resolve this.

- On archs like x86, with variable size instructions, the original instruction can be across 2 pages. This is because we copy the maximum instruction size from the given vaddr into a buffer for subsequent analysis. So the copy_insn takes care of getting two pages if and when required.
Currently the insert and remove breakpoint
assumes that the instruction size of a breakpoint is the smallest size for that architecture. Hence reading/writing to one page in write_opcode is good enough.

- Again on variable instruction size supporting archs, if two subsequent instructions are probed, the original instruction if copied using get_user_pages might already have a breakpoint included. (This shouldnt have any effect on the uprobes though.)

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

Re: [PATCH v5 3.1.0-rc4-tip 5/26] Uprobes: copy of the original instruction.

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

But I am starting to think I simply do not understand this change. To the point, I do not underestand why do we need copy_insn() at all. We are going to replace this page, can't we save/analyze ->insn later when we copy the content of the old page? Most probably I missed something simple...

Quoted 8 lines

Copying the instruction at the time we replace the original instruction would have been ideal. However there are a few irritants to handle.

...
How do we distinguish if the
breakpoint instruction was around in the text or somebody inserted a breakpoint in that address-space? Since we read from the page-cache, we can easily resolve this.

Ah. I see.

Quoted 2 lines

- On archs like x86, with variable size instructions, the original instruction can be across 2 pages.

Heh. Indeed ;)

Thanks Srikar.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#41 lore raw

PATCH v5 6/26 Uprobes: define fixups.

In reply to Srikar Dronamraju

During the first insertion of a probepoint, instruction is analyzed for fixups and cached in the per-uprobe struct. On a probehit, the cached fixup is used. Fixup analysis and caching is done in arch-specific code.

---
 include/linux/uprobes.h |   12 ++++++++++++
 1 files changed, 12 insertions(+), 0 deletions(-)
include/linux/uprobes.h +12 -0
index 50a8c67..074c4e9 100644
@@ -33,6 +33,17 @@ struct vm_area_struct;
 #define MAX_UINSN_BYTES 4
 #endif
 
+#define uprobe_opcode_sz sizeof(uprobe_opcode_t)
+
+/* Post-execution fixups.  Some architectures may define others. */
+
+/* No fixup needed */
+#define UPROBES_FIX_NONE	0x0
+/* Adjust IP back to vicinity of actual insn */
+#define UPROBES_FIX_IP	0x1
+/* Adjust the return address of a call insn */
+#define UPROBES_FIX_CALL	0x2
+
 struct uprobe_consumer {
 	int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs);
 	/*
@@ -53,6 +64,7 @@ struct uprobe {
 	struct inode		*inode;		/* Also hold a ref to inode */
 	loff_t			offset;
 	int			copy;
+	u16			fixups;
 	u8			insn[MAX_UINSN_BYTES];
 };
 
#42 lore raw

PATCH v5 7/26 Uprobes: uprobes arch info

In reply to Srikar Dronamraju

Introduce per uprobe arch info structure.
Used to store arch specific details. For example: details to handle Rip relative instructions in X86_64.

---
 include/linux/uprobes.h |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)
include/linux/uprobes.h +2 -1
index 074c4e9..2548b94 100644
@@ -29,7 +29,7 @@ struct vm_area_struct;
 #ifdef CONFIG_ARCH_SUPPORTS_UPROBES
 #include <asm/uprobes.h>
 #else
-
+struct uprobe_arch_info {};
 #define MAX_UINSN_BYTES 4
 #endif
 
@@ -60,6 +60,7 @@ struct uprobe {
 	atomic_t		ref;
 	struct rw_semaphore	consumer_rwsem;
 	struct list_head	pending_list;
+	struct uprobe_arch_info arch_info;
 	struct uprobe_consumer	*consumers;
 	struct inode		*inode;		/* Also hold a ref to inode */
 	loff_t			offset;

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#43 lore raw

PATCH v5 8/26 x86: analyze instruction and determine fixups.

In reply to Srikar Dronamraju

The instruction analysis is based on x86 instruction decoder and determines if an instruction can be probed and determines the necessary fixups after singlestep. Instruction analysis is done at probe insertion time so that we avoid having to repeat the same analysis every time a probe is hit.

---
 arch/x86/Kconfig               |    3 
 arch/x86/include/asm/uprobes.h |   42 ++++
 arch/x86/kernel/Makefile       |    1 
 arch/x86/kernel/uprobes.c      |  385 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 431 insertions(+), 0 deletions(-)
 create mode 100644 arch/x86/include/asm/uprobes.h
 create mode 100644 arch/x86/kernel/uprobes.c
arch/x86/Kconfig +3 -0
index f1833e3..39fd62b 100644
@@ -250,6 +250,9 @@ config ARCH_CPU_PROBE_RELEASE
 	def_bool y
 	depends on HOTPLUG_CPU
 
+config ARCH_SUPPORTS_UPROBES
+	def_bool y
+
 source "init/Kconfig"
 source "kernel/Kconfig.freezer"
 
arch/x86/include/asm/uprobes.h new file +42 -0

42 lines not on this page · raw message

arch/x86/kernel/Makefile +1 -0
index 82f2912..e95cc9d 100644
@@ -98,6 +98,7 @@ obj-$(CONFIG_X86_CHECK_BIOS_CORRUPTION) += check.o
 
 obj-$(CONFIG_SWIOTLB)			+= pci-swiotlb.o
 obj-$(CONFIG_OF)			+= devicetree.o
+obj-$(CONFIG_UPROBES)			+= uprobes.o
 
 ###
 # 64 bit specific files
arch/x86/kernel/uprobes.c new file +385 -0

385 lines not on this page · raw message

#44 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 8/26] x86: analyze instruction and determine fixups.

Quoted 9 lines · Srikar Dronamraju

The instruction analysis is based on x86 instruction decoder and determines if an instruction can be probed and determines the necessary fixups after singlestep. Instruction analysis is done at probe insertion time so that we avoid having to repeat the same analysis every time a probe is hit.

Quoted 8 lines
---
 arch/x86/Kconfig               |    3 
 arch/x86/include/asm/uprobes.h |   42 ++++
 arch/x86/kernel/Makefile       |    1 
 arch/x86/kernel/uprobes.c      |  385 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 431 insertions(+), 0 deletions(-)
 create mode 100644 arch/x86/include/asm/uprobes.h
 create mode 100644 arch/x86/kernel/uprobes.c

You've probably thought of this but it would be nice to skip XOL for nops. This would be a common case with static probes (e.g. sdt.h) where the probe template includes a nop where we can easily plant int $0x3.

Perhaps a check can be added to the analysis so that after calling the filter/handler we can immediately continue the process instead of executing the (useless) nop out-of-line.

Stefan

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#45 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 8/26] x86: analyze instruction and determine fixups.

Quoted 3 lines · Stefan Hajnoczi

You've probably thought of this but it would be nice to skip XOL for nops. This would be a common case with static probes (e.g. sdt.h) where the probe template includes a nop where we can easily plant int $0x3.

Do we now have sdt.h support for uprobes? That's one of the killer features that always seemed to get postponed.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#46 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 8/26] x86: analyze instruction and determine fixups.

Quoted 7 lines · Christoph Hellwig
Quoted 3 lines · Stefan Hajnoczi

You've probably thought of this but it would be nice to skip XOL for nops. This would be a common case with static probes (e.g. sdt.h) where the probe template includes a nop where we can easily plant int $0x3.

Do we now have sdt.h support for uprobes? That's one of the killer features that always seemed to get postponed.

Not yet but it's a question of doing roughly what SystemTap does to parse the appropriate ELF sections and then putting those probes into uprobes.

Masami looked at this and found that SystemTap sdt.h currently requires an extra userspace memory store in order to activate probes. Each probe has a "semaphore" 16-bit counter which applications may test before hitting the probe itself. This is used to avoid overhead in applications that do expensive argument processing (e.g. creating strings) for probes.

But this should be solvable so it would be possible to use perf-probe(1) on a std.h-enabled binary. Some distros already ship such binaries!

Stefan

#47 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 8/26] x86: analyze instruction and determine fixups.

Quoted 13 lines · Stefan Hajnoczi
Quoted 7 lines · Christoph Hellwig
Quoted 3 lines · Stefan Hajnoczi

You've probably thought of this but it would be nice to skip XOL for nops. This would be a common case with static probes (e.g. sdt.h) where the probe template includes a nop where we can easily plant int $0x3.

Do we now have sdt.h support for uprobes? That's one of the killer features that always seemed to get postponed.

Not yet but it's a question of doing roughly what SystemTap does to parse the appropriate ELF sections and then putting those probes into uprobes.

Quoted 6 lines

Masami looked at this and found that SystemTap sdt.h currently requires an extra userspace memory store in order to activate probes. Each probe has a "semaphore" 16-bit counter which applications may test before hitting the probe itself. This is used to avoid overhead in applications that do expensive argument processing (e.g. creating strings) for probes.

Indeed, originally, those semaphores designed for such use cases. However, some applications *always* use it (e.g. qemu-kvm).

Quoted 3 lines

But this should be solvable so it would be possible to use perf-probe(1) on a std.h-enabled binary. Some distros already ship such binaries!

I'm not sure that we should stick on the current implementation of the sdt.h. I think we'd better modify the sdt.h to replace such semaphores with checking whether the tracepoint is changed from nop.

Or, we can introduce an add-hoc ptrace code to perftools for modifying those semaphores. However, this means that user always has to use perf to trace applications, and it's hard to trace multiple applications at a time (can we attach all of them?)...

Thank you,

Signature · 4 lines
Masami HIRAMATSU
Software Platform Research Dept. Linux Technology Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]
#48 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 8/26] x86: analyze instruction and determine fixups.

Quoted 9 lines · Masami Hiramatsu
Quoted 4 lines · Stefan Hajnoczi
Quoted 1 line · Christoph Hellwig

On Tue, Sep 20, 2011 at 06:13:10PM +0100, Stefan Hajnoczi wrote:

But this should be solvable so it would be possible to use perf-probe(1) on a std.h-enabled binary. Some distros already ship such binaries!

I'm not sure that we should stick on the current implementation of the sdt.h. I think we'd better modify the sdt.h to replace such semaphores with checking whether the tracepoint is changed from nop.

I like this option. The only implication is that all userspace tracing needs to go through uprobes if we want to support multiple consumers tracing the same address.

Quoted 4 lines

Or, we can introduce an add-hoc ptrace code to perftools for modifying those semaphores. However, this means that user always has to use perf to trace applications, and it's hard to trace multiple applications at a time (can we attach all of them?)...

I don't think perf needs to stay attached to the processes. It just needs to increment the semaphores on startup and decrement them on shutdown.

Are you going to attempt either of these implementations?

Stefan

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#49 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 8/26] x86: analyze instruction and determine fixups.

Quoted 8 lines · Masami Hiramatsu
Quoted 6 lines

Masami looked at this and found that SystemTap sdt.h currently requires an extra userspace memory store in order to activate probes. Each probe has a "semaphore" 16-bit counter which applications may test before hitting the probe itself. This is used to avoid overhead in applications that do expensive argument processing (e.g. creating strings) for probes.

Indeed, originally, those semaphores designed for such use cases. However, some applications *always* use it (e.g. qemu-kvm).

I found that qemu-kvm generates its tracepoints like this:

  static inline void trace_$name($args) {
      if (QEMU_${nameupper}_ENABLED()) {
          QEMU_${nameupper}($argnames);
      }
  }

In that case, the $args are always computed to call the inline, so you'll basically just get a memory read, jump, NOP. There's no benefit from checking ENABLED() here, and removing it would leave only the NOP.
Even if you invent an improved mechanism for ENABLED(), that doesn't
change the fact that it's doing useless work here.

So in this case, it may be better to patch qemu, assuming my statements hold for DTrace's implementation on other platforms too. The ENABLED() guard still does have other genuine uses though, as with the string preparation in Python's probes.

Quoted 7 lines · Stefan Hajnoczi
Quoted 3 lines

I'm not sure that we should stick on the current implementation of the sdt.h. I think we'd better modify the sdt.h to replace such semaphores with checking whether the tracepoint is changed from nop.

I like this option. The only implication is that all userspace tracing needs to go through uprobes if we want to support multiple consumers tracing the same address.

This limitation is practically true already, since sharing consumers have to negotiate the breakpoint anyway.

If we can find a better way to handle semaphores, we at systemtap will welcome sdt.h improvements. On the face of it, checking one's own NOP for modification sounds pretty elegant, but I'm not convinced that it's possible in practice.

For one, it requires arch specific knowledge in sdt.h of what the NOP or breakpoint looks like, whereas sdt.h currently only knows whether to use NOP or NOP 0, without knowledge of how that's encoded. And this gets trickier with archs like IA64 where you're part of a bundle. So this much is hard, but not impossible.

Another issue is that there's not an easy compile-time correlation between semaphore checks and probe locations, nor is it necessarily a 1:1 mapping. The FOO_ENABLED() and PROBE_FOO() code blocks are distinct, and the compiler can do many tricks with them, loop unrolling, function specialization, etc. And if we start placing constraints to prevent this, then I think we'll be impacting code-gen of the application more than we'd like.

So, I invite sdt.h prototypes of the nop check, but I'm skeptical...

Quoted 8 lines
Quoted 4 lines

Or, we can introduce an add-hoc ptrace code to perftools for modifying those semaphores. However, this means that user always has to use perf to trace applications, and it's hard to trace multiple applications at a time (can we attach all of them?)...

I don't think perf needs to stay attached to the processes. It just needs to increment the semaphores on startup and decrement them on shutdown.

You're still relying on getting in there twice, but ptrace could be busy either or both times. Plus you could inadvertently block any of the other legitimate ptrace apps, especially if doing systemwide probing.

FWIW, the counter-semaphore is really only useful for the case where the breakpoint is placed centrally (by uprobes), but the semaphore is managed by each separate consumer. In that case each consumer can inc/dec their presence. But if uprobes were to manage this itself, it basically becomes a simple flag. So, it would do the trick to have uprobes take an extra inode offset as a flag to write in a 1, which is admittedly a bit gross, but IMO the most workable.

Josh

PS - context for the CCed systemtap list:
https://lkml.org/lkml/2011/9/23/93

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#50 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 8/26] x86: analyze instruction and determine fixups.

Quoted 10 lines · Josh Stone
Quoted 8 lines · Masami Hiramatsu
Quoted 6 lines

Masami looked at this and found that SystemTap sdt.h currently requires an extra userspace memory store in order to activate probes. Each probe has a "semaphore" 16-bit counter which applications may test before hitting the probe itself. This is used to avoid overhead in applications that do expensive argument processing (e.g. creating strings) for probes.

Indeed, originally, those semaphores designed for such use cases. However, some applications *always* use it (e.g. qemu-kvm).

Quoted 7 lines

I found that qemu-kvm generates its tracepoints like this:

  static inline void trace_$name($args) {
      if (QEMU_${nameupper}_ENABLED()) {
          QEMU_${nameupper}($argnames);
      }
  }

Right, that's what I've said.

Quoted 5 lines

In that case, the $args are always computed to call the inline, so you'll basically just get a memory read, jump, NOP. There's no benefit from checking ENABLED() here, and removing it would leave only the NOP.
Even if you invent an improved mechanism for ENABLED(), that doesn't
change the fact that it's doing useless work here.

Yeah, this use is totally meaningless...

Quoted 4 lines

So in this case, it may be better to patch qemu, assuming my statements hold for DTrace's implementation on other platforms too. The ENABLED() guard still does have other genuine uses though, as with the string preparation in Python's probes.

I agree with that qemu needs to be fixed. However, this is just for the qemu case. Not the best solution.

Quoted 17 lines · Stefan Hajnoczi
Quoted 7 lines
Quoted 3 lines

I'm not sure that we should stick on the current implementation of the sdt.h. I think we'd better modify the sdt.h to replace such semaphores with checking whether the tracepoint is changed from nop.

I like this option. The only implication is that all userspace tracing needs to go through uprobes if we want to support multiple consumers tracing the same address.

This limitation is practically true already, since sharing consumers have to negotiate the breakpoint anyway.

If we can find a better way to handle semaphores, we at systemtap will welcome sdt.h improvements. On the face of it, checking one's own NOP for modification sounds pretty elegant, but I'm not convinced that it's possible in practice.

Quoted 5 lines

For one, it requires arch specific knowledge in sdt.h of what the NOP or breakpoint looks like, whereas sdt.h currently only knows whether to use NOP or NOP 0, without knowledge of how that's encoded. And this gets trickier with archs like IA64 where you're part of a bundle. So this much is hard, but not impossible.

Even though, we can start with x86, which is currently one and only one platform supporting uprobes :)
Maybe we can prepare asm/sdt.h for describing arch-dep code.

Quoted 7 lines

Another issue is that there's not an easy compile-time correlation between semaphore checks and probe locations, nor is it necessarily a 1:1 mapping. The FOO_ENABLED() and PROBE_FOO() code blocks are distinct, and the compiler can do many tricks with them, loop unrolling, function specialization, etc. And if we start placing constraints to prevent this, then I think we'll be impacting code-gen of the application more than we'd like.

Perhaps, we can use the constructor attribute for that purpose.

__attribute__((constructor)) FOO_init() {
	/* Search FOO tracepoint address from tracepoint table(like extable) */
	FOO_sem = __find_first_trace_point("FOO");
}

This sets the address of first tracepoint of FOO to FOO_sem. :)

Thank you,

Signature · 11 lines
Masami HIRAMATSU
Software Platform Research Dept. Linux Technology Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#51 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 8/26] x86: analyze instruction and determine fixups.

Quoted 17 lines · Masami Hiramatsu
Quoted 17 lines · Stefan Hajnoczi
Quoted 7 lines
Quoted 3 lines

I'm not sure that we should stick on the current implementation of the sdt.h. I think we'd better modify the sdt.h to replace such semaphores with checking whether the tracepoint is changed from nop.

I like this option. The only implication is that all userspace tracing needs to go through uprobes if we want to support multiple consumers tracing the same address.

This limitation is practically true already, since sharing consumers have to negotiate the breakpoint anyway.

If we can find a better way to handle semaphores, we at systemtap will welcome sdt.h improvements. On the face of it, checking one's own NOP for modification sounds pretty elegant, but I'm not convinced that it's possible in practice.

Quoted 8 lines
Quoted 5 lines

For one, it requires arch specific knowledge in sdt.h of what the NOP or breakpoint looks like, whereas sdt.h currently only knows whether to use NOP or NOP 0, without knowledge of how that's encoded. And this gets trickier with archs like IA64 where you're part of a bundle. So this much is hard, but not impossible.

Even though, we can start with x86, which is currently one and only one platform supporting uprobes :)

This inode-based uprobes only supports x86 so far. The utrace-based uprobes also supports s390 and ppc, which you can bet Srikar will be tasked with here before long...

But uprobes is not the only sdt.h consumer anyway. GDB also supports sdt.h in its targets, covering x86, ppc, s390, and arm so far IIRC.

Quoted 1 line

Maybe we can prepare asm/sdt.h for describing arch-dep code.

In sys/sdt.h itself, these would probably be short enough snippets that we can just add each arch's check directly. I'm just averse to adding arch-dependent stuff unless we have to, but maybe we need it.

Quoted 8 lines
Quoted 7 lines

Another issue is that there's not an easy compile-time correlation between semaphore checks and probe locations, nor is it necessarily a 1:1 mapping. The FOO_ENABLED() and PROBE_FOO() code blocks are distinct, and the compiler can do many tricks with them, loop unrolling, function specialization, etc. And if we start placing constraints to prevent this, then I think we'll be impacting code-gen of the application more than we'd like.

Quoted 8 lines

Perhaps, we can use the constructor attribute for that purpose.

__attribute__((constructor)) FOO_init() {
	/* Search FOO tracepoint address from tracepoint table(like extable) */
	FOO_sem = __find_first_trace_point("FOO");
}

This sets the address of first tracepoint of FOO to FOO_sem. :)

This works as long as all FOO instances are enabled altogether, and as long as FOO events are not in constructors themselves. Those are minor and probably reasonable limitations, just need to be acknowledged. And I'm not sure how complicated __find_first_trace_point will be.

This adds a bit of start-up overhead too, but the libraries that strongly care about this (like glibc and libgcc) are not the ones who are using ENABLED() checks.

So OK, can you flesh out or prototype what __find_first_trace_point and its tracepoint table should look like? If you can demonstrate these, then I'd be willing to try modifying bin/dtrace and sys/sdt.h.

The good news is that if we figure this out, then the kernel can be completely SDT-agnostic -- it's just like any other address to probe. I hope we can also make it transparent to existing sdt.h consumers, so it just looks like there's no semaphore-counter to manipulate.

Josh

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#52 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 8/26] x86: analyze instruction and determine fixups.

In reply to Josh Stone
Quoted 24 lines · Josh Stone
Quoted 8 lines · Masami Hiramatsu
Quoted 6 lines

Masami looked at this and found that SystemTap sdt.h currently requires an extra userspace memory store in order to activate probes. Each probe has a "semaphore" 16-bit counter which applications may test before hitting the probe itself. This is used to avoid overhead in applications that do expensive argument processing (e.g. creating strings) for probes.

Indeed, originally, those semaphores designed for such use cases. However, some applications *always* use it (e.g. qemu-kvm).

I found that qemu-kvm generates its tracepoints like this:

  static inline void trace_$name($args) {
      if (QEMU_${nameupper}_ENABLED()) {
          QEMU_${nameupper}($argnames);
      }
  }

In that case, the $args are always computed to call the inline, so you'll basically just get a memory read, jump, NOP. There's no benefit from checking ENABLED() here, and removing it would leave only the NOP.
Even if you invent an improved mechanism for ENABLED(), that doesn't
change the fact that it's doing useless work here.

Quoted 4 lines

So in this case, it may be better to patch qemu, assuming my statements hold for DTrace's implementation on other platforms too. The ENABLED() guard still does have other genuine uses though, as with the string preparation in Python's probes.

I will get qemu fixed.

Stefan

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#53 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 8/26] x86: analyze instruction and determine fixups.

In reply to Stefan Hajnoczi
Quoted 7 lines · Stefan Hajnoczi <stefanha <at> linux.vnet.ibm.com> writes:
Quoted 2 lines · Christoph Hellwig

Do we now have sdt.h support for uprobes? That's one of the killer features that always seemed to get postponed.

Not yet but it's a question of doing roughly what SystemTap does to parse the appropriate ELF sections and then putting those probes into uprobes.

GDB now also implements this:
http://sourceware.org/ml/gdb-patches/2011-04/msg00036.html

Roland also posted a simple C based parser when he first suggested the new format:
http://www.sourceware.org/ml/systemtap/2010-q3/msg00145.html

There is a complete description of the elf note section, argument format and semaphore handling at:
http://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation

Cheers,

Mark

#54 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 8/26] x86: analyze instruction and determine fixups.

In reply to Srikar Dronamraju

Hi Srikar,

I noticed that this produces a compiler warning on i686 from test_bit -> variable_test_bit, that I think must be addressed. It is similar to something I fixed earlier in SystemTap's uprobes, but at that time I didn't fully analyze the issue, so I'll attempt it now.

Masami, please note that what I describe below also happens on kprobes' bitvector twobyte_is_boostable.

On 09/20/2011 05:01 AM, Srikar Dronamraju wrote:
[...]

Quoted 1 line

+static const u32 good_insns_64[256 / 32] = {

[...]

Quoted 1 line

+static const u32 good_insns_32[256 / 32] = {

[...]

Quoted 1 line

+static const u32 good_2byte_insns[256 / 32] = {

[...]

Quoted 7 lines
arch/x86/kernel/uprobes.c new file +7 -0
+static int validate_insn_32bits(struct uprobe *uprobe, struct insn *insn)
+{
+	insn_init(insn, uprobe->insn, false);
+
+	/* Skip good instruction prefixes; reject "bad" ones. */
+	insn_get_opcode(insn);
+	if (is_prefix_bad(insn))

arch/x86/kernel/uprobes.c line 207

Quoted 10 lines
arch/x86/kernel/uprobes.c new file +10 -0
+		return -ENOTSUPP;
+	if (test_bit(OPCODE1(insn), (unsigned long *) good_insns_32))
+		return 0;
+	if (insn->opcode.nbytes == 2) {
+		if (test_bit(OPCODE2(insn),
+					(unsigned long *) good_2byte_insns))
+			return 0;
+	}
+	return -ENOTSUPP;
+}

gcc version 4.6.0 20110603 (Red Hat 4.6.0-10) (GCC) says:

Quoted 7 lines

CC arch/x86/kernel/uprobes.o
In file included from include/linux/bitops.h:22:0,

                 from include/linux/kernel.h:17,
                 from arch/x86/kernel/uprobes.c:24:

/home/jistone/linux-2.6/arch/x86/include/asm/bitops.h: In function ?analyze_insn?:
/home/jistone/linux-2.6/arch/x86/include/asm/bitops.h:319:2: warning: use of memory input without lvalue in asm operand 1 is deprecated [enabled by default] /home/jistone/linux-2.6/arch/x86/include/asm/bitops.h:319:2: warning: use of memory input without lvalue in asm operand 1 is deprecated [enabled by default]

That's from variable_test_bit, whose second argument is volatile const unsigned long *, then referenced with asm "m" (*(unsigned long *)addr).

The fix I used in SystemTap's case was to make the bitvectors volatile as well. But now I want to better know *why* this fix works. There's no real difference in code-gen:

Quoted 12 lines
(quoted diff) +2 -2
@@ -91,8 +91,8 @@ Disassembly of section .text:
   63:	90                   	nop
   64:	8d 74 26 00          	lea    0x0(%esi,%eiz,1),%esi
   68:	0f b6 45 bc          	movzbl -0x44(%ebp),%eax
-  6c:	0f a3 05 00 00 00 00 	bt     %eax,0x0
-			6f: R_386_32	.rodata.cst4
+  6c:	0f a3 05 20 00 00 00 	bt     %eax,0x20
+			6f: R_386_32	.data
   73:	19 c0                	sbb    %eax,%eax
   75:	85 c0                	test   %eax,%eax
   77:	75 1c                	jne    95 <analyze_insn+0x95>

Review Disassembly of section .text: · line 107

Quoted 10 lines
(quoted diff) +2 -2
@@ -100,8 +100,8 @@ Disassembly of section .text:
   7d:	b8 f4 fd ff ff       	mov    $0xfffffdf4,%eax
   82:	75 d9                	jne    5d <analyze_insn+0x5d>
   84:	0f b6 55 bd          	movzbl -0x43(%ebp),%edx
-  88:	0f a3 15 04 00 00 00 	bt     %edx,0x4
-			8b: R_386_32	.rodata.cst4
+  88:	0f a3 15 00 00 00 00 	bt     %edx,0x0
+			8b: R_386_32	.data
   8f:	19 d2                	sbb    %edx,%edx
   91:	85 d2                	test   %edx,%edx
   93:	74 c8                	je     5d <analyze_insn+0x5d>

The volatile makes the bitvectors move from .rodata to .data, not all that surprising, I guess. Then I figured out that the former case only has the first word of good_insns_32 and good_2byte_insns, where the latter volatile case keeps the whole thing.

as-is:

Quoted 2 lines

Contents of section .rodata.cst4:
0000 7f7f7f7f 00c0fffe ........

with volatile:

Quoted 7 lines

Contents of section .data:

 0000 00c0fffe 0fff0e00 ffffffff ffffffc0  ................
 0010 ffffffff 3fbffffe fffffeff fffffe7f  ....?...........
 0020 7f7f7f7f bfbfbfbf ffffffff 370fffff  ............7...
 0030 ffffffff ffffffff ff0fbfff 0f0fecf3  ................
 0040 3f3f3f3f 3f3f3f3f 0000ffff 380fffff  ????????....8...
 0050 fbffffff ffffffff cf0f8fff 0f0fecf3  ................

So declaring the bitvectors volatile makes gcc to keep them around in full. Otherwise it apparently looks to gcc like only the first word is used by variable_test_bit's asm statement.

On x86_64, the warning doesn't appear, and even in .rodata the bitvectors appear in full. I'm guessing that the pointer aliasing from u32* to a 64-bit unsigned long* makes gcc forgo the data elision.

I wonder if variable_test_bit() could/should force aliasing to fix this for all callers. But for now, marking volatile does the trick.

Thanks,
Josh

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#55 lore raw

PATCH 1/1 x86: Make variable_test_bit reference all of *addr

This casts the addr that's fed to asm into a struct-array pointer, so gcc knows that more than just the first long is needed. Since there's no fixed size for all callers, an arbitrary size is chosen just to ensure that it's probably good enough.

I noticed this warning on i686, with gcc-4.6.1-9.fc15:

CC arch/x86/kernel/kprobes.o
In file included from include/linux/bitops.h:22:0,

                 from include/linux/kernel.h:17,
                 from [...]/arch/x86/include/asm/percpu.h:44,
                 from [...]/arch/x86/include/asm/current.h:5,
                 from [...]/arch/x86/include/asm/processor.h:15,
                 from [...]/arch/x86/include/asm/atomic.h:6,
                 from include/linux/atomic.h:4,
                 from include/linux/mutex.h:18,
                 from include/linux/notifier.h:13,
                 from include/linux/kprobes.h:34,
                 from arch/x86/kernel/kprobes.c:43:

[...]/arch/x86/include/asm/bitops.h: In function ‘can_boost.part.1’:
[...]/arch/x86/include/asm/bitops.h:319:2: warning: use of memory input without lvalue in asm operand 1 is deprecated [enabled by default]

In investigating the impact of this warning, I discovered that only the first long of the 32-byte twobyte_is_boostable[] was making into the object file.

Jakub advised that variable_test_bit is incorrectly telling gcc that its asm only uses a single long from the addr pointer, and he suggested the struct-array cast to broaden the memory reference.

Cc Jakub Jelinek <[email protected]>

---

An alternate fix would be to make kprobes' twobyte_is_boostable[] volatile, which forces gcc to keep it around. I feel that's treating the symptom though, rather than the cause in variable_test_bit().

IMO this is also a good candidate for -stable, for fixing the obviously bad data behavior, but I'll let others judge...

---
 arch/x86/include/asm/bitops.h |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)
arch/x86/include/asm/bitops.h +2 -1
index 1775d6e..0565371 100644
@@ -319,7 +319,8 @@ static inline int variable_test_bit(int nr, volatile const unsigned long *addr)
 	asm volatile("bt %2,%1\n\t"
 		     "sbb %0,%0"
 		     : "=r" (oldbit)
-		     : "m" (*(unsigned long *)addr), "Ir" (nr));
+		     : "m" (*(struct { unsigned long _[0x10000]; } *)addr),
+		       "Ir" (nr));
 
 	return oldbit;
 }
Signature · 1 line
1.7.6.4
#56 lore raw

Re: [PATCH] x86: Make variable_test_bit reference all of *addr

This is concerning... the kernel relies heavily on asm volatile being a universal memory consumer. If that is suddenly broken, we are f*** in many, many, MANY places in the kernel all of a sudden!

Quoted 67 lines · Josh Stone

This casts the addr that's fed to asm into a struct-array pointer, so gcc knows that more than just the first long is needed. Since there's no fixed size for all callers, an arbitrary size is chosen just to ensure that it's probably good enough.

I noticed this warning on i686, with gcc-4.6.1-9.fc15:

CC arch/x86/kernel/kprobes.o
In file included from include/linux/bitops.h:22:0,

                from include/linux/kernel.h:17,
                from [...]/arch/x86/include/asm/percpu.h:44,
                from [...]/arch/x86/include/asm/current.h:5,
                from [...]/arch/x86/include/asm/processor.h:15,
                from [...]/arch/x86/include/asm/atomic.h:6,
                from include/linux/atomic.h:4,
                from include/linux/mutex.h:18,
                from include/linux/notifier.h:13,
                from include/linux/kprobes.h:34,
                from arch/x86/kernel/kprobes.c:43:

[...]/arch/x86/include/asm/bitops.h: In function ‘can_boost.part.1’:
[...]/arch/x86/include/asm/bitops.h:319:2: warning: use of memory input without lvalue in asm operand 1 is deprecated [enabled by default]

In investigating the impact of this warning, I discovered that only the first long of the 32-byte twobyte_is_boostable[] was making into the object file.

Jakub advised that variable_test_bit is incorrectly telling gcc that its
asm only uses a single long from the addr pointer, and he suggested the struct-array cast to broaden the memory reference.

Cc Jakub Jelinek <[email protected]>

---

An alternate fix would be to make kprobes' twobyte_is_boostable[] volatile, which forces gcc to keep it around. I feel that's treating the symptom though, rather than the cause in variable_test_bit().

IMO this is also a good candidate for -stable, for fixing the obviously bad
data behavior, but I'll let others judge...

---
arch/x86/include/asm/bitops.h | 3 ++-

1 files changed, 2 insertions(+), 1 deletions(-)
arch/x86/include/asm/bitops.h +2 -1
index 1775d6e..0565371 100644
@@ -319,7 +319,8 @@ static inline int variable_test_bit(int nr,
 volatile const unsigned long *addr)
 	asm volatile("bt %2,%1\n\t"
 		     "sbb %0,%0"
 		     : "=r" (oldbit)
-		     : "m" (*(unsigned long *)addr), "Ir" (nr));
+		     : "m" (*(struct { unsigned long _[0x10000]; } *)addr),
+		       "Ir" (nr));
 
 	return oldbit;

}

Signature · 1 line
1.7.6.4
Signature · 1 line
Sent from my Android phone with K-9 Mail. Please excuse my brevity.
#57 lore raw

Re: [PATCH] x86: Make variable_test_bit reference all of *addr

Quoted 1 line · "[email protected]" <[email protected]> writes:

This is concerning... the kernel relies heavily on asm volatile being a universal memory consumer. If that is suddenly broken, we are f*** in many, many, MANY places in the kernel all of a sudden!

I don't think that's true. We generally add "memory" clobbers for this purpose. asm volatile just means "don't move"

Just this one doesn't have it for unknown reasons (someone overoptimizing?)

-Andi

Signature · 1 line
[email protected] -- Speaking for myself only
#58 lore raw

Re: [PATCH] x86: Make variable_test_bit reference all of *addr

Quoted 2 lines · Josh Stone

[...]/arch/x86/include/asm/bitops.h: In function ‘can_boost.part.1’:
[...]/arch/x86/include/asm/bitops.h:319:2: warning: use of memory input without lvalue in asm operand 1 is deprecated [enabled by default]

I probably should have noted that Jakub also blamed gcc's behavior, for transforming const memory into a literal constant and then complaining about lvalues. He fixed that upstream, and applied to 4.6.1-10.fc16:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50571

I didn't figure out any automated way to detect the problem in general (apart from the presence of that warning), but here's how I'm checking kprobes' in particular.

Using gcc-4.6.1-9.fc15.i686:

Quoted 11 lines
$ objdump -rd arch/x86/kernel/kprobes.o | grep -A1 -w bt
     551:	0f a3 05 00 00 00 00 	bt     %eax,0x0
			554: R_386_32	.rodata.cst4
$ objdump -s -j .rodata -j .rodata.cst4 arch/x86/kernel/kprobes.o

arch/x86/kernel/kprobes.o: file format elf32-i386

Contents of section .rodata:
0000 02000000 ....
Contents of section .rodata.cst4:
0000 4c030000 L...

Using gcc-4.6.1-9.fc15.i686, with my variable_test_bit patch:

Quoted 5 lines
$ objdump -rd arch/x86/kernel/kprobes.o | grep -A1 -w bt
     551:	0f a3 05 20 00 00 00 	bt     %eax,0x20
			554: R_386_32	.rodata
$ objdump -s -j .rodata arch/x86/kernel/kprobes.o
Quoted 7 lines

arch/x86/kernel/kprobes.o: file format elf32-i386

Contents of section .rodata:

 0000 02000000 00000000 00000000 00000000  ................
 0010 00000000 00000000 00000000 00000000  ................
 0020 4c030000 0f000200 ffff0000 ffcff0c0  L...............
 0030 0000ffff 3bbbfff8 03ff2ebb 26bb2e77  ....;.......&..w

Using gcc-4.6.1-10.fc16.i686, with Jakub's fix, without my patch:

Quoted 5 lines
$ objdump -rd arch/x86/kernel/kprobes.o | grep -A1 -w bt
     551:	0f a3 05 20 00 00 00 	bt     %eax,0x20
			554: R_386_32	.rodata
$ objdump -s -j .rodata arch/x86/kernel/kprobes.o
Quoted 7 lines

arch/x86/kernel/kprobes.o: file format elf32-i386

Contents of section .rodata:

 0000 02000000 00000000 00000000 00000000  ................
 0010 00000000 00000000 00000000 00000000  ................
 0020 4c030000 0f000200 ffff0000 ffcff0c0  L...............
 0030 0000ffff 3bbbfff8 03ff2ebb 26bb2e77  ....;.......&..w

There's some zero-padding on the previous .rodata contents, but then starting at 0x20 it now has the full 32-bytes of twobyte_is_boostable[].

So Jakub's gcc change fixes this issue independently of my patch, but I got the impression from him that the way the kernel is expressing this is still in the realm of "gcc might break your expectations here". If that's not the case, then my patch here is only needed if you want to cope with prior broken versions. Jakub, do you have an idea of the range of gcc versions broken in this way?

Quoted 9 lines · Andi Kleen
Quoted 3 lines · "[email protected]" <[email protected]> writes:

This is concerning... the kernel relies heavily on asm volatile being a universal memory consumer. If that is suddenly broken, we are f*** in many, many, MANY places in the kernel all of a sudden!

I don't think that's true. We generally add "memory" clobbers for this purpose. asm volatile just means "don't move"

Just this one doesn't have it for unknown reasons (someone overoptimizing?)

Which overoptimizing part are you referring to? The only part of variable_test_bit that's not volatile is "m" (*(unsigned long *)addr), and throwing volatile in that cast does nothing for the problem (at least on gcc-4.6.1-9.fc15).

We can make twobyte_is_boostable[] volatile instead, which does the trick, but that seems a kludge to me.

Josh

#59 lore raw

Re: [PATCH] x86: Make variable_test_bit reference all of *addr

I mean the volatile in "asm volatile".

Quoted 92 lines · Josh Stone
Quoted 2 lines · Josh Stone

[...]/arch/x86/include/asm/bitops.h: In function ‘can_boost.part.1’:
[...]/arch/x86/include/asm/bitops.h:319:2: warning: use of memory

input without lvalue in asm operand 1 is deprecated [enabled by default]

I probably should have noted that Jakub also blamed gcc's behavior, for transforming const memory into a literal constant and then complaining about lvalues. He fixed that upstream, and applied to 4.6.1-10.fc16:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50571

I didn't figure out any automated way to detect the problem in general (apart from the presence of that warning), but here's how I'm checking kprobes' in particular.

Using gcc-4.6.1-9.fc15.i686:

Quoted 11 lines
$ objdump -rd arch/x86/kernel/kprobes.o | grep -A1 -w bt
     551:	0f a3 05 00 00 00 00 	bt     %eax,0x0
			554: R_386_32	.rodata.cst4
$ objdump -s -j .rodata -j .rodata.cst4 arch/x86/kernel/kprobes.o

arch/x86/kernel/kprobes.o: file format elf32-i386

Contents of section .rodata:
0000 02000000 ....
Contents of section .rodata.cst4:
0000 4c030000 L...

Using gcc-4.6.1-9.fc15.i686, with my variable_test_bit patch:

Quoted 5 lines
$ objdump -rd arch/x86/kernel/kprobes.o | grep -A1 -w bt
     551:	0f a3 05 20 00 00 00 	bt     %eax,0x20
			554: R_386_32	.rodata
$ objdump -s -j .rodata arch/x86/kernel/kprobes.o
Quoted 7 lines

arch/x86/kernel/kprobes.o: file format elf32-i386

Contents of section .rodata:

 0000 02000000 00000000 00000000 00000000  ................
 0010 00000000 00000000 00000000 00000000  ................
 0020 4c030000 0f000200 ffff0000 ffcff0c0  L...............
 0030 0000ffff 3bbbfff8 03ff2ebb 26bb2e77  ....;.......&..w

Using gcc-4.6.1-10.fc16.i686, with Jakub's fix, without my patch:

Quoted 5 lines
$ objdump -rd arch/x86/kernel/kprobes.o | grep -A1 -w bt
     551:	0f a3 05 20 00 00 00 	bt     %eax,0x20
			554: R_386_32	.rodata
$ objdump -s -j .rodata arch/x86/kernel/kprobes.o
Quoted 7 lines

arch/x86/kernel/kprobes.o: file format elf32-i386

Contents of section .rodata:

 0000 02000000 00000000 00000000 00000000  ................
 0010 00000000 00000000 00000000 00000000  ................
 0020 4c030000 0f000200 ffff0000 ffcff0c0  L...............
 0030 0000ffff 3bbbfff8 03ff2ebb 26bb2e77  ....;.......&..w

There's some zero-padding on the previous .rodata contents, but then starting at 0x20 it now has the full 32-bytes of twobyte_is_boostable[].

So Jakub's gcc change fixes this issue independently of my patch, but I got the impression from him that the way the kernel is expressing this is still in the realm of "gcc might break your expectations here". If that's not the case, then my patch here is only needed if you want to cope with prior broken versions. Jakub, do you have an idea of the range of gcc versions broken in this way?

Quoted 2 lines · Andi Kleen
Quoted 1 line · "[email protected]" <[email protected]> writes:

This is concerning... the kernel relies heavily on asm volatile

being

Quoted 1 line
Quoted 1 line

a universal memory consumer. If that is suddenly broken, we are

f***

Quoted 3 lines
Quoted 1 line

in many, many, MANY places in the kernel all of a sudden!

I don't think that's true. We generally add "memory" clobbers for

this

Quoted 3 lines

purpose. asm volatile just means "don't move"

Just this one doesn't have it for unknown reasons (someone

overoptimizing?)

Which overoptimizing part are you referring to? The only part of variable_test_bit that's not volatile is "m" (*(unsigned long *)addr), and throwing volatile in that cast does nothing for the problem (at least on gcc-4.6.1-9.fc15).

We can make twobyte_is_boostable[] volatile instead, which does the trick, but that seems a kludge to me.

Josh

Signature · 1 line
Sent from my Android phone with K-9 Mail. Please excuse my brevity.
#60 lore raw

Re: [PATCH] x86: Make variable_test_bit reference all of *addr

Quoted 1 line · On Thu, Oct 06, 2011 at 08:12:48PM -0700, [email protected] wrote:

I mean the volatile in "asm volatile".

That volatile has nothing to do with memory. It just means "don't move much". It's actually quite vague, because the rest of the function can still move around.

-Andi

#61 lore raw

Re: [PATCH] x86: Make variable_test_bit reference all of *addr

In reply to Josh Stone

Hi Josh,

Thank you for reporting details :)

Quoted 2 lines · Josh Stone

We can make twobyte_is_boostable[] volatile instead, which does the trick, but that seems a kludge to me.

I think this time we'd better to do this (with comments), since this may not affect most of bitmap users using it for checking dynamically allocated bit-flags. kprobe's bitmap usage seems a special case.

Thank you,

Signature · 4 lines
Masami HIRAMATSU
Software Platform Research Dept. Linux Technology Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]
#62 lore raw

Re: [PATCH] x86: Make variable_test_bit reference all of *addr

In reply to Josh Stone

Hi Josh,

Thank you for reporting details :)

Quoted 2 lines · Josh Stone

We can make twobyte_is_boostable[] volatile instead, which does the trick, but that seems a kludge to me.

I think this time we'd better to do this (with comments), since this may not affect most of bitmap users using it for checking dynamically allocated bit-flags. kprobe's bitmap usage seems a special case.

Thank you,

Signature · 4 lines
Masami HIRAMATSU
Software Platform Research Dept. Linux Technology Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]
#63 lore raw

PATCH 1/1 x86: Make kprobes' twobyte_is_boostable volatile

When compiling an i386_defconfig kernel with gcc-4.6.1-9.fc15.i686, I noticed a warning about the asm operand for test_bit in kprobes' can_boost. I discovered that this caused only the first long of twobyte_is_boostable[] to be output.

Jakub filed and fixed gcc PR50571 to correct the warning and this output issue. But to solve it for less current gcc, we can make kprobes' twobyte_is_boostable[] volatile, and it won't be optimized out.

Before:

CC arch/x86/kernel/kprobes.o
In file included from include/linux/bitops.h:22:0,

                   from include/linux/kernel.h:17,
                   from [...]/arch/x86/include/asm/percpu.h:44,
                   from [...]/arch/x86/include/asm/current.h:5,
                   from [...]/arch/x86/include/asm/processor.h:15,
                   from [...]/arch/x86/include/asm/atomic.h:6,
                   from include/linux/atomic.h:4,
                   from include/linux/mutex.h:18,
                   from include/linux/notifier.h:13,
                   from include/linux/kprobes.h:34,
                   from arch/x86/kernel/kprobes.c:43:

[...]/arch/x86/include/asm/bitops.h: In function ‘can_boost.part.1’:
[...]/arch/x86/include/asm/bitops.h:319:2: warning: use of memory input without lvalue in asm operand 1 is deprecated [enabled by default]

  $ objdump -rd arch/x86/kernel/kprobes.o | grep -A1 -w bt
       551:	0f a3 05 00 00 00 00 	bt     %eax,0x0
                          554: R_386_32	.rodata.cst4
  $ objdump -s -j .rodata.cst4 -j .data arch/x86/kernel/kprobes.o

arch/x86/kernel/kprobes.o: file format elf32-i386

Contents of section .data:
0000 48000000 00000000 00000000 00000000 H...............
Contents of section .rodata.cst4:
0000 4c030000 L...

Only a single long of twobyte_is_boostable[] is in the object file.

After, with volatile:

  $ objdump -rd arch/x86/kernel/kprobes.o | grep -A1 -w bt
       551:	0f a3 05 20 00 00 00 	bt     %eax,0x20
                          554: R_386_32	.data
  $ objdump -s -j .rodata.cst4 -j .data arch/x86/kernel/kprobes.o

arch/x86/kernel/kprobes.o: file format elf32-i386

Contents of section .data:

   0000 48000000 00000000 00000000 00000000  H...............
   0010 00000000 00000000 00000000 00000000  ................
   0020 4c030000 0f000200 ffff0000 ffcff0c0  L...............
   0030 0000ffff 3bbbfff8 03ff2ebb 26bb2e77  ....;.......&..w

Now all 32 bytes are output into .data instead.

Cc Masami Hiramatsu <[email protected]>
Cc Jakub Jelinek <[email protected]>
---
 arch/x86/kernel/kprobes.c |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)
arch/x86/kernel/kprobes.c +3 -1
index f1a6244..c0ed3d9 100644
@@ -75,8 +75,10 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
 	/*
 	 * Undefined/reserved opcodes, conditional jump, Opcode Extension
 	 * Groups, and some special opcodes can not boost.
+	 * This is volatile to keep gcc from statically optimizing it out, as
+	 * variable_test_bit makes gcc think only *(unsigned long*) is used.
 	 */
-static const u32 twobyte_is_boostable[256 / 32] = {
+static volatile const u32 twobyte_is_boostable[256 / 32] = {
 	/*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f          */
 	/*      ----------------------------------------------          */
 	W(0x00, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0) | /* 00 */
Signature · 1 line
1.7.6.4
Acked-by on v1 1/1
#64 lore raw

Re: [PATCH] x86: Make kprobes' twobyte_is_boostable volatile

Quoted 8 lines · Josh Stone

When compiling an i386_defconfig kernel with gcc-4.6.1-9.fc15.i686, I noticed a warning about the asm operand for test_bit in kprobes' can_boost. I discovered that this caused only the first long of twobyte_is_boostable[] to be output.

Jakub filed and fixed gcc PR50571 to correct the warning and this output issue. But to solve it for less current gcc, we can make kprobes' twobyte_is_boostable[] volatile, and it won't be optimized out.

Uh, this should be an urgent fix.

Thanks a lot!

Quoted 75 lines

Before:

CC arch/x86/kernel/kprobes.o
In file included from include/linux/bitops.h:22:0,

                   from include/linux/kernel.h:17,
                   from [...]/arch/x86/include/asm/percpu.h:44,
                   from [...]/arch/x86/include/asm/current.h:5,
                   from [...]/arch/x86/include/asm/processor.h:15,
                   from [...]/arch/x86/include/asm/atomic.h:6,
                   from include/linux/atomic.h:4,
                   from include/linux/mutex.h:18,
                   from include/linux/notifier.h:13,
                   from include/linux/kprobes.h:34,
                   from arch/x86/kernel/kprobes.c:43:

[...]/arch/x86/include/asm/bitops.h: In function ‘can_boost.part.1’:
[...]/arch/x86/include/asm/bitops.h:319:2: warning: use of memory input without lvalue in asm operand 1 is deprecated [enabled by default]

  $ objdump -rd arch/x86/kernel/kprobes.o | grep -A1 -w bt
       551:	0f a3 05 00 00 00 00 	bt     %eax,0x0
                          554: R_386_32	.rodata.cst4
  $ objdump -s -j .rodata.cst4 -j .data arch/x86/kernel/kprobes.o

arch/x86/kernel/kprobes.o: file format elf32-i386

Contents of section .data:

   0000 48000000 00000000 00000000 00000000  H...............
  Contents of section .rodata.cst4:
   0000 4c030000                             L...

Only a single long of twobyte_is_boostable[] is in the object file.

After, with volatile:

  $ objdump -rd arch/x86/kernel/kprobes.o | grep -A1 -w bt
       551:	0f a3 05 20 00 00 00 	bt     %eax,0x20
                          554: R_386_32	.data
  $ objdump -s -j .rodata.cst4 -j .data arch/x86/kernel/kprobes.o

arch/x86/kernel/kprobes.o: file format elf32-i386

Contents of section .data:

   0000 48000000 00000000 00000000 00000000  H...............
   0010 00000000 00000000 00000000 00000000  ................
   0020 4c030000 0f000200 ffff0000 ffcff0c0  L...............
   0030 0000ffff 3bbbfff8 03ff2ebb 26bb2e77  ....;.......&..w

Now all 32 bytes are output into .data instead.

Cc Masami Hiramatsu <[email protected]>
Cc Jakub Jelinek <[email protected]>
---
 arch/x86/kernel/kprobes.c |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)
arch/x86/kernel/kprobes.c +3 -1
index f1a6244..c0ed3d9 100644
@@ -75,8 +75,10 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
 	/*
 	 * Undefined/reserved opcodes, conditional jump, Opcode Extension
 	 * Groups, and some special opcodes can not boost.
+	 * This is volatile to keep gcc from statically optimizing it out, as
+	 * variable_test_bit makes gcc think only *(unsigned long*) is used.
 	 */
-static const u32 twobyte_is_boostable[256 / 32] = {
+static volatile const u32 twobyte_is_boostable[256 / 32] = {
 	/*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f          */
 	/*      ----------------------------------------------          */
 	W(0x00, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0) | /* 00 */
Signature · 4 lines
Masami HIRAMATSU
Software Platform Research Dept. Linux Technology Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]
#65 lore raw

Re: [PATCH] x86: Make variable_test_bit reference all of *addr

In reply to Andi Kleen

Memory clobbers are universal producers, not consumers.

Quoted 17 lines · Andi Kleen
Quoted 1 line · "[email protected]" <[email protected]> writes:

This is concerning... the kernel relies heavily on asm volatile being

a universal memory consumer. If that is suddenly broken, we are f*** in many, many, MANY places in the kernel all of a sudden!

I don't think that's true. We generally add "memory" clobbers for this purpose. asm volatile just means "don't move"

Just this one doesn't have it for unknown reasons (someone overoptimizing?)

-Andi

Signature · 1 line
[email protected] -- Speaking for myself only
Signature · 1 line
Sent from my Android phone with K-9 Mail. Please excuse my brevity.
maintainer
#66 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 8/26] x86: analyze instruction and determine fixups.

In reply to Srikar Dronamraju

arch/x86/Kconfig config ARCH_CPU_PROBE_RELEASE · line 255

Quoted 10 lines · Srikar Dronamraju
arch/x86/Kconfig +3 -0
@@ -250,6 +250,9 @@ config ARCH_CPU_PROBE_RELEASE
 	def_bool y
 	depends on HOTPLUG_CPU
 
+config ARCH_SUPPORTS_UPROBES
+	def_bool y
+

It seems you should also change the INSTRUCTION_DECODER entry.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#67 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 8/26] x86: analyze instruction and determine fixups.

Quoted 8 lines · Oleg Nesterov
Quoted 7 lines · Srikar Dronamraju
arch/x86/Kconfig +0 -0
@@ -250,6 +250,9 @@ config ARCH_CPU_PROBE_RELEASE
 	def_bool y
 	depends on HOTPLUG_CPU
 
Quoted 5 lines

arch/x86/Kconfig config ARCH_CPU_PROBE_RELEASE · line 255

Quoted 3 lines
arch/x86/Kconfig +3 -0
+config ARCH_SUPPORTS_UPROBES
+	def_bool y
+

It seems you should also change the INSTRUCTION_DECODER entry.

Okay will do.

Signature · 2 lines
Thanks and Regards
Srikar
#68 lore raw

PATCH v5 9/26 Uprobes: Background page replacement.

In reply to Srikar Dronamraju

Provides Background page replacement by
- cow the page that needs replacement.
- modify a copy of the cowed page.
- replace the cow page with the modified page
- flush the page tables.

Also provides additional routines to read an opcode from a given virtual address and for verifying if a instruction is a breakpoint instruction.

---
 include/linux/uprobes.h |    4 +
 kernel/uprobes.c        |  268 ++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 266 insertions(+), 6 deletions(-)
include/linux/uprobes.h +4 -0
index 2548b94..2c139f3 100644
@@ -29,6 +29,7 @@ struct vm_area_struct;
 #ifdef CONFIG_ARCH_SUPPORTS_UPROBES
 #include <asm/uprobes.h>
 #else
+typedef u8 uprobe_opcode_t;
 struct uprobe_arch_info {};
 #define MAX_UINSN_BYTES 4
 #endif
@@ -74,6 +75,9 @@ extern int __weak set_bkpt(struct task_struct *tsk, struct uprobe *uprobe,
 							unsigned long vaddr);
 extern int __weak set_orig_insn(struct task_struct *tsk,
 		struct uprobe *uprobe, unsigned long vaddr, bool verify);
+extern bool __weak is_bkpt_insn(u8 *insn);
+extern int __weak read_opcode(struct task_struct *tsk, unsigned long vaddr,
+						uprobe_opcode_t *opcode);
 extern int register_uprobe(struct inode *inode, loff_t offset,
 				struct uprobe_consumer *consumer);
 extern void unregister_uprobe(struct inode *inode, loff_t offset,
kernel/uprobes.c +262 -6

293 lines not on this page · raw message

maintainer
#69 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 9/26] Uprobes: Background page replacement.

Quoted 12 lines · Srikar Dronamraju
kernel/uprobes.c +11 -0
+int __weak read_opcode(struct task_struct *tsk, unsigned long vaddr,
+						uprobe_opcode_t *opcode)
+{
+	struct vm_area_struct *vma;
+	struct page *page;
+	void *vaddr_new;
+	int ret;
+
+	ret = get_user_pages(tsk, tsk->mm, vaddr, 1, 0, 0, &page, &vma);
+	if (ret <= 0)
+		return ret;

kernel/uprobes.c static bool valid_vma(struct vm_area_struct *vma) · line 258

Quoted 10 lines
kernel/uprobes.c +10 -0
+	ret = -EINVAL;
+
+	/*
+	 * We are interested in text pages only. Our pages of interest
+	 * should be mapped for read and execute only. We desist from
+	 * adding probes in write mapped pages since the breakpoints
+	 * might end up in the file copy.
+	 */
+	if (!valid_vma(vma))
+		goto put_out;

Another case when valid_vma() looks suspicious. We are going to restore the original instruction. We shouldn't fail (at least we shouldn't "leak" ->mm_uprobes_count) if ->vm_flags was changed between register_uprobe() and unregister_uprobe().

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#70 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 9/26] Uprobes: Background page replacement.

Quoted 20 lines · Oleg Nesterov
Quoted 19 lines · Srikar Dronamraju
kernel/uprobes.c +18 -0
+int __weak read_opcode(struct task_struct *tsk, unsigned long vaddr,
+						uprobe_opcode_t *opcode)
+{
+	struct vm_area_struct *vma;
+	struct page *page;
+	void *vaddr_new;
+	int ret;
+
+	ret = get_user_pages(tsk, tsk->mm, vaddr, 1, 0, 0, &page, &vma);
+	if (ret <= 0)
+		return ret;
+	ret = -EINVAL;
+
+	/*
+	 * We are interested in text pages only. Our pages of interest
+	 * should be mapped for read and execute only. We desist from
+	 * adding probes in write mapped pages since the breakpoints
+	 * might end up in the file copy.
Quoted 9 lines

kernel/uprobes.c static bool valid_vma(struct vm_area_struct *vma) · line 172

Quoted 3 lines
kernel/uprobes.c +3 -0
+	 */
+	if (!valid_vma(vma))
+		goto put_out;

Another case when valid_vma() looks suspicious. We are going to restore the original instruction. We shouldn't fail (at least we shouldn't "leak" ->mm_uprobes_count) if ->vm_flags was changed between register_uprobe() and unregister_uprobe().

Agree.

Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#71 lore raw

PATCH v5 10/26 x86: Set instruction pointer.

In reply to Srikar Dronamraju

Provides x86 specific routine to set the instruction pointer to the given address.

---
 arch/x86/include/asm/uprobes.h |    1 +
 arch/x86/kernel/uprobes.c      |   10 ++++++++++
 2 files changed, 11 insertions(+), 0 deletions(-)
arch/x86/include/asm/uprobes.h +1 -0
index 4295ce0..35ac9d7 100644
@@ -39,4 +39,5 @@ struct uprobe_arch_info {};
 #endif
 struct uprobe;
 extern int analyze_insn(struct task_struct *tsk, struct uprobe *uprobe);
+extern void set_instruction_pointer(struct pt_regs *regs, unsigned long vaddr);
 #endif	/* _ASM_UPROBES_H */
arch/x86/kernel/uprobes.c +10 -0
index e4fd077..e1c1dfe 100644
@@ -383,3 +383,13 @@ int analyze_insn(struct task_struct *tsk, struct uprobe *uprobe)
 	prepare_fixups(uprobe, &insn);
 	return 0;
 }
+
+/*
+ * @reg: reflects the saved state of the task
+ * @vaddr: the virtual address to jump to.
+ * Return 0 on success or a -ve number on error.
+ */
+void set_instruction_pointer(struct pt_regs *regs, unsigned long vaddr)
+{
+	regs->ip = vaddr;
+}

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#72 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 10/26] x86: Set instruction pointer.

arch/x86/include/asm/uprobes.h struct uprobe_arch_info {}; · line 42

Quoted 8 lines · Srikar Dronamraju
arch/x86/include/asm/uprobes.h +1 -0
@@ -39,4 +39,5 @@ struct uprobe_arch_info {};
 #endif
 struct uprobe;
 extern int analyze_insn(struct task_struct *tsk, struct uprobe *uprobe);
+extern void set_instruction_pointer(struct pt_regs *regs, unsigned long vaddr);

Well, this is minor, but we already have instruction_pointer_set().

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#73 lore raw

PATCH v5 11/26 x86: Introduce TIF_UPROBE FLAG.

In reply to Srikar Dronamraju

On a breakpoint or singlestep, the exception notifier will just set this thread_info FLAG so that do_notify_resume can be made aware that a breakpoint/singlestep has occurred.

---
 arch/x86/include/asm/thread_info.h |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)
arch/x86/include/asm/thread_info.h +2 -0
index a1fe5c1..aeb3e04 100644
@@ -84,6 +84,7 @@ struct thread_info {
 #define TIF_SECCOMP		8	/* secure computing */
 #define TIF_MCE_NOTIFY		10	/* notify userspace of an MCE */
 #define TIF_USER_RETURN_NOTIFY	11	/* notify kernel of userspace return */
+#define TIF_UPROBE		12	/* breakpointed or singlestepping */
 #define TIF_NOTSC		16	/* TSC is not accessible in userland */
 #define TIF_IA32		17	/* 32bit process */
 #define TIF_FORK		18	/* ret_from_fork */
@@ -107,6 +108,7 @@ struct thread_info {
 #define _TIF_SECCOMP		(1 << TIF_SECCOMP)
 #define _TIF_MCE_NOTIFY		(1 << TIF_MCE_NOTIFY)
 #define _TIF_USER_RETURN_NOTIFY	(1 << TIF_USER_RETURN_NOTIFY)
+#define _TIF_UPROBE		(1 << TIF_UPROBE)
 #define _TIF_NOTSC		(1 << TIF_NOTSC)
 #define _TIF_IA32		(1 << TIF_IA32)
 #define _TIF_FORK		(1 << TIF_FORK)

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#74 lore raw

PATCH v5 12/26 Uprobes: Handle breakpoint and Singlestep

In reply to Srikar Dronamraju

Provides routines to create/manage and free the task specific information.

Adds a hook in uprobe_notify_resume to handle breakpoint and singlestep exception.

Uprobes needs to maintain some task specific information including if a task has hit a probepoint, uprobe corresponding to the probehit, the slot where the original instruction is copied to before single-stepping.

---
 include/linux/sched.h   |    3 +
 include/linux/uprobes.h |   35 ++++++++
 kernel/fork.c           |    4 +
 kernel/uprobes.c        |  205 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 247 insertions(+), 0 deletions(-)
include/linux/sched.h +3 -0
index bc6f5f2..4f84980 100644
@@ -1569,6 +1569,9 @@ struct task_struct {
 #ifdef CONFIG_HAVE_HW_BREAKPOINT
 	atomic_t ptrace_bp_refcnt;
 #endif
+#ifdef CONFIG_UPROBES
+	struct uprobe_task *utask;
+#endif
 };
 
 /* Future-safe accessor for struct task_struct's cpus_allowed. */
include/linux/uprobes.h +35 -0
index 2c139f3..fa7eaba 100644
@@ -70,6 +70,26 @@ struct uprobe {
 	u8			insn[MAX_UINSN_BYTES];
 };
 
+enum uprobe_task_state {
+	UTASK_RUNNING,
+	UTASK_BP_HIT,
+	UTASK_SSTEP
+};
+
+/*
+ * uprobe_utask -- not a user-visible struct.
+ * Corresponds to a thread in a probed process.
+ * Guarded by uproc->mutex.
+ */
+struct uprobe_task {
+	unsigned long xol_vaddr;
+	unsigned long vaddr;
+
+	enum uprobe_task_state state;
+
+	struct uprobe *active_uprobe;
+};
+
 #ifdef CONFIG_UPROBES
 extern int __weak set_bkpt(struct task_struct *tsk, struct uprobe *uprobe,
 							unsigned long vaddr);
@@ -82,8 +102,13 @@ extern int register_uprobe(struct inode *inode, loff_t offset,
 				struct uprobe_consumer *consumer);
 extern void unregister_uprobe(struct inode *inode, loff_t offset,
 				struct uprobe_consumer *consumer);
+extern void free_uprobe_utask(struct task_struct *tsk);
 extern int mmap_uprobe(struct vm_area_struct *vma);
 extern void munmap_uprobe(struct vm_area_struct *vma);
+extern unsigned long __weak get_uprobe_bkpt_addr(struct pt_regs *regs);
+extern int uprobe_post_notifier(struct pt_regs *regs);
+extern int uprobe_bkpt_notifier(struct pt_regs *regs);
+extern void uprobe_notify_resume(struct pt_regs *regs);
 #else /* CONFIG_UPROBES is not defined */
 static inline int register_uprobe(struct inode *inode, loff_t offset,
 				struct uprobe_consumer *consumer)
@@ -101,5 +126,15 @@ static inline int mmap_uprobe(struct vm_area_struct *vma)
 static inline void munmap_uprobe(struct vm_area_struct *vma)
 {
 }
+static inline void uprobe_notify_resume(struct pt_regs *regs)
+{
+}
+static inline unsigned long get_uprobe_bkpt_addr(struct pt_regs *regs)
+{
+	return 0;
+}
+static inline void free_uprobe_utask(struct task_struct *tsk)
+{
+}
 #endif /* CONFIG_UPROBES */
 #endif	/* _LINUX_UPROBES_H */
kernel/fork.c +4 -0
index 7cc0b51..5914bc1 100644
@@ -195,6 +195,7 @@ void __put_task_struct(struct task_struct *tsk)
 	delayacct_tsk_free(tsk);
 	put_signal_struct(tsk->signal);
 
+	free_uprobe_utask(tsk);
 	if (!profile_handoff_task(tsk))
 		free_task(tsk);
 }
@@ -1285,6 +1286,9 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 	INIT_LIST_HEAD(&p->pi_state_list);
 	p->pi_state_cache = NULL;
 #endif
+#ifdef CONFIG_UPROBES
+	p->utask = NULL;
+#endif
 	/*
 	 * sigaltstack should be cleared when sharing the same VM
 	 */
kernel/uprobes.c +205 -0

230 lines not on this page · raw message

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#75 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 12/26] Uprobes: Handle breakpoint and Singlestep

Quoted 3 lines · Srikar Dronamraju

Hence provide some extra
+ * time (by way of synchronize_sched() for breakpoint hit threads to acquire
+ * the uprobes_treelock before the uprobe is removed from the rbtree.

'Some extra time' doesn't make me all warm an fuzzy inside, but instead screams we fudge around a race condition.

ISTR raising this before ;-)

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#76 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 12/26] Uprobes: Handle breakpoint and Singlestep

Quoted 7 lines · Peter Zijlstra
Quoted 3 lines · Srikar Dronamraju

Hence provide some extra
+ * time (by way of synchronize_sched() for breakpoint hit threads to acquire
+ * the uprobes_treelock before the uprobe is removed from the rbtree.

'Some extra time' doesn't make me all warm an fuzzy inside, but instead screams we fudge around a race condition.

The extra time provided is sufficient to avoid the race. So will modify it to mean "sufficient" instead of "some".

Would that suffice?

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

Re: [PATCH v5 3.1.0-rc4-tip 12/26] Uprobes: Handle breakpoint and Singlestep

Quoted 7 lines · Srikar Dronamraju
Quoted 5 lines · Peter Zijlstra
Quoted 3 lines · Srikar Dronamraju

Hence provide some extra
+ * time (by way of synchronize_sched() for breakpoint hit threads to acquire
+ * the uprobes_treelock before the uprobe is removed from the rbtree.

Quoted 7 lines
Quoted 2 lines

'Some extra time' doesn't make me all warm an fuzzy inside, but instead screams we fudge around a race condition.

The extra time provided is sufficient to avoid the race. So will modify it to mean "sufficient" instead of "some".

Would that suffice?

Much better, for extra point, explain why its sufficient as well ;-)

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#78 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 12/26] Uprobes: Handle breakpoint and Singlestep

Quoted 12 lines · Peter Zijlstra
Quoted 10 lines · Srikar Dronamraju
Quoted 7 lines · Peter Zijlstra
Quoted 3 lines · Srikar Dronamraju

Hence provide some extra
+ * time (by way of synchronize_sched() for breakpoint hit threads to acquire
+ * the uprobes_treelock before the uprobe is removed from the rbtree.

'Some extra time' doesn't make me all warm an fuzzy inside, but instead screams we fudge around a race condition.

Quoted 6 lines
Quoted 4 lines

The extra time provided is sufficient to avoid the race. So will modify it to mean "sufficient" instead of "some".

Would that suffice?

Much better, for extra point, explain why its sufficient as well ;-)

+1 ;)

I can't understand why synchronize_sched helps. In fact it is very possible I simply misunderstood the problem, I'll appreciate if you can explain.

Just for example. Suppose that uprobe_notify_resume() sleeps in down_read(mmap_sem). In this case synchronize_sched() can return even before it takes this sem, how this can help the subsequent find_uprobe() ? Or that task can be simply preempted before.

Or I missed the point completely?

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#79 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 12/26] Uprobes: Handle breakpoint and Singlestep

In reply to Srikar Dronamraju
Quoted 1 line · Srikar Dronamraju

+ * Guarded by uproc->mutex.

That seems to be the only reference to this thing here... what's a uproc and where's this mutex?

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#80 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 12/26] Uprobes: Handle breakpoint and Singlestep

In reply to Srikar Dronamraju

kernel/fork.c static struct task_struct *copy_process(unsigned long clone_flags, · line 1291

Quoted 8 lines · Srikar Dronamraju
kernel/fork.c +3 -0
@@ -1285,6 +1286,9 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 	INIT_LIST_HEAD(&p->pi_state_list);
 	p->pi_state_cache = NULL;
 #endif
+#ifdef CONFIG_UPROBES
+	p->utask = NULL;
+#endif

I am not sure I understand this all right, but I am not sure this is enough...

What if the forking task (current) is in UTASK_BP_HIT state?

IOW, uprobe replaces the original syscall insn with "int3", then we enter the kernel from the xol_vma. The new child has the same modified instruction pointer (pointing to nowhere without CLONE_VM) and in any case it doesn't have TIF_SINGLESTEP.

No?

Quoted 24 lines
kernel/uprobes.c +24 -0
+void uprobe_notify_resume(struct pt_regs *regs)
+{
+	struct vm_area_struct *vma;
+	struct uprobe_task *utask;
+	struct mm_struct *mm;
+	struct uprobe *u = NULL;
+	unsigned long probept;
+
+	utask = current->utask;
+	mm = current->mm;
+	if (!utask || utask->state == UTASK_BP_HIT) {
+		probept = get_uprobe_bkpt_addr(regs);
+		down_read(&mm->mmap_sem);
+		vma = find_vma(mm, probept);
+		if (vma && valid_vma(vma))
+			u = find_uprobe(vma->vm_file->f_mapping->host,
+					probept - vma->vm_start +
+					(vma->vm_pgoff << PAGE_SHIFT));
+		up_read(&mm->mmap_sem);
+		if (!u)
+			/* No matching uprobe; signal SIGTRAP. */
+			goto cleanup_ret;
+		if (!utask) {
+			utask = add_utask();

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1145

Quoted 10 lines
kernel/uprobes.c +10 -0
+			/* Cannot Allocate; re-execute the instruction. */
+			if (!utask)
+				goto cleanup_ret;
+		}
+		/* TODO Start queueing signals. */
+		utask->active_uprobe = u;
+		handler_chain(u, regs);
+		utask->state = UTASK_SSTEP;
+		if (!pre_ssout(u, regs, probept))
+			user_enable_single_step(current);

Oooh. Playing with user_*_single_step() is obviously not very nice... But I guess you have no choice. Although I _hope_ we can do something else later.

And what if we step into a syscall insn? I do not understand this low level code, but it seems that in this case we trap in kernel mode and do_debug() doesn't clear X86_EFLAGS_TF because uprobes hook DIE_DEBUG. IOW, the task will trap again and again inside this syscall, no?

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1151

Quoted 3 lines
kernel/uprobes.c +3 -0
+	} else if (utask->state == UTASK_SSTEP) {
+		u = utask->active_uprobe;
+		if (sstep_complete(u, regs)) {

It is not clear to me if it is correct to simply return if sstep_complete() returns false... What if X86_EFLAGS_TF was "lost" somehow?

Again, I am not saying I understand this magic. Not at all ;) Please simply ignore my email if you think everything is fine.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#81 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 12/26] Uprobes: Handle breakpoint and Singlestep

Quoted 6 lines · Oleg Nesterov

What if the forking task (current) is in UTASK_BP_HIT state?
...

And what if we step into a syscall insn?
...

And I guess there would be a lot more problems here. But, looking at is_prefix_bad() I see the nice comment:

* opcodes we'll probably never support:
* 0f - lar, lsl, syscall, clts, sysret, sysenter, sysexit, invd, wbinvd, ud2

This answers my questions.

Quoted 1 line

Please simply ignore my email if you think everything is fine.

Yep.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#82 lore raw

PATCH v5 13/26 x86: define a x86 specific exception notifier.

In reply to Srikar Dronamraju

Uprobes uses notifier mechanism to get in control when an application encounters a breakpoint or a singlestep exception.

---
 arch/x86/include/asm/uprobes.h |    4 ++++
 arch/x86/kernel/signal.c       |   14 ++++++++++++++
 arch/x86/kernel/uprobes.c      |   29 +++++++++++++++++++++++++++++
 3 files changed, 47 insertions(+), 0 deletions(-)
arch/x86/include/asm/uprobes.h +4 -0
index 35ac9d7..7d9d0a5 100644
@@ -23,6 +23,8 @@
  *	Jim Keniston
  */
 
+#include <linux/notifier.h>
+
 typedef u8 uprobe_opcode_t;
 #define MAX_UINSN_BYTES 16
 #define UPROBES_XOL_SLOT_BYTES	128	/* to keep it cache aligned */
@@ -40,4 +42,6 @@ struct uprobe_arch_info {};
 struct uprobe;
 extern int analyze_insn(struct task_struct *tsk, struct uprobe *uprobe);
 extern void set_instruction_pointer(struct pt_regs *regs, unsigned long vaddr);
+extern int uprobe_exception_notify(struct notifier_block *self,
+				       unsigned long val, void *data);
 #endif	/* _ASM_UPROBES_H */
arch/x86/kernel/signal.c +14 -0
index 54ddaeb..97fac33 100644
@@ -20,6 +20,7 @@
 #include <linux/personality.h>
 #include <linux/uaccess.h>
 #include <linux/user-return-notifier.h>
+#include <linux/uprobes.h>
 
 #include <asm/processor.h>
 #include <asm/ucontext.h>
@@ -820,6 +821,19 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
 		mce_notify_process();
 #endif /* CONFIG_X86_64 && CONFIG_X86_MCE */
 
+	if (thread_info_flags & _TIF_UPROBE) {
+		clear_thread_flag(TIF_UPROBE);
+#ifdef CONFIG_X86_32
+		/*
+		 * On x86_32, do_notify_resume() gets called with
+		 * interrupts disabled. Hence enable interrupts if they
+		 * are still disabled.
+		 */
+		local_irq_enable();
+#endif
+		uprobe_notify_resume(regs);
+	}
+
 	/* deal with pending signal delivery */
 	if (thread_info_flags & _TIF_SIGPENDING)
 		do_signal(regs);
arch/x86/kernel/uprobes.c +29 -0
index e1c1dfe..8ec759a 100644
@@ -393,3 +393,32 @@ void set_instruction_pointer(struct pt_regs *regs, unsigned long vaddr)
 {
 	regs->ip = vaddr;
 }
+
+/*
+ * Wrapper routine for handling exceptions.
+ */
+int uprobe_exception_notify(struct notifier_block *self,
+				       unsigned long val, void *data)
+{
+	struct die_args *args = data;
+	struct pt_regs *regs = args->regs;
+	int ret = NOTIFY_DONE;
+
+	/* We are only interested in userspace traps */
+	if (regs && !user_mode_vm(regs))
+		return NOTIFY_DONE;
+
+	switch (val) {
+	case DIE_INT3:
+		/* Run your handler here */
+		if (uprobe_bkpt_notifier(regs))
+			ret = NOTIFY_STOP;
+		break;
+	case DIE_DEBUG:
+		if (uprobe_post_notifier(regs))
+			ret = NOTIFY_STOP;
+	default:
+		break;
+	}
+	return ret;
+}

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#83 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 13/26] x86: define a x86 specific exception notifier.

Quoted 10 lines · Srikar Dronamraju
arch/x86/kernel/signal.c +6 -0
@@ -820,6 +821,19 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
                mce_notify_process();
 #endif /* CONFIG_X86_64 && CONFIG_X86_MCE */
 
+       if (thread_info_flags & _TIF_UPROBE) {
+               clear_thread_flag(TIF_UPROBE);
+#ifdef CONFIG_X86_32
+               /*
+                * On x86_32, do_notify_resume() gets called with
+                * interrupts disabled. Hence enable interrupts if they

arch/x86/kernel/signal.c do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) · line 839

Quoted 10 lines
arch/x86/kernel/signal.c +7 -0
+                * are still disabled.
+                */
+               local_irq_enable();
+#endif
+               uprobe_notify_resume(regs);
+       }
+
        /* deal with pending signal delivery */
        if (thread_info_flags & _TIF_SIGPENDING)
                do_signal(regs); 

It would be good to remove this difference between i386 and x86_64.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#84 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 13/26] x86: define a x86 specific exception notifier.

Quoted 15 lines · Peter Zijlstra
Quoted 14 lines · Srikar Dronamraju
arch/x86/kernel/signal.c +10 -0
@@ -820,6 +821,19 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
                mce_notify_process();
 #endif /* CONFIG_X86_64 && CONFIG_X86_MCE */
 
+       if (thread_info_flags & _TIF_UPROBE) {
+               clear_thread_flag(TIF_UPROBE);
+#ifdef CONFIG_X86_32
+               /*
+                * On x86_32, do_notify_resume() gets called with
+                * interrupts disabled. Hence enable interrupts if they
+                * are still disabled.
+                */
+               local_irq_enable();
+#endif
Quoted 8 lines

arch/x86/kernel/signal.c do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) · line 839

Quoted 6 lines
arch/x86/kernel/signal.c +3 -0
+               uprobe_notify_resume(regs);
+       }
+
        /* deal with pending signal delivery */
        if (thread_info_flags & _TIF_SIGPENDING)
                do_signal(regs); 

It would be good to remove this difference between i386 and x86_64.

I think, we have already discussed this. I tried getting to know why we have this difference in behaviour. However I havent been able to find the answer.

If you can get somebody to answer this, I would be happy to modify as required.

Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#85 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 13/26] x86: define a x86 specific exception notifier.

Quoted 27 lines · Srikar Dronamraju
Quoted 15 lines · Peter Zijlstra
Quoted 14 lines · Srikar Dronamraju
arch/x86/kernel/signal.c +10 -0
@@ -820,6 +821,19 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
                mce_notify_process();
 #endif /* CONFIG_X86_64 && CONFIG_X86_MCE */
 
+       if (thread_info_flags & _TIF_UPROBE) {
+               clear_thread_flag(TIF_UPROBE);
+#ifdef CONFIG_X86_32
+               /*
+                * On x86_32, do_notify_resume() gets called with
+                * interrupts disabled. Hence enable interrupts if they
+                * are still disabled.
+                */
+               local_irq_enable();
+#endif
Quoted 8 lines

arch/x86/kernel/signal.c do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) · line 839

Quoted 6 lines
arch/x86/kernel/signal.c +3 -0
+               uprobe_notify_resume(regs);
+       }
+
        /* deal with pending signal delivery */
        if (thread_info_flags & _TIF_SIGPENDING)
                do_signal(regs); 

It would be good to remove this difference between i386 and x86_64.

Quoted 6 lines

I think, we have already discussed this. I tried getting to know why we have this difference in behaviour. However I havent been able to find the answer.

If you can get somebody to answer this, I would be happy to modify as required.

The Changelog failed to mention this. Afaict there really is no reason other than that touching entry_32.S is a pain.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#86 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 13/26] x86: define a x86 specific exception notifier.

In reply to Srikar Dronamraju
Quoted 8 lines · Srikar Dronamraju
arch/x86/kernel/uprobes.c +7 -0
+int uprobe_exception_notify(struct notifier_block *self,
+				       unsigned long val, void *data)
+{
+	struct die_args *args = data;
+	struct pt_regs *regs = args->regs;
+	int ret = NOTIFY_DONE;
+

arch/x86/kernel/uprobes.c void set_instruction_pointer(struct pt_regs *regs, unsigned long vaddr) · line 416

Quoted 10 lines
arch/x86/kernel/uprobes.c +10 -0
+	/* We are only interested in userspace traps */
+	if (regs && !user_mode_vm(regs))
+		return NOTIFY_DONE;
+
+	switch (val) {
+	case DIE_INT3:
+		/* Run your handler here */
+		if (uprobe_bkpt_notifier(regs))
+			ret = NOTIFY_STOP;
+		break;

OK, but I simply can't understand do_int3(). It uses DIE_INT3 or DIE_TRAP depending on CONFIG_KPROBES.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#87 lore raw

PATCH v5 14/26 uprobe: register exception notifier

In reply to Srikar Dronamraju

Use the notifier mechanism to register uprobes exception notifier.

---
 kernel/uprobes.c |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)
kernel/uprobes.c +18 -0
index 8b6654e..083c577 100644
@@ -30,6 +30,7 @@
 #include <linux/mmu_notifier.h>	/* set_pte_at_notify */
 #include <linux/swap.h>		/* try_to_free_swap */
 #include <linux/ptrace.h>	/* user_enable_single_step */
+#include <linux/kdebug.h>	/* notifier mechanism */
 #include <linux/uprobes.h>
 
 static struct rb_root uprobes_tree = RB_ROOT;
@@ -1209,3 +1210,20 @@ int uprobe_post_notifier(struct pt_regs *regs)
 	set_thread_flag(TIF_UPROBE);
 	return 1;
 }
+
+struct notifier_block uprobe_exception_nb = {
+	.notifier_call = uprobe_exception_notify,
+	.priority = INT_MAX - 1,	/* notified after kprobes, kgdb */
+};
+
+static int __init init_uprobes(void)
+{
+	return register_die_notifier(&uprobe_exception_nb);
+}
+
+static void __exit exit_uprobes(void)
+{
+}
+
+module_init(init_uprobes);
+module_exit(exit_uprobes);

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#88 lore raw

PATCH v5 15/26 x86: Define x86_64 specific uprobe_task_arch_info structure

In reply to Srikar Dronamraju

On x86_64, need to handle RIP relative instructions, which requires us to save and restore a register.

---
 arch/x86/include/asm/uprobes.h |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)
arch/x86/include/asm/uprobes.h +5 -0
index 7d9d0a5..2ad2c71 100644
@@ -36,8 +36,13 @@ typedef u8 uprobe_opcode_t;
 struct uprobe_arch_info {
 	unsigned long rip_rela_target_address;
 };
+
+struct uprobe_task_arch_info {
+	unsigned long saved_scratch_register;
+};
 #else
 struct uprobe_arch_info {};
+struct uprobe_task_arch_info {};
 #endif
 struct uprobe;
 extern int analyze_insn(struct task_struct *tsk, struct uprobe *uprobe);

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#89 lore raw

PATCH v5 16/26 uprobes: Introduce uprobe_task_arch_info structure.

In reply to Srikar Dronamraju

uprobe_task_arch_info structure helps save and restore architecture specific artifacts at the probehit/singlestep/original instruction restore time.

---
 include/linux/uprobes.h |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)
include/linux/uprobes.h +2 -0
index fa7eaba..30576fa 100644
@@ -31,6 +31,7 @@ struct vm_area_struct;
 #else
 typedef u8 uprobe_opcode_t;
 struct uprobe_arch_info {};
+struct uprobe_task_arch_info {};	/* arch specific task info */
 #define MAX_UINSN_BYTES 4
 #endif
 
@@ -86,6 +87,7 @@ struct uprobe_task {
 	unsigned long vaddr;
 
 	enum uprobe_task_state state;
+	struct uprobe_task_arch_info tskinfo;
 
 	struct uprobe *active_uprobe;
 };
#90 lore raw

PATCH v5 17/26 x86: arch specific hooks for pre/post singlestep handling.

In reply to Srikar Dronamraju

Hooks for handling pre singlestepping and post singlestepping.

---
 arch/x86/include/asm/uprobes.h |    2 +
 arch/x86/kernel/uprobes.c      |  138 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 140 insertions(+), 0 deletions(-)
arch/x86/include/asm/uprobes.h +2 -0
index 2ad2c71..1c30cfd 100644
@@ -47,6 +47,8 @@ struct uprobe_task_arch_info {};
 struct uprobe;
 extern int analyze_insn(struct task_struct *tsk, struct uprobe *uprobe);
 extern void set_instruction_pointer(struct pt_regs *regs, unsigned long vaddr);
+extern int pre_xol(struct uprobe *uprobe, struct pt_regs *regs);
+extern int post_xol(struct uprobe *uprobe, struct pt_regs *regs);
 extern int uprobe_exception_notify(struct notifier_block *self,
 				       unsigned long val, void *data);
 #endif	/* _ASM_UPROBES_H */
arch/x86/kernel/uprobes.c +138 -0

150 lines not on this page · raw message

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#91 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 17/26] x86: arch specific hooks for pre/post singlestep handling.

arch/x86/kernel/uprobes.c void set_instruction_pointer(struct pt_regs *regs, unsigned long vaddr) · line 459

Quoted 5 lines · Srikar Dronamraju
arch/x86/kernel/uprobes.c +5 -0
+fail:
+       pr_warn_once("uprobes: Failed to adjust return address after"
+               " single-stepping call instruction;"
+               " pid=%d, sp=%#lx\n", current->pid, sp);
+       return -EFAULT; 

So how can that happen? Single-Step while someone unmapped the stack?

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#92 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 17/26] x86: arch specific hooks for pre/post singlestep handling.

Quoted 8 lines · Peter Zijlstra

arch/x86/kernel/uprobes.c void set_instruction_pointer(struct pt_regs *regs, unsigned long vaddr) · line 459

Quoted 5 lines · Srikar Dronamraju
arch/x86/kernel/uprobes.c +5 -0
+fail:
+       pr_warn_once("uprobes: Failed to adjust return address after"
+               " single-stepping call instruction;"
+               " pid=%d, sp=%#lx\n", current->pid, sp);
+       return -EFAULT; 

So how can that happen? Single-Step while someone unmapped the stack?

We do a copy_to_user, copy_from_user just above this, Now if either of them fail, we have no choice but to Bail out. What caused this EFault may not be under Uprobes's Control.

Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#93 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 17/26] x86: arch specific hooks for pre/post singlestep handling.

Quoted 9 lines · Srikar Dronamraju
Quoted 7 lines · Peter Zijlstra
Quoted 5 lines · Srikar Dronamraju
arch/x86/kernel/uprobes.c +5 -0
+fail:
+       pr_warn_once("uprobes: Failed to adjust return address after"
+               " single-stepping call instruction;"
+               " pid=%d, sp=%#lx\n", current->pid, sp);
+       return -EFAULT; 
Quoted 3 lines
Quoted 1 line

So how can that happen? Single-Step while someone unmapped the stack?

We do a copy_to_user, copy_from_user just above this,

I saw that,

Quoted 2 lines

Now if either of
them fail, we have no choice but to Bail out.

Agreed,

Quoted 1 line

What caused this EFault may not be under Uprobes's Control.

I never said it was.. All I asked is what (outside of uprobe) was done to cause this, and why is this particular error important enough to warrant a warn.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#94 lore raw

PATCH v5 18/26 uprobes: slot allocation.

In reply to Srikar Dronamraju

One page of slots are allocated per mm.
On a probehit one free slot is acquired and released after singlestep operation completes.

---
 include/linux/mm_types.h |    2 
 include/linux/uprobes.h  |   22 ++++
 kernel/fork.c            |    2 
 kernel/uprobes.c         |  246 +++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 267 insertions(+), 5 deletions(-)
include/linux/mm_types.h +2 -0
index 9aeb64f..aa2e427 100644
@@ -12,6 +12,7 @@
 #include <linux/completion.h>
 #include <linux/cpumask.h>
 #include <linux/page-debug-flags.h>
+#include <linux/uprobes.h>
 #include <asm/page.h>
 #include <asm/mmu.h>
 
@@ -351,6 +352,7 @@ struct mm_struct {
 #endif
 #ifdef CONFIG_UPROBES
 	atomic_t mm_uprobes_count;
+	struct uprobes_xol_area *uprobes_xol_area;
 #endif
 };
 
include/linux/uprobes.h +22 -0
index 30576fa..a407d17 100644
@@ -92,6 +92,27 @@ struct uprobe_task {
 	struct uprobe *active_uprobe;
 };
 
+/*
+ * On a breakpoint hit, thread contests for a slot.  It free the
+ * slot after singlestep.  Only definite number of slots are
+ * allocated.
+ */
+
+struct uprobes_xol_area {
+	spinlock_t slot_lock;	/* protects bitmap and slot (de)allocation*/
+	wait_queue_head_t wq;	/* if all slots are busy */
+	atomic_t slot_count;	/* currently in use slots */
+	unsigned long *bitmap;	/* 0 = free slot */
+	struct page *page;
+
+	/*
+	 * We keep the vma's vm_start rather than a pointer to the vma
+	 * itself.  The probed process or a naughty kernel module could make
+	 * the vma go away, and we must handle that reasonably gracefully.
+	 */
+	unsigned long vaddr;		/* Page(s) of instruction slots */
+};
+
 #ifdef CONFIG_UPROBES
 extern int __weak set_bkpt(struct task_struct *tsk, struct uprobe *uprobe,
 							unsigned long vaddr);
@@ -105,6 +126,7 @@ extern int register_uprobe(struct inode *inode, loff_t offset,
 extern void unregister_uprobe(struct inode *inode, loff_t offset,
 				struct uprobe_consumer *consumer);
 extern void free_uprobe_utask(struct task_struct *tsk);
+extern void free_uprobes_xol_area(struct mm_struct *mm);
 extern int mmap_uprobe(struct vm_area_struct *vma);
 extern void munmap_uprobe(struct vm_area_struct *vma);
 extern unsigned long __weak get_uprobe_bkpt_addr(struct pt_regs *regs);
kernel/fork.c +2 -0
index 5914bc1..088a27c 100644
@@ -557,6 +557,7 @@ void mmput(struct mm_struct *mm)
 	might_sleep();
 
 	if (atomic_dec_and_test(&mm->mm_users)) {
+		free_uprobes_xol_area(mm);
 		exit_aio(mm);
 		ksm_exit(mm);
 		khugepaged_exit(mm); /* must run before exit_mmap */
@@ -744,6 +745,7 @@ struct mm_struct *dup_mm(struct task_struct *tsk)
 #ifdef CONFIG_UPROBES
 	atomic_set(&mm->mm_uprobes_count,
 			atomic_read(&oldmm->mm_uprobes_count));
+	mm->uprobes_xol_area = NULL;
 #endif
 
 	if (!mm_init(mm, tsk))
kernel/uprobes.c +241 -5

297 lines not on this page · raw message

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#95 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 18/26] uprobes: slot allocation.

Quoted 39 lines · Srikar Dronamraju
kernel/uprobes.c +39 -0
+static int xol_add_vma(struct uprobes_xol_area *area)
+{
+       const struct cred *curr_cred;
+       struct vm_area_struct *vma;
+       struct mm_struct *mm;
+       unsigned long addr;
+       int ret = -ENOMEM;
+
+       mm = get_task_mm(current);
+       if (!mm)
+               return -ESRCH;
+
+       down_write(&mm->mmap_sem);
+       if (mm->uprobes_xol_area) {
+               ret = -EALREADY;
+               goto fail;
+       }
+
+       /*
+        * Find the end of the top mapping and skip a page.
+        * If there is no space for PAGE_SIZE above
+        * that, mmap will ignore our address hint.
+        *
+        * override credentials otherwise anonymous memory might
+        * not be granted execute permission when the selinux
+        * security hooks have their way.
+        */
+       vma = rb_entry(rb_last(&mm->mm_rb), struct vm_area_struct, vm_rb);
+       addr = vma->vm_end + PAGE_SIZE;
+       curr_cred = override_creds(&init_cred);
+       addr = do_mmap_pgoff(NULL, addr, PAGE_SIZE, PROT_EXEC, MAP_PRIVATE, 0);
+       revert_creds(curr_cred);
+
+       if (addr & ~PAGE_MASK)
+               goto fail;
+       vma = find_vma(mm, addr);
+
+       /* Don't expand vma on mremap(). */
+       vma->vm_flags |= VM_DONTEXPAND | VM_DONTCOPY;

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1097

Quoted 10 lines
kernel/uprobes.c +10 -0
+       area->vaddr = vma->vm_start;
+       if (get_user_pages(current, mm, area->vaddr, 1, 1, 1, &area->page,
+                               &vma) > 0)
+               ret = 0;
+
+fail:
+       up_write(&mm->mmap_sem);
+       mmput(mm);
+       return ret;
+} 

So is that the right way? I looked back to the previous discussion with Eric and couldn't really make up my mind either way. The changelog is entirely without detail and Eric isn't CC'ed.

What's the point of having these discussions if all traces of them disappear on the next posting?

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#96 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 18/26] uprobes: slot allocation.

Quoted 46 lines · Peter Zijlstra
Quoted 45 lines · Srikar Dronamraju
kernel/uprobes.c +45 -0
+static int xol_add_vma(struct uprobes_xol_area *area)
+{
+       const struct cred *curr_cred;
+       struct vm_area_struct *vma;
+       struct mm_struct *mm;
+       unsigned long addr;
+       int ret = -ENOMEM;
+
+       mm = get_task_mm(current);
+       if (!mm)
+               return -ESRCH;
+
+       down_write(&mm->mmap_sem);
+       if (mm->uprobes_xol_area) {
+               ret = -EALREADY;
+               goto fail;
+       }
+
+       /*
+        * Find the end of the top mapping and skip a page.
+        * If there is no space for PAGE_SIZE above
+        * that, mmap will ignore our address hint.
+        *
+        * override credentials otherwise anonymous memory might
+        * not be granted execute permission when the selinux
+        * security hooks have their way.
+        */
+       vma = rb_entry(rb_last(&mm->mm_rb), struct vm_area_struct, vm_rb);
+       addr = vma->vm_end + PAGE_SIZE;
+       curr_cred = override_creds(&init_cred);
+       addr = do_mmap_pgoff(NULL, addr, PAGE_SIZE, PROT_EXEC, MAP_PRIVATE, 0);
+       revert_creds(curr_cred);
+
+       if (addr & ~PAGE_MASK)
+               goto fail;
+       vma = find_vma(mm, addr);
+
+       /* Don't expand vma on mremap(). */
+       vma->vm_flags |= VM_DONTEXPAND | VM_DONTCOPY;
+       area->vaddr = vma->vm_start;
+       if (get_user_pages(current, mm, area->vaddr, 1, 1, 1, &area->page,
+                               &vma) > 0)
+               ret = 0;
+
+fail:
Quoted 8 lines

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1097

Quoted 4 lines
kernel/uprobes.c +4 -0
+       up_write(&mm->mmap_sem);
+       mmput(mm);
+       return ret;
+} 

So is that the right way? I looked back to the previous discussion with Eric and couldn't really make up my mind either way. The changelog is entirely without detail and Eric isn't CC'ed.

This is based on what Stephen Smalley suggested on the same thread
https://lkml.org/lkml/2011/4/20/224

I used to keep the changelog after the marker after Christoph Hellwig had suggested that https://lkml.org/lkml/2010/7/20/5
However "stg export" removes lines after the --- marker.

I agree that I should have copied Eric and Stephen atleast on this patch. However if the number of to/cc are greater than 20, the LKML archive cool ignore the mail.

I know that these arent problems faced by others and open to suggestions on how they have overcome the same.

Quoted 3 lines

What's the point of having these discussions if all traces of them disappear on the next posting?

Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#97 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 18/26] uprobes: slot allocation.

Quoted 3 lines · Srikar Dronamraju

I used to keep the changelog after the marker after Christoph Hellwig had suggested that https://lkml.org/lkml/2010/7/20/5
However "stg export" removes lines after the --- marker.

That's no excuse for writing shitty changelogs. Version logs contain the incremental changes in each version, but the changelog should be a full and proper description of the patch, irrespective of how many iterations and changes it has undergone.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#98 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 18/26] uprobes: slot allocation.

In reply to Srikar Dronamraju
Quoted 7 lines · Srikar Dronamraju
kernel/uprobes.c +7 -0
+static struct uprobes_xol_area *xol_alloc_area(void)
+{
+       struct uprobes_xol_area *area = NULL;
+
+       area = kzalloc(sizeof(*area), GFP_KERNEL);
+       if (unlikely(!area))
+               return NULL;

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1122

Quoted 10 lines
kernel/uprobes.c +10 -0
+
+       area->bitmap = kzalloc(BITS_TO_LONGS(UINSNS_PER_PAGE) * sizeof(long),
+                                                               GFP_KERNEL);
+
+       if (!area->bitmap)
+               goto fail;
+
+       init_waitqueue_head(&area->wq);
+       spin_lock_init(&area->slot_lock);
+       if (!xol_add_vma(area) && !current->mm->uprobes_xol_area) {

So what happens if xol_add_vma() succeeds, but we find ->uprobes_xol_area set?

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1124

Quoted 2 lines
kernel/uprobes.c +2 -0
+               task_lock(current);
+               if (!current->mm->uprobes_xol_area) {

Having to re-test it under this lock seems to suggest it could.

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1127

Quoted 3 lines
kernel/uprobes.c +3 -0
+                       current->mm->uprobes_xol_area = area;
+                       task_unlock(current);
+                       return area;

This function would be so much easier to read if the success case (this here I presume) would not be nested 2 deep.

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1128

Quoted 3 lines
kernel/uprobes.c +3 -0
+               }
+               task_unlock(current);
+       }

at which point you could end up with two extra vmas? Because there's no freeing of the result of xol_add_vma().

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1136

Quoted 5 lines
kernel/uprobes.c +5 -0
+fail:
+       kfree(area->bitmap);
+       kfree(area);
+       return current->mm->uprobes_xol_area;
+} 

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#99 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 18/26] uprobes: slot allocation.

Quoted 34 lines · Peter Zijlstra
Quoted 7 lines · Srikar Dronamraju
kernel/uprobes.c +7 -0
+static struct uprobes_xol_area *xol_alloc_area(void)
+{
+       struct uprobes_xol_area *area = NULL;
+
+       area = kzalloc(sizeof(*area), GFP_KERNEL);
+       if (unlikely(!area))
+               return NULL;

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1122

Quoted 10 lines
kernel/uprobes.c +10 -0
+
+       area->bitmap = kzalloc(BITS_TO_LONGS(UINSNS_PER_PAGE) * sizeof(long),
+                                                               GFP_KERNEL);
+
+       if (!area->bitmap)
+               goto fail;
+
+       init_waitqueue_head(&area->wq);
+       spin_lock_init(&area->slot_lock);
+       if (!xol_add_vma(area) && !current->mm->uprobes_xol_area) {

So what happens if xol_add_vma() succeeds, but we find ->uprobes_xol_area set?

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1124

Quoted 2 lines
kernel/uprobes.c +2 -0
+               task_lock(current);
+               if (!current->mm->uprobes_xol_area) {

Having to re-test it under this lock seems to suggest it could.

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1127

Quoted 3 lines
kernel/uprobes.c +3 -0
+                       current->mm->uprobes_xol_area = area;
+                       task_unlock(current);
+                       return area;

This function would be so much easier to read if the success case (this here I presume) would not be nested 2 deep.

Quoted 7 lines

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1128

Quoted 3 lines
kernel/uprobes.c +3 -0
+               }
+               task_unlock(current);
+       }

at which point you could end up with two extra vmas? Because there's no freeing of the result of xol_add_vma().

Agree, we need to unmap the vma in that case.

Quoted 5 lines
Quoted 5 lines
kernel/uprobes.c +5 -0
+fail:
+       kfree(area->bitmap);
+       kfree(area);
+       return current->mm->uprobes_xol_area;
+} 
Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#100 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 18/26] uprobes: slot allocation.

In reply to Srikar Dronamraju

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1158

Quoted 6 lines · Srikar Dronamraju
kernel/uprobes.c +6 -0
+static void xol_wait_event(struct uprobes_xol_area *area)
+{
+       if (atomic_read(&area->slot_count) >= UINSNS_PER_PAGE)
+               wait_event(area->wq,
+                       (atomic_read(&area->slot_count) < UINSNS_PER_PAGE));
+} 

That's mighty redundant, look up wait_event() and try again.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#101 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 18/26] uprobes: slot allocation.

In reply to Srikar Dronamraju
Quoted 12 lines · Srikar Dronamraju
kernel/uprobes.c +12 -0
+static unsigned long xol_take_insn_slot(struct uprobes_xol_area *area)
+{
+       unsigned long slot_addr, flags;
+       int slot_nr;
+
+       do {
+               spin_lock_irqsave(&area->slot_lock, flags);
+               slot_nr = find_first_zero_bit(area->bitmap, UINSNS_PER_PAGE);
+               if (slot_nr < UINSNS_PER_PAGE) {
+                       __set_bit(slot_nr, area->bitmap);
+                       slot_addr = area->vaddr +
+                                       (slot_nr * UPROBES_XOL_SLOT_BYTES);

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1184

Quoted 10 lines
kernel/uprobes.c +10 -0
+                       atomic_inc(&area->slot_count);
+               }
+               spin_unlock_irqrestore(&area->slot_lock, flags);
+               if (slot_nr >= UINSNS_PER_PAGE)
+                       xol_wait_event(area);
+
+       } while (slot_nr >= UINSNS_PER_PAGE);
+
+       return slot_addr;
+} 

Why isn't a find_first_bit() + set_and_test_bit() not sufficient? That is, what do you need that lock for?

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#102 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 18/26] uprobes: slot allocation.

Quoted 18 lines · Peter Zijlstra
Quoted 17 lines · Srikar Dronamraju
kernel/uprobes.c +17 -0
+static unsigned long xol_take_insn_slot(struct uprobes_xol_area *area)
+{
+       unsigned long slot_addr, flags;
+       int slot_nr;
+
+       do {
+               spin_lock_irqsave(&area->slot_lock, flags);
+               slot_nr = find_first_zero_bit(area->bitmap, UINSNS_PER_PAGE);
+               if (slot_nr < UINSNS_PER_PAGE) {
+                       __set_bit(slot_nr, area->bitmap);
+                       slot_addr = area->vaddr +
+                                       (slot_nr * UPROBES_XOL_SLOT_BYTES);
+                       atomic_inc(&area->slot_count);
+               }
+               spin_unlock_irqrestore(&area->slot_lock, flags);
+               if (slot_nr >= UINSNS_PER_PAGE)
+                       xol_wait_event(area);
Quoted 8 lines

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1184

Quoted 5 lines
kernel/uprobes.c +5 -0
+
+       } while (slot_nr >= UINSNS_PER_PAGE);
+
+       return slot_addr;
+} 

Why isn't a find_first_bit() + set_and_test_bit() not sufficient? That is, what do you need that lock for?

yes, we could do without the lock to.
Will do this in the next patchset.

Signature · 9 lines
Thanks and Regards 
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#103 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 18/26] uprobes: slot allocation.

In reply to Srikar Dronamraju

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1252

Quoted 3 lines · Srikar Dronamraju
kernel/uprobes.c +3 -0
+               spin_lock_irqsave(&area->slot_lock, flags);
+               __clear_bit(slot_nr, area->bitmap);
+               spin_unlock_irqrestore(&area->slot_lock, flags); 

that so wants to be clear_bit()..

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#104 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 18/26] uprobes: slot allocation.

In reply to Srikar Dronamraju
Quoted 47 lines · Srikar Dronamraju
kernel/uprobes.c +46 -0
+static unsigned long xol_take_insn_slot(struct uprobes_xol_area *area)
+{
+       unsigned long slot_addr, flags;
+       int slot_nr;
+
+       do {
+               spin_lock_irqsave(&area->slot_lock, flags);
+               slot_nr = find_first_zero_bit(area->bitmap, UINSNS_PER_PAGE);
+               if (slot_nr < UINSNS_PER_PAGE) {
+                       __set_bit(slot_nr, area->bitmap);
+                       slot_addr = area->vaddr +
+                                       (slot_nr * UPROBES_XOL_SLOT_BYTES);
+                       atomic_inc(&area->slot_count);
+               }
+               spin_unlock_irqrestore(&area->slot_lock, flags);
+               if (slot_nr >= UINSNS_PER_PAGE)
+                       xol_wait_event(area);
+
+       } while (slot_nr >= UINSNS_PER_PAGE);
+
+       return slot_addr;
+}
 
+static void xol_free_insn_slot(struct task_struct *tsk)
+{
+       struct uprobes_xol_area *area;
+       unsigned long vma_end;
+       unsigned long slot_addr;
+
+       if (!tsk->mm || !tsk->mm->uprobes_xol_area || !tsk->utask)
+               return;
+
+       slot_addr = tsk->utask->xol_vaddr;
+
+       if (unlikely(!slot_addr || IS_ERR_VALUE(slot_addr)))
+               return;
+
+       area = tsk->mm->uprobes_xol_area;
+       vma_end = area->vaddr + PAGE_SIZE;
+       if (area->vaddr <= slot_addr && slot_addr < vma_end) {
+               int slot_nr;
+               unsigned long offset = slot_addr - area->vaddr;
+               unsigned long flags;
+
+               slot_nr = offset / UPROBES_XOL_SLOT_BYTES;
+               if (slot_nr >= UINSNS_PER_PAGE)
+                       return;

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1258

Quoted 10 lines
kernel/uprobes.c +10 -0
+
+               spin_lock_irqsave(&area->slot_lock, flags);
+               __clear_bit(slot_nr, area->bitmap);
+               spin_unlock_irqrestore(&area->slot_lock, flags);
+               atomic_dec(&area->slot_count);
+               if (waitqueue_active(&area->wq))
+                       wake_up(&area->wq);
+               tsk->utask->xol_vaddr = 0;
+       }
+} 

So if you want to keep that slot_lock, you might as well make ->slot_count a normal integer.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#105 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 18/26] uprobes: slot allocation.

In reply to Srikar Dronamraju
Quoted 10 lines · Srikar Dronamraju

- * valid_vma: Verify if the specified vma is an executable vma
+ * valid_vma: Verify if the specified vma is an executable vma,
+ * but not an XOL vma.
* - Return 1 if the specified virtual address is in an
- * executable vma.
+ * executable vma, but not in an XOL vma.
*/
static bool valid_vma(struct vm_area_struct *vma)
{

kernel/uprobes.c struct vma_info { · line 70

Quoted 7 lines
kernel/uprobes.c +4 -0
+	struct uprobes_xol_area *area = vma->vm_mm->uprobes_xol_area;
+
 	if (!vma->vm_file)
 		return false;
 
+	if (area && (area->vaddr == vma->vm_start))
+			return false;

Could you explain why do we need this "but not an XOL vma" check? xol_vma->vm_file is always NULL, no?

Quoted 14 lines
kernel/uprobes.c +14 -0
+static struct uprobes_xol_area *xol_alloc_area(void)
+{
+	struct uprobes_xol_area *area = NULL;
+
+	area = kzalloc(sizeof(*area), GFP_KERNEL);
+	if (unlikely(!area))
+		return NULL;
+
+	area->bitmap = kzalloc(BITS_TO_LONGS(UINSNS_PER_PAGE) * sizeof(long),
+								GFP_KERNEL);
+
+	if (!area->bitmap)
+		goto fail;
+

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1129

Quoted 10 lines
kernel/uprobes.c +10 -0
+	init_waitqueue_head(&area->wq);
+	spin_lock_init(&area->slot_lock);
+	if (!xol_add_vma(area) && !current->mm->uprobes_xol_area) {
+		task_lock(current);
+		if (!current->mm->uprobes_xol_area) {
+			current->mm->uprobes_xol_area = area;
+			task_unlock(current);
+			return area;
+		}
+		task_unlock(current);

But you can't rely on task_lock(), you can race with another thread with the same ->mm. I guess you need mmap_sem or xchg().

kernel/uprobes.c static struct uprobe_task *add_utask(void) · line 1316

Quoted 6 lines

static int pre_ssout(struct uprobe *uprobe, struct pt_regs *regs,

kernel/uprobes.c +2 -1
 				unsigned long vaddr)
 {
-	/* TODO: Yet to be implemented */
+	if (xol_get_insn_slot(uprobe, vaddr) && !pre_xol(uprobe, regs)) {
+		set_instruction_pointer(regs, current->utask->xol_vaddr);

set_instruction_pointer() looks unneded, pre_xol() has already changed regs->ip.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#106 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 18/26] uprobes: slot allocation.

Quoted 16 lines · Oleg Nesterov
Quoted 15 lines · Srikar Dronamraju

- * valid_vma: Verify if the specified vma is an executable vma
+ * valid_vma: Verify if the specified vma is an executable vma,
+ * but not an XOL vma.
* - Return 1 if the specified virtual address is in an
- * executable vma.
+ * executable vma, but not in an XOL vma.
*/
static bool valid_vma(struct vm_area_struct *vma)
{

kernel/uprobes.c +2 -0
+	struct uprobes_xol_area *area = vma->vm_mm->uprobes_xol_area;
+
 	if (!vma->vm_file)
 		return false;
Quoted 6 lines

kernel/uprobes.c struct vma_info { · line 70

Quoted 2 lines
kernel/uprobes.c +2 -0
+	if (area && (area->vaddr == vma->vm_start))
+			return false;

Could you explain why do we need this "but not an XOL vma" check? xol_vma->vm_file is always NULL, no?

Yes, xol_vma->vm_file is always NULL.
previously we used shmem_file_setup before we map the XOL area. However we now use init_creds instead, so this should also change accordingly. Will correct this.

Quoted 19 lines
Quoted 19 lines
kernel/uprobes.c +19 -0
+static struct uprobes_xol_area *xol_alloc_area(void)
+{
+	struct uprobes_xol_area *area = NULL;
+
+	area = kzalloc(sizeof(*area), GFP_KERNEL);
+	if (unlikely(!area))
+		return NULL;
+
+	area->bitmap = kzalloc(BITS_TO_LONGS(UINSNS_PER_PAGE) * sizeof(long),
+								GFP_KERNEL);
+
+	if (!area->bitmap)
+		goto fail;
+
+	init_waitqueue_head(&area->wq);
+	spin_lock_init(&area->slot_lock);
+	if (!xol_add_vma(area) && !current->mm->uprobes_xol_area) {
+		task_lock(current);
+		if (!current->mm->uprobes_xol_area) {
Quoted 8 lines

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1129

Quoted 5 lines
kernel/uprobes.c +5 -0
+			current->mm->uprobes_xol_area = area;
+			task_unlock(current);
+			return area;
+		}
+		task_unlock(current);

But you can't rely on task_lock(), you can race with another thread with the same ->mm. I guess you need mmap_sem or xchg().

Agree,
I think its better to use cmpxchg instead of xchg(). Otherwise, (using xchg), I would set area to new value, but the old area might be in use already. So I cant unmap the old area.

If I use cmpxchg, I can free up the new area if previous area is non NULL.

However setting uprobes_xol_area in xol_add_vma() where we already take mmap_sem for write while maping the xol_area is the best option.

Quoted 11 lines

kernel/uprobes.c static struct uprobe_task *add_utask(void) · line 1316

Quoted 7 lines

static int pre_ssout(struct uprobe *uprobe, struct pt_regs *regs,

kernel/uprobes.c +2 -1
 				unsigned long vaddr)
 {
-	/* TODO: Yet to be implemented */
+	if (xol_get_insn_slot(uprobe, vaddr) && !pre_xol(uprobe, regs)) {
+		set_instruction_pointer(regs, current->utask->xol_vaddr);

set_instruction_pointer() looks unneded, pre_xol() has already changed regs->ip.

Agree.

Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#107 lore raw

PATCH v5 19/26 tracing: Extract out common code for kprobes/uprobes traceevents.

In reply to Srikar Dronamraju

Move parts of trace_kprobe.c that can be shared with upcoming trace_uprobe.c. Common code to kernel/trace/trace_probe.h and kernel/trace/trace_probe.c.

---
 kernel/trace/Kconfig        |    4 
 kernel/trace/Makefile       |    1 
 kernel/trace/trace_kprobe.c |  894 +------------------------------------------
 kernel/trace/trace_probe.c  |  778 +++++++++++++++++++++++++++++++++++++
 kernel/trace/trace_probe.h  |  160 ++++++++
 5 files changed, 963 insertions(+), 874 deletions(-)
 create mode 100644 kernel/trace/trace_probe.c
 create mode 100644 kernel/trace/trace_probe.h
kernel/trace/Kconfig +4 -0
index cd31345..520106a 100644
@@ -373,6 +373,7 @@ config KPROBE_EVENT
 	depends on HAVE_REGS_AND_STACK_ACCESS_API
 	bool "Enable kprobes-based dynamic events"
 	select TRACING
+	select PROBE_EVENTS
 	default y
 	help
 	  This allows the user to add tracing events (similar to tracepoints)
@@ -385,6 +386,9 @@ config KPROBE_EVENT
 	  This option is also required by perf-probe subcommand of perf tools.
 	  If you want to use perf tools, this option is strongly recommended.
 
+config PROBE_EVENTS
+	def_bool n
+
 config DYNAMIC_FTRACE
 	bool "enable/disable ftrace tracepoints dynamically"
 	depends on FUNCTION_TRACER
kernel/trace/Makefile +1 -0
index 761c510..692223a 100644
@@ -56,5 +56,6 @@ obj-$(CONFIG_TRACEPOINTS) += power-traces.o
 ifeq ($(CONFIG_TRACING),y)
 obj-$(CONFIG_KGDB_KDB) += trace_kdb.o
 endif
+obj-$(CONFIG_PROBE_EVENTS) +=trace_probe.o
 
 libftrace-y := ftrace.o
kernel/trace/trace_kprobe.c +20 -874

1,012 lines not on this page · raw message

kernel/trace/trace_probe.c new file +778 -0

778 lines not on this page · raw message

kernel/trace/trace_probe.h new file +160 -0

160 lines not on this page · raw message

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#108 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 19/26] tracing: Extract out common code for kprobes/uprobes traceevents.

Quoted 3 lines · Srikar Dronamraju

Move parts of trace_kprobe.c that can be shared with upcoming trace_uprobe.c. Common code to kernel/trace/trace_probe.h and kernel/trace/trace_probe.c.

This seems including different changes (as below). Please separate it. (Maybe "Use Boolean instead of integer" patch? :))

[...]

Quoted 9 lines
kernel/trace/trace_kprobe.c +1 -1
@@ -651,7 +107,7 @@ static struct trace_probe *alloc_trace_probe(const char *group,
 					     void *addr,
 					     const char *symbol,
 					     unsigned long offs,
-					     int nargs, int is_return)
+					     int nargs, bool is_return)
 {
 	struct trace_probe *tp;
 	int ret = -ENOMEM;

[...]

Quoted 14 lines
kernel/trace/trace_kprobe.c +1 -2
@@ -1153,7 +366,7 @@ static int create_trace_probe(int argc, char **argv)
 	 */
 	struct trace_probe *tp;
 	int i, ret = 0;
-	int is_return = 0, is_delete = 0;
+	bool is_return = false, is_delete = false;
 	char *symbol = NULL, *event = NULL, *group = NULL;
 	char *arg;
 	unsigned long offset = 0;
@@ -1162,11 +375,11 @@ static int create_trace_probe(int argc, char **argv)
 
 	/* argc must be >= 1 */
 	if (argv[0][0] == 'p')
-		is_return = 0;

kernel/trace/trace_kprobe.c static int create_trace_probe(int argc, char **argv) · line 385

Quoted 10 lines
kernel/trace/trace_kprobe.c +3 -2
+		is_return = false;
 	else if (argv[0][0] == 'r')
-		is_return = 1;
+		is_return = true;
 	else if (argv[0][0] == '-')
-		is_delete = 1;
+		is_delete = true;
 	else {
 		pr_info("Probe definition must be started with 'p', 'r' or"
 			" '-'.\n");

And also, this has bugs in selftest code.

[...]

Quoted 24 lines
kernel/trace/trace_kprobe.c +3 -3
@@ -2020,7 +1166,7 @@ static __init int kprobe_trace_self_tests_init(void)
 
 	pr_info("Testing kprobe tracing: ");
 
-	ret = command_trace_probe("p:testprobe kprobe_trace_selftest_target "
+	ret = traceprobe_command("p:testprobe kprobe_trace_selftest_target "
 				  "$stack $stack0 +0($stack)");
 	if (WARN_ON_ONCE(ret)) {
 		pr_warning("error on probing function entry.\n");
@@ -2035,7 +1181,7 @@ static __init int kprobe_trace_self_tests_init(void)
 			enable_trace_probe(tp, TP_FLAG_TRACE);
 	}
 
-	ret = command_trace_probe("r:testprobe2 kprobe_trace_selftest_target "
+	ret = traceprobe_command("r:testprobe2 kprobe_trace_selftest_target "
 				  "$retval");
 	if (WARN_ON_ONCE(ret)) {
 		pr_warning("error on probing function return.\n");
@@ -2055,13 +1201,13 @@ static __init int kprobe_trace_self_tests_init(void)
 
 	ret = target(1, 2, 3, 4, 5, 6);
 
-	ret = command_trace_probe("-:testprobe");
+	ret = traceprobe_command_trace_probe("-:testprobe");

kernel/trace/trace_kprobe.c static __init int kprobe_trace_self_tests_init(void) · line 1213

Quoted 10 lines
kernel/trace/trace_kprobe.c +1 -1
 	if (WARN_ON_ONCE(ret)) {
 		pr_warning("error on deleting a probe.\n");
 		warn++;
 	}
 
-	ret = command_trace_probe("-:testprobe2");
+	ret = traceprobe_command_trace_probe("-:testprobe2");
 	if (WARN_ON_ONCE(ret)) {
 		pr_warning("error on deleting a probe.\n");
 		warn++;

traceprobe_command(str) and traceprobe_command_trace_probe(str) should be traceprobe_command(str, create_trace_probe).

Thank you,

Signature · 11 lines
Masami HIRAMATSU
Software Platform Research Dept. Linux Technology Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#109 lore raw

PATCH v5 20/26 tracing: uprobes trace_event interface

In reply to Srikar Dronamraju

Implements trace_event support for uprobes. In its current form it can be used to put probes at a specified offset in a file and dump the required registers when the code flow reaches the probed address.

The following example shows how to dump the instruction pointer and %ax a register at the probed text address. Here we are trying to probe zfree in /bin/zsh

# cd /sys/kernel/debug/tracing/
# cat /proc/`pgrep  zsh`/maps | grep /bin/zsh | grep r-xp
00400000-0048a000 r-xp 00000000 08:03 130904 /bin/zsh
# objdump -T /bin/zsh | grep -w zfree
0000000000446420 g    DF .text  0000000000000012  Base        zfree
# echo 'p /bin/zsh:0x46420 %ip %ax' > uprobe_events
# cat uprobe_events
p:uprobes/p_zsh_0x46420 /bin/zsh:0x0000000000046420
# echo 1 > events/uprobes/enable
# sleep 20
# echo 0 > events/uprobes/enable
# cat trace
# tracer: nop
#
#           TASK-PID    CPU#    TIMESTAMP  FUNCTION
#              | |       |          |         |
             zsh-24842 [006] 258544.995456: p_zsh_0x46420: (0x446420) arg1=446421 arg2=79
             zsh-24842 [007] 258545.000270: p_zsh_0x46420: (0x446420) arg1=446421 arg2=79
             zsh-24842 [002] 258545.043929: p_zsh_0x46420: (0x446420) arg1=446421 arg2=79
             zsh-24842 [004] 258547.046129: p_zsh_0x46420: (0x446420) arg1=446421 arg2=79

TODO: Connect a filter to a consumer.

---
 arch/Kconfig                |    8 
 kernel/trace/Kconfig        |   16 +
 kernel/trace/Makefile       |    1 
 kernel/trace/trace.h        |    5 
 kernel/trace/trace_kprobe.c |    4 
 kernel/trace/trace_probe.c  |   14 +
 kernel/trace/trace_probe.h  |    6 
 kernel/trace/trace_uprobe.c |  770 +++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 809 insertions(+), 15 deletions(-)
 create mode 100644 kernel/trace/trace_uprobe.c
arch/Kconfig +1 -7
index d6a4e1d..53ce702 100644
@@ -62,14 +62,8 @@ config OPTPROBES
 	depends on !PREEMPT
 
 config UPROBES
-	bool "User-space probes (EXPERIMENTAL)"
 	select MM_OWNER
-	help
-	  Uprobes enables kernel subsystems to establish probepoints
-	  in user applications and execute handler functions when
-	  the probepoints are hit.
-
-	  If in doubt, say "N".
+	def_bool n
 
 config HAVE_EFFICIENT_UNALIGNED_ACCESS
 	bool
kernel/trace/Kconfig +16 -0
index 520106a..b001fb1 100644
@@ -386,6 +386,22 @@ config KPROBE_EVENT
 	  This option is also required by perf-probe subcommand of perf tools.
 	  If you want to use perf tools, this option is strongly recommended.
 
+config UPROBE_EVENT
+	bool "Enable uprobes-based dynamic events"
+	depends on ARCH_SUPPORTS_UPROBES
+	depends on MMU
+	select UPROBES
+	select PROBE_EVENTS
+	select TRACING
+	default n
+	help
+	  This allows the user to add tracing events on top of userspace dynamic
+	  events (similar to tracepoints) on the fly via the traceevents interface.
+	  Those events can be inserted wherever uprobes can probe, and record
+	  various registers.
+	  This option is required if you plan to use perf-probe subcommand of perf
+	  tools on user space applications.
+
 config PROBE_EVENTS
 	def_bool n
 
kernel/trace/Makefile +1 -0
index 692223a..bb3d3ff 100644
@@ -57,5 +57,6 @@ ifeq ($(CONFIG_TRACING),y)
 obj-$(CONFIG_KGDB_KDB) += trace_kdb.o
 endif
 obj-$(CONFIG_PROBE_EVENTS) +=trace_probe.o
+obj-$(CONFIG_UPROBE_EVENT) += trace_uprobe.o
 
 libftrace-y := ftrace.o
kernel/trace/trace.h +5 -0
index 616846b..c9b737c 100644
@@ -97,6 +97,11 @@ struct kretprobe_trace_entry_head {
 	unsigned long		ret_ip;
 };
 
+struct uprobe_trace_entry_head {
+	struct trace_entry	ent;
+	unsigned long		ip;
+};
+
 /*
  * trace_flag_type is an enumeration that holds different
  * states when a trace occurs. These are:
kernel/trace/trace_kprobe.c +2 -2
index d5f4e51..b156d8f 100644
@@ -514,8 +514,8 @@ static int create_trace_probe(int argc, char **argv)
 		}
 
 		/* Parse fetch argument */
-		ret = traceprobe_parse_probe_arg(arg, &tp->size, &tp->args[i],
-								is_return);
+		ret = traceprobe_parse_probe_arg(arg, &tp->size,
+					&tp->args[i], is_return, true);
 		if (ret) {
 			pr_info("Parse error at argument[%d]. (%d)\n", i, ret);
 			goto error;
kernel/trace/trace_probe.c +10 -4
index 52580b5..d8f71ef 100644
@@ -528,13 +528,17 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
 
 /* Recursive argument parser */
 static int parse_probe_arg(char *arg, const struct fetch_type *t,
-		     struct fetch_param *f, bool is_return)
+		     struct fetch_param *f, bool is_return, bool is_kprobe)
 {
 	int ret = 0;
 	unsigned long param;
 	long offset;
 	char *tmp;
 
+	/* Until uprobe_events supports only reg arguments */
+	if (!is_kprobe && arg[0] != '%')
+		return -EINVAL;
+
 	switch (arg[0]) {
 	case '$':
 		ret = parse_probe_vars(arg + 1, t, f, is_return);
@@ -584,7 +588,8 @@ static int parse_probe_arg(char *arg, const struct fetch_type *t,
 			if (!dprm)
 				return -ENOMEM;
 			dprm->offset = offset;
-			ret = parse_probe_arg(arg, t2, &dprm->orig, is_return);
+			ret = parse_probe_arg(arg, t2, &dprm->orig, is_return,
+							is_kprobe);
 			if (ret)
 				kfree(dprm);
 			else {
@@ -638,7 +643,7 @@ static int __parse_bitfield_probe_arg(const char *bf,
 
 /* String length checking wrapper */
 int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
-		struct probe_arg *parg, bool is_return)
+		struct probe_arg *parg, bool is_return, bool is_kprobe)
 {
 	const char *t;
 	int ret;
@@ -664,7 +669,8 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
 	}
 	parg->offset = *size;
 	*size += parg->type->size;
-	ret = parse_probe_arg(arg, parg->type, &parg->fetch, is_return);
+	ret = parse_probe_arg(arg, parg->type, &parg->fetch, is_return,
+							is_kprobe);
 	if (ret >= 0 && t != NULL)
 		ret = __parse_bitfield_probe_arg(t, parg->type, &parg->fetch);
 	if (ret >= 0) {
kernel/trace/trace_probe.h +4 -2
index 500a08f..0cab89a 100644
@@ -48,6 +48,7 @@
 #define FIELD_STRING_IP "__probe_ip"
 #define FIELD_STRING_RETIP "__probe_ret_ip"
 #define FIELD_STRING_FUNC "__probe_func"
+#define FIELD_STRING_PID "__probe_pid"
 
 #undef DEFINE_FIELD
 #define DEFINE_FIELD(type, item, name, is_signed)			\
@@ -65,6 +66,7 @@
 #define TP_FLAG_TRACE	1
 #define TP_FLAG_PROFILE	2
 #define TP_FLAG_REGISTERED 4
+#define TP_FLAG_UPROBE	8
 
 
 /* data_rloc: data relative location, compatible with u32 */
@@ -131,7 +133,7 @@ static inline __kprobes void call_fetch(struct fetch_param *fprm,
 }
 
 /* Check the name is good for event/group/fields */
-static int is_good_name(const char *name)
+static inline int is_good_name(const char *name)
 {
 	if (!isalpha(*name) && *name != '_')
 		return 0;
@@ -143,7 +145,7 @@ static int is_good_name(const char *name)
 }
 
 extern int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
-		   struct probe_arg *parg, bool is_return);
+		   struct probe_arg *parg, bool is_return, bool is_kprobe);
 
 extern int traceprobe_conflict_field_name(const char *name,
 			       struct probe_arg *args, int narg);
kernel/trace/trace_uprobe.c new file +770 -0

770 lines not on this page · raw message

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#110 lore raw

PATCH v5 21/26 tracing: uprobes Documentation

In reply to Srikar Dronamraju

Documents how to trace user space applications using uprobe tracer.

---
 Documentation/trace/uprobetracer.txt |   94 ++++++++++++++++++++++++++++++++++
 1 files changed, 94 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/trace/uprobetracer.txt
Documentation/trace/uprobetracer.txt new file +94 -0
new file mode 100644
index 0000000..6c18ffe
@@ -0,0 +1,94 @@
+		Uprobe-tracer: Uprobe-based Event Tracing
+		=========================================
+                 Documentation is written by Srikar Dronamraju
+
+Overview
+--------
+These events are similar to kprobe based events.
+To enable this feature, build your kernel with CONFIG_UPROBE_EVENTS=y.
+
+Similar to the kprobe-event tracer, this doesn't need to be activated via
+current_tracer. Instead of that, add probe points via
+/sys/kernel/debug/tracing/uprobe_events, and enable it via
+/sys/kernel/debug/tracing/events/uprobes/<EVENT>/enabled.
+
+
+Synopsis of uprobe_tracer
+-------------------------
+  p[:[GRP/]EVENT] PATH:SYMBOL[+offs] [FETCHARGS]	: Set a probe
+
+ GRP		: Group name. If omitted, use "uprobes" for it.
+ EVENT		: Event name. If omitted, the event name is generated
+		  based on SYMBOL+offs.
+ PATH		: path to an executable or a library.
+ SYMBOL[+offs]	: Symbol+offset where the probe is inserted.
+
+ FETCHARGS	: Arguments. Each probe can have up to 128 args.
+  %REG		: Fetch register REG
+
+Event Profiling
+---------------
+ You can check the total number of probe hits and probe miss-hits via
+/sys/kernel/debug/tracing/uprobe_profile.
+ The first column is event name, the second is the number of probe hits,
+the third is the number of probe miss-hits.
+
+Usage examples
+--------------
+To add a probe as a new event, write a new definition to uprobe_events
+as below.
+
+  echo 'p: /bin/bash:0x4245c0' > /sys/kernel/debug/tracing/uprobe_events
+
+ This sets a uprobe at an offset of 0x4245c0 in the executable /bin/bash
+
+
+  echo > /sys/kernel/debug/tracing/uprobe_events
+
+ This clears all probe points.
+
+The following example shows how to dump the instruction pointer and %ax
+a register at the probed text address.  Here we are trying to probe
+function zfree in /bin/zsh
+
+    # cd /sys/kernel/debug/tracing/
+    # cat /proc/`pgrep  zsh`/maps | grep /bin/zsh | grep r-xp
+    00400000-0048a000 r-xp 00000000 08:03 130904 /bin/zsh
+    # objdump -T /bin/zsh | grep -w zfree
+    0000000000446420 g    DF .text  0000000000000012  Base        zfree
+
+0x46420 is the offset of zfree in object /bin/zsh that is loaded at
+0x00400000. Hence the command to probe would be :
+
+    # echo 'p /bin/zsh:0x46420 %ip %ax' > uprobe_events
+
+We can see the events that are registered by looking at the uprobe_events
+file.
+
+    # cat uprobe_events
+    p:uprobes/p_zsh_0x46420 /bin/zsh:0x0000000000046420
+
+Right after definition, each event is disabled by default. For tracing these
+events, you need to enable it by:
+
+    # echo 1 > events/uprobes/enable
+
+Lets disable the event after sleeping for some time.
+    # sleep 20
+    # echo 0 > events/uprobes/enable
+
+And you can see the traced information via /sys/kernel/debug/tracing/trace.
+
+    # cat trace
+    # tracer: nop
+    #
+    #           TASK-PID    CPU#    TIMESTAMP  FUNCTION
+    #              | |       |          |         |
+                 zsh-24842 [006] 258544.995456: p_zsh_0x46420: (0x446420) arg1=446421 arg2=79
+                 zsh-24842 [007] 258545.000270: p_zsh_0x46420: (0x446420) arg1=446421 arg2=79
+                 zsh-24842 [002] 258545.043929: p_zsh_0x46420: (0x446420) arg1=446421 arg2=79
+                 zsh-24842 [004] 258547.046129: p_zsh_0x46420: (0x446420) arg1=446421 arg2=79
+
+Each line shows us probes were triggered for a pid 24842 with ip being
+0x446421 and contents of ax register being 79.
+
#111 lore raw

PATCH v5 22/26 perf: rename target_module to target

In reply to Srikar Dronamraju

This is a precursor patch that modifies names that refer to kernel/module to also refer to user space names.

---
 tools/perf/builtin-probe.c    |   12 ++++++------
 tools/perf/util/probe-event.c |   26 +++++++++++++-------------
 2 files changed, 19 insertions(+), 19 deletions(-)
tools/perf/builtin-probe.c +6 -6
index 710ae3d..93d5171 100644
@@ -61,7 +61,7 @@ static struct {
 	struct perf_probe_event events[MAX_PROBES];
 	struct strlist *dellist;
 	struct line_range line_range;
-	const char *target_module;
+	const char *target;
 	int max_probe_points;
 	struct strfilter *filter;
 } params;
@@ -249,7 +249,7 @@ static const struct option options[] = {
 		   "file", "vmlinux pathname"),
 	OPT_STRING('s', "source", &symbol_conf.source_prefix,
 		   "directory", "path to kernel source"),
-	OPT_STRING('m', "module", &params.target_module,
+	OPT_STRING('m', "module", &params.target,
 		   "modname|path",
 		   "target module name (for online) or path (for offline)"),
 #endif
@@ -336,7 +336,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 		if (!params.filter)
 			params.filter = strfilter__new(DEFAULT_FUNC_FILTER,
 						       NULL);
-		ret = show_available_funcs(params.target_module,
+		ret = show_available_funcs(params.target,
 					   params.filter);
 		strfilter__delete(params.filter);
 		if (ret < 0)
@@ -357,7 +357,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 			usage_with_options(probe_usage, options);
 		}
 
-		ret = show_line_range(&params.line_range, params.target_module);
+		ret = show_line_range(&params.line_range, params.target);
 		if (ret < 0)
 			pr_err("  Error: Failed to show lines. (%d)\n", ret);
 		return ret;
@@ -374,7 +374,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 
 		ret = show_available_vars(params.events, params.nevents,
 					  params.max_probe_points,
-					  params.target_module,
+					  params.target,
 					  params.filter,
 					  params.show_ext_vars);
 		strfilter__delete(params.filter);
@@ -396,7 +396,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 	if (params.nevents) {
 		ret = add_perf_probe_events(params.events, params.nevents,
 					    params.max_probe_points,
-					    params.target_module,
+					    params.target,
 					    params.force_add);
 		if (ret < 0) {
 			pr_err("  Error: Failed to add events. (%d)\n", ret);
tools/perf/util/probe-event.c +13 -13
index 1c7bfa5..3ee7c39 100644
@@ -275,10 +275,10 @@ static int add_module_to_probe_trace_events(struct probe_trace_event *tevs,
 /* Try to find perf_probe_event with debuginfo */
 static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
 					  struct probe_trace_event **tevs,
-					  int max_tevs, const char *module)
+					  int max_tevs, const char *target)
 {
 	bool need_dwarf = perf_probe_event_need_dwarf(pev);
-	struct debuginfo *dinfo = open_debuginfo(module);
+	struct debuginfo *dinfo = open_debuginfo(target);
 	int ntevs, ret = 0;
 
 	if (!dinfo) {
@@ -297,9 +297,9 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
 
 	if (ntevs > 0) {	/* Succeeded to find trace events */
 		pr_debug("find %d probe_trace_events.\n", ntevs);
-		if (module)
+		if (target)
 			ret = add_module_to_probe_trace_events(*tevs, ntevs,
-							       module);
+							       target);
 		return ret < 0 ? ret : ntevs;
 	}
 
@@ -1798,14 +1798,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 
 static int convert_to_probe_trace_events(struct perf_probe_event *pev,
 					  struct probe_trace_event **tevs,
-					  int max_tevs, const char *module)
+					  int max_tevs, const char *target)
 {
 	struct symbol *sym;
 	int ret = 0, i;
 	struct probe_trace_event *tev;
 
 	/* Convert perf_probe_event with debuginfo */
-	ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module);
+	ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target);
 	if (ret != 0)
 		return ret;	/* Found in debuginfo or got an error */
 
@@ -1821,8 +1821,8 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
 		goto error;
 	}
 
-	if (module) {
-		tev->point.module = strdup(module);
+	if (target) {
+		tev->point.module = strdup(target);
 		if (tev->point.module == NULL) {
 			ret = -ENOMEM;
 			goto error;
@@ -1886,7 +1886,7 @@ struct __event_package {
 };
 
 int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
-			  int max_tevs, const char *module, bool force_add)
+			  int max_tevs, const char *target, bool force_add)
 {
 	int i, j, ret;
 	struct __event_package *pkgs;
@@ -1909,7 +1909,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
 		ret  = convert_to_probe_trace_events(pkgs[i].pev,
 						     &pkgs[i].tevs,
 						     max_tevs,
-						     module);
+						     target);
 		if (ret < 0)
 			goto end;
 		pkgs[i].ntevs = ret;
@@ -2063,7 +2063,7 @@ static int filter_available_functions(struct map *map __unused,
 	return 1;
 }
 
-int show_available_funcs(const char *module, struct strfilter *_filter)
+int show_available_funcs(const char *target, struct strfilter *_filter)
 {
 	struct map *map;
 	int ret;
@@ -2074,9 +2074,9 @@ int show_available_funcs(const char *module, struct strfilter *_filter)
 	if (ret < 0)
 		return ret;
 
-	map = kernel_get_module_map(module);
+	map = kernel_get_module_map(target);
 	if (!map) {
-		pr_err("Failed to find %s map.\n", (module) ? : "kernel");
+		pr_err("Failed to find %s map.\n", (target) ? : "kernel");
 		return -EINVAL;
 	}
 	available_func_filter = _filter;
#112 lore raw

PATCH v5 23/26 perf: perf interface for uprobes

In reply to Srikar Dronamraju

Enhances perf probe to user space executables and libraries. Provides very basic support for uprobes.

[ Probing a function in the executable using function name ]
-------------------------------------------------------------
[root@localhost ~]# perf probe -x /bin/zsh zfree Add new event:
probe_zsh:zfree (on /bin/zsh:0x45400)

You can now use it on all perf tools, such as:

perf record -e probe_zsh:zfree -aR sleep 1

[root@localhost ~]# perf record -e probe_zsh:zfree -aR sleep 15 [ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.314 MB perf.data (~13715 samples) ] [root@localhost ~]# perf report --stdio
# Events: 3K probe_zsh:zfree
#

# Overhead  Command  Shared Object  Symbol
# ........  .......  .............  ......

#
100.00% zsh zsh [.] zfree

#
# (For a higher level overview, try: perf report --sort comm,dso) #
[root@localhost ~]

[ Probing a library function using function name ]
--------------------------------------------------
[root@localhost]#

[root@localhost]# perf probe -x /lib64/libc.so.6 malloc
Add new event:
  probe_libc:malloc    (on /lib64/libc-2.5.so:0x74dc0)

You can now use it on all perf tools, such as:

perf record -e probe_libc:malloc -aR sleep 1

[root@localhost]#

[root@localhost]# perf probe --list
  probe_libc:malloc    (on /lib64/libc-2.5.so:0x0000000000074dc0)
---
 tools/perf/builtin-probe.c    |   37 ++++
 tools/perf/util/probe-event.c |  346 +++++++++++++++++++++++++++++++++--------
 tools/perf/util/probe-event.h |    8 +
 tools/perf/util/symbol.c      |   10 +
 tools/perf/util/symbol.h      |    1 
 5 files changed, 324 insertions(+), 78 deletions(-)
tools/perf/builtin-probe.c +33 -4
index 93d5171..43e6321 100644
@@ -57,6 +57,7 @@ static struct {
 	bool show_ext_vars;
 	bool show_funcs;
 	bool mod_events;
+	bool uprobes;
 	int nevents;
 	struct perf_probe_event events[MAX_PROBES];
 	struct strlist *dellist;
@@ -78,6 +79,7 @@ static int parse_probe_event(const char *str)
 		return -1;
 	}
 
+	pev->uprobes = params.uprobes;
 	/* Parse a perf-probe command into event */
 	ret = parse_perf_probe_command(str, pev);
 	pr_debug("%d arguments\n", pev->nargs);
@@ -128,6 +130,27 @@ static int opt_del_probe_event(const struct option *opt __used,
 	return 0;
 }
 
+static int opt_set_target(const struct option *opt, const char *str,
+			int unset __used)
+{
+	int ret = -ENOENT;
+
+	if  (str && !params.target) {
+		if (!strcmp(opt->long_name, "exec"))
+			params.uprobes = true;
+#ifdef DWARF_SUPPORT
+		else if (!strcmp(opt->long_name, "module"))
+			params.uprobes = false;
+#endif
+		else
+			return ret;
+
+		params.target = str;
+		ret = 0;
+	}
+	return ret;
+}
+
 #ifdef DWARF_SUPPORT
 static int opt_show_lines(const struct option *opt __used,
 			  const char *str, int unset __used)
@@ -249,9 +272,9 @@ static const struct option options[] = {
 		   "file", "vmlinux pathname"),
 	OPT_STRING('s', "source", &symbol_conf.source_prefix,
 		   "directory", "path to kernel source"),
-	OPT_STRING('m', "module", &params.target,
-		   "modname|path",
-		   "target module name (for online) or path (for offline)"),
+	OPT_CALLBACK('m', "module", NULL, "modname|path",
+		"target module name (for online) or path (for offline)",
+		opt_set_target),
 #endif
 	OPT__DRY_RUN(&probe_event_dry_run),
 	OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
@@ -263,6 +286,8 @@ static const struct option options[] = {
 		     "\t\t\t(default: \"" DEFAULT_VAR_FILTER "\" for --vars,\n"
 		     "\t\t\t \"" DEFAULT_FUNC_FILTER "\" for --funcs)",
 		     opt_set_filter),
+	OPT_CALLBACK('x', "exec", NULL, "executable|path",
+			"target executable name or path", opt_set_target),
 	OPT_END()
 };
 
@@ -313,6 +338,10 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 			pr_err("  Error: Don't use --list with --funcs.\n");
 			usage_with_options(probe_usage, options);
 		}
+		if (params.uprobes) {
+			pr_warning("  Error: Don't use --list with --exec.\n");
+			usage_with_options(probe_usage, options);
+		}
 		ret = show_perf_probe_events();
 		if (ret < 0)
 			pr_err("  Error: Failed to show event list. (%d)\n",
@@ -346,7 +375,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 	}
 
 #ifdef DWARF_SUPPORT
-	if (params.show_lines) {
+	if (params.show_lines && !params.uprobes) {
 		if (params.mod_events) {
 			pr_err("  Error: Don't use --line with"
 			       " --add/--del.\n");
tools/perf/util/probe-event.c +276 -70

549 lines not on this page · raw message

tools/perf/util/probe-event.h +5 -3
index a7dee83..9e8c846 100644
@@ -7,7 +7,7 @@
 
 extern bool probe_event_dry_run;
 
-/* kprobe-tracer tracing point */
+/* kprobe-tracer and uprobe-tracer tracing point */
 struct probe_trace_point {
 	char		*symbol;	/* Base symbol */
 	char		*module;	/* Module name */
@@ -21,7 +21,7 @@ struct probe_trace_arg_ref {
 	long				offset;	/* Offset value */
 };
 
-/* kprobe-tracer tracing argument */
+/* kprobe-tracer and uprobe-tracer tracing argument */
 struct probe_trace_arg {
 	char				*name;	/* Argument name */
 	char				*value;	/* Base value */
@@ -29,12 +29,13 @@ struct probe_trace_arg {
 	struct probe_trace_arg_ref	*ref;	/* Referencing offset */
 };
 
-/* kprobe-tracer tracing event (point + arg) */
+/* kprobe-tracer and uprobe-tracer tracing event (point + arg) */
 struct probe_trace_event {
 	char				*event;	/* Event name */
 	char				*group;	/* Group name */
 	struct probe_trace_point	point;	/* Trace point */
 	int				nargs;	/* Number of args */
+	bool				uprobes;	/* uprobes only */
 	struct probe_trace_arg		*args;	/* Arguments */
 };
 
@@ -70,6 +71,7 @@ struct perf_probe_event {
 	char			*group;	/* Group name */
 	struct perf_probe_point	point;	/* Probe point */
 	int			nargs;	/* Number of arguments */
+	bool			uprobes;
 	struct perf_probe_arg	*args;	/* Arguments */
 };
 
tools/perf/util/symbol.c +9 -1
index 245e60d..7bc468f 100644
@@ -567,7 +567,7 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map,
 	struct machine *machine = kmaps->machine;
 	struct map *curr_map = map;
 	struct symbol *pos;
-	int count = 0, moved = 0;	
+	int count = 0, moved = 0;
 	struct rb_root *root = &dso->symbols[map->type];
 	struct rb_node *next = rb_first(root);
 	int kernel_range = 0;
@@ -2685,3 +2685,11 @@ int machine__load_vmlinux_path(struct machine *machine, enum map_type type,
 
 	return ret;
 }
+
+struct map *dso__new_map(const char *name)
+{
+	struct dso *dso = dso__new(name);
+	struct map *map = map__new2(0, dso, MAP__FUNCTION);
+
+	return map;
+}
tools/perf/util/symbol.h +1 -0
index 7733f0b..cf567d2 100644
@@ -216,6 +216,7 @@ void dso__set_long_name(struct dso *dso, char *name);
 void dso__set_build_id(struct dso *dso, void *build_id);
 void dso__read_running_kernel_build_id(struct dso *dso,
 				       struct machine *machine);
+struct map *dso__new_map(const char *name);
 struct symbol *dso__find_symbol(struct dso *dso, enum map_type type,
 				u64 addr);
 struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type,

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#113 lore raw

PATCH v5 24/26 perf: show possible probes in a given executable file or library.

In reply to Srikar Dronamraju

Enhances -F/--funcs option of "perf probe" to list possible probe points in an executable file or library.

Show last 10 functions in /bin/zsh.

# perf probe -F -x /bin/zsh | tail
zstrtol
ztrcmp
ztrdup
ztrduppfx
ztrftime
ztrlen
ztrncpy
ztrsub
zwarn
zwarnnam

Show first 10 functions in /lib/libc.so.6

# perf probe -F -x /lib/libc.so.6 | head
_IO_adjust_column
_IO_adjust_wcolumn
_IO_default_doallocate
_IO_default_finish
_IO_default_pbackfail
_IO_default_uflow
_IO_default_xsgetn
_IO_default_xsputn
_IO_do_write@@GLIBC_2.2.5
_IO_doallocbuf
---
 tools/perf/builtin-probe.c    |    4 +--
 tools/perf/util/probe-event.c |   56 +++++++++++++++++++++++++++++++----------
 tools/perf/util/probe-event.h |    4 +--
 3 files changed, 47 insertions(+), 17 deletions(-)
tools/perf/builtin-probe.c +2 -2
index 43e6321..5e7622c 100644
@@ -365,8 +365,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 		if (!params.filter)
 			params.filter = strfilter__new(DEFAULT_FUNC_FILTER,
 						       NULL);
-		ret = show_available_funcs(params.target,
-					   params.filter);
+		ret = show_available_funcs(params.target, params.filter,
+					params.uprobes);
 		strfilter__delete(params.filter);
 		if (ret < 0)
 			pr_err("  Error: Failed to show functions."
tools/perf/util/probe-event.c +43 -13

87 lines not on this page · raw message

tools/perf/util/probe-event.h +2 -2
index 9e8c846..f9f3de8 100644
@@ -131,8 +131,8 @@ extern int show_line_range(struct line_range *lr, const char *module);
 extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
 			       int max_probe_points, const char *module,
 			       struct strfilter *filter, bool externs);
-extern int show_available_funcs(const char *module, struct strfilter *filter);
-
+extern int show_available_funcs(const char *module, struct strfilter *filter,
+				bool user);
 
 /* Maximum index number of event-name postfix */
 #define MAX_EVENT_INDEX	1024
#114 lore raw

PATCH v5 25/26 perf: Documentation for perf uprobes

In reply to Srikar Dronamraju

Modify perf-probe.txt to include uprobe documentation

---
 tools/perf/Documentation/perf-probe.txt |   14 ++++++++++++++
 1 files changed, 14 insertions(+), 0 deletions(-)
tools/perf/Documentation/perf-probe.txt +14 -0
index 800775e..3c98a54 100644
@@ -78,6 +78,8 @@ OPTIONS
 -F::
 --funcs::
 	Show available functions in given module or kernel.
+	With -x/--exec, can also list functions in a user space executable
+	/ shared library.
 
 --filter=FILTER::
 	(Only for --vars and --funcs) Set filter. FILTER is a combination of glob
@@ -98,6 +100,11 @@ OPTIONS
 --max-probes::
 	Set the maximum number of probe points for an event. Default is 128.
 
+-x::
+--exec=PATH::
+	Specify path to the executable or shared library file for user
+	space tracing. Can also be used with --funcs option.
+
 PROBE SYNTAX
 ------------
 Probe points are defined by following syntax.
@@ -182,6 +189,13 @@ Delete all probes on schedule().
 
  ./perf probe --del='schedule*'
 
+Add probes at zfree() function on /bin/zsh
+
+ ./perf probe -x /bin/zsh zfree
+
+Add probes at malloc() function on libc
+
+ ./perf probe -x /lib/libc.so.6 malloc
 
 SEE ALSO
 --------

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#115 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 25/26] perf: Documentation for perf uprobes

Quoted 1 line · Srikar Dronamraju

Modify perf-probe.txt to include uprobe documentation

This change should be included in 23rd and 24th patches, because the documentation should be updated with the tool enhancement.

Thank you,

Quoted 50 lines
---
 tools/perf/Documentation/perf-probe.txt |   14 ++++++++++++++
 1 files changed, 14 insertions(+), 0 deletions(-)
tools/perf/Documentation/perf-probe.txt +14 -1
index 800775e..3c98a54 100644
@@ -78,6 +78,8 @@ OPTIONS
 -F::
 --funcs::
 	Show available functions in given module or kernel.
+	With -x/--exec, can also list functions in a user space executable
+	/ shared library.
 
 --filter=FILTER::
 	(Only for --vars and --funcs) Set filter. FILTER is a combination of glob
@@ -98,6 +100,11 @@ OPTIONS
 --max-probes::
 	Set the maximum number of probe points for an event. Default is 128.
 
+-x::
+--exec=PATH::
+	Specify path to the executable or shared library file for user
+	space tracing. Can also be used with --funcs option.
+
 PROBE SYNTAX
 ------------
 Probe points are defined by following syntax.
@@ -182,6 +189,13 @@ Delete all probes on schedule().
 
  ./perf probe --del='schedule*'
 
+Add probes at zfree() function on /bin/zsh
+
+ ./perf probe -x /bin/zsh zfree
+
+Add probes at malloc() function on libc
+
+ ./perf probe -x /lib/libc.so.6 malloc
 
 SEE ALSO
 --------
--

To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Signature · 11 lines
Masami HIRAMATSU
Software Platform Research Dept. Linux Technology Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#116 lore raw

PATCH v5 26/26 uprobes: queue signals while thread is singlestepping.

In reply to Srikar Dronamraju

- Queue signals delivered from the time we singlestep till completion of postprocessing. The queueing is done on a per-task basis.
- After singlestep completion, dequeue the signals.

---
 include/linux/uprobes.h |    3 ++-
 kernel/signal.c         |   22 +++++++++++++++++++++-
 kernel/uprobes.c        |   22 ++++++++++++++++------
 3 files changed, 39 insertions(+), 8 deletions(-)
include/linux/uprobes.h +2 -1
index a407d17..189cdce 100644
@@ -24,7 +24,7 @@
  */
 
 #include <linux/rbtree.h>
-
+#include <linux/signal.h>	/* sigpending */
 struct vm_area_struct;
 #ifdef CONFIG_ARCH_SUPPORTS_UPROBES
 #include <asm/uprobes.h>
@@ -90,6 +90,7 @@ struct uprobe_task {
 	struct uprobe_task_arch_info tskinfo;
 
 	struct uprobe *active_uprobe;
+	struct sigpending delayed;
 };
 
 /*
kernel/signal.c +21 -1
index 291c970..48b8c7c 100644
@@ -1034,6 +1034,11 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,
 		return 0;
 
 	pending = group ? &t->signal->shared_pending : &t->pending;
+#ifdef CONFIG_UPROBES
+	if (!group && t->utask && t->utask->active_uprobe)
+		pending = &t->utask->delayed;
+#endif
+
 	/*
 	 * Short-circuit ignored signals and support queuing
 	 * exactly one non-rt signal, so that we can get more
@@ -1106,6 +1111,11 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,
 		}
 	}
 
+#ifdef CONFIG_UPROBES
+	if (!group && t->utask && t->utask->active_uprobe)
+		return 0;
+#endif
+
 out_set:
 	signalfd_notify(t, sig);
 	sigaddset(&pending->signal, sig);
@@ -1569,6 +1579,13 @@ int send_sigqueue(struct sigqueue *q, struct task_struct *t, int group)
 	}
 	q->info.si_overrun = 0;
 
+#ifdef CONFIG_UPROBES
+	if (!group && t->utask && t->utask->active_uprobe) {
+		pending = &t->utask->delayed;
+		list_add_tail(&q->list, &pending->list);
+		goto out;
+	}
+#endif
 	signalfd_notify(t, sig);
 	pending = group ? &t->signal->shared_pending : &t->pending;
 	list_add_tail(&q->list, &pending->list);
@@ -2199,7 +2216,10 @@ int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,
 			spin_unlock_irq(&sighand->siglock);
 			goto relock;
 		}
-
+#ifdef CONFIG_UPROBES
+		if (current->utask && current->utask->active_uprobe)
+			break;
+#endif
 		signr = dequeue_signal(current, &current->blocked, info);
 
 		if (!signr)
kernel/uprobes.c +16 -6
index ca1f622..d065fa7 100644
@@ -1298,11 +1298,14 @@ void free_uprobe_utask(struct task_struct *tsk)
 static struct uprobe_task *add_utask(void)
 {
 	struct uprobe_task *utask;
+	struct sigpending *delayed;
 
 	utask = kzalloc(sizeof *utask, GFP_KERNEL);
 	if (unlikely(utask == NULL))
 		return ERR_PTR(-ENOMEM);
 
+	delayed = &utask->delayed;
+	INIT_LIST_HEAD(&delayed->list);
 	utask->active_uprobe = NULL;
 	current->utask = utask;
 	return utask;
@@ -1337,6 +1340,16 @@ static bool sstep_complete(struct uprobe *uprobe, struct pt_regs *regs)
 	return true;
 }
 
+static void pushback_signals(struct sigpending *pending)
+{
+	struct sigqueue *q, *tmpq;
+
+	list_for_each_entry_safe(q, tmpq, &pending->list, list) {
+		list_del(&q->list);
+		send_sigqueue(q, current, 0);
+	}
+}
+
 /*
  * uprobe_notify_resume gets called in task context just before returning
  * to userspace.
@@ -1373,7 +1386,6 @@ void uprobe_notify_resume(struct pt_regs *regs)
 			if (!utask)
 				goto cleanup_ret;
 		}
-		/* TODO Start queueing signals. */
 		utask->active_uprobe = u;
 		handler_chain(u, regs);
 		utask->state = UTASK_SSTEP;
@@ -1390,8 +1402,7 @@ void uprobe_notify_resume(struct pt_regs *regs)
 			utask->state = UTASK_RUNNING;
 			user_disable_single_step(current);
 			xol_free_insn_slot(current);
-
-			/* TODO Stop queueing signals. */
+			pushback_signals(&current->utask->delayed);
 		}
 	}
 	return;
@@ -1404,9 +1415,8 @@ void uprobe_notify_resume(struct pt_regs *regs)
 	if (u) {
 		put_uprobe(u);
 		set_instruction_pointer(regs, probept);
-	} else {
-		/*TODO Return SIGTRAP signal */
-	}
+	} else
+		send_sig(SIGTRAP, current, 0);
 }
 
 /*
maintainer
#117 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 26/26] uprobes: queue signals while thread is singlestepping.

Quoted 35 lines · Srikar Dronamraju
kernel/signal.c +17 -0
+#ifdef CONFIG_UPROBES
+       if (!group && t->utask && t->utask->active_uprobe)
+               pending = &t->utask->delayed;
+#endif
+
        /*
         * Short-circuit ignored signals and support queuing
         * exactly one non-rt signal, so that we can get more
@@ -1106,6 +1111,11 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,
                }
        }
 
+#ifdef CONFIG_UPROBES
+       if (!group && t->utask && t->utask->active_uprobe)
+               return 0;
+#endif
+
 out_set:
        signalfd_notify(t, sig);
        sigaddset(&pending->signal, sig);
@@ -1569,6 +1579,13 @@ int send_sigqueue(struct sigqueue *q, struct task_struct *t, int group)
        }
        q->info.si_overrun = 0;
 
+#ifdef CONFIG_UPROBES
+       if (!group && t->utask && t->utask->active_uprobe) {
+               pending = &t->utask->delayed;
+               list_add_tail(&q->list, &pending->list);
+               goto out;
+       }
+#endif
        signalfd_notify(t, sig);
        pending = group ? &t->signal->shared_pending : &t->pending;
        list_add_tail(&q->list, &pending->list);

kernel/signal.c int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka, · line 2222

Quoted 8 lines
kernel/signal.c +4 -1
@@ -2199,7 +2216,10 @@ int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,
                        spin_unlock_irq(&sighand->siglock);
                        goto relock;
                }
-
+#ifdef CONFIG_UPROBES
+               if (current->utask && current->utask->active_uprobe)
+                       break;
+#endif 

That's just crying for something like:

#ifdef CONFIG_UPROBES
static inline bool uprobe_delay_signal(struct task_struct *p)

{
	return p->utask && p->utask->active_uprobe;
}
#else
static inline bool uprobe_delay_signal(struct task_struct *p)
{
	return false;
}

#endif

That'll instantly kill the #ifdeffery as well as describe wtf you're actually doing.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#118 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 26/26] uprobes: queue signals while thread is singlestepping.

Quoted 57 lines · Peter Zijlstra
Quoted 35 lines · Srikar Dronamraju
kernel/signal.c +17 -0
+#ifdef CONFIG_UPROBES
+       if (!group && t->utask && t->utask->active_uprobe)
+               pending = &t->utask->delayed;
+#endif
+
        /*
         * Short-circuit ignored signals and support queuing
         * exactly one non-rt signal, so that we can get more
@@ -1106,6 +1111,11 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,
                }
        }
 
+#ifdef CONFIG_UPROBES
+       if (!group && t->utask && t->utask->active_uprobe)
+               return 0;
+#endif
+
 out_set:
        signalfd_notify(t, sig);
        sigaddset(&pending->signal, sig);
@@ -1569,6 +1579,13 @@ int send_sigqueue(struct sigqueue *q, struct task_struct *t, int group)
        }
        q->info.si_overrun = 0;
 
+#ifdef CONFIG_UPROBES
+       if (!group && t->utask && t->utask->active_uprobe) {
+               pending = &t->utask->delayed;
+               list_add_tail(&q->list, &pending->list);
+               goto out;
+       }
+#endif
        signalfd_notify(t, sig);
        pending = group ? &t->signal->shared_pending : &t->pending;
        list_add_tail(&q->list, &pending->list);

kernel/signal.c int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka, · line 2222

Quoted 8 lines
kernel/signal.c +4 -1
@@ -2199,7 +2216,10 @@ int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,
                        spin_unlock_irq(&sighand->siglock);
                        goto relock;
                }
-
+#ifdef CONFIG_UPROBES
+               if (current->utask && current->utask->active_uprobe)
+                       break;
+#endif 

That's just crying for something like:

#ifdef CONFIG_UPROBES
static inline bool uprobe_delay_signal(struct task_struct *p)

{
	return p->utask && p->utask->active_uprobe;
}
#else
static inline bool uprobe_delay_signal(struct task_struct *p)
{
	return false;
}
Quoted 4 lines

#endif

That'll instantly kill the #ifdeffery as well as describe wtf you're actually doing.

Okay,

I did a rethink and implemented this patch a little differently using block_all_signals, unblock_all_signals. This wouldnt need the #ifdeffery + no changes in kernel/signal.c

Will post the same in the next patchset.

Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#119 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 26/26] uprobes: queue signals while thread is singlestepping.

Srikar, I am still reading this series, need more time to read this patch, but:

Quoted 4 lines · Srikar Dronamraju

I did a rethink and implemented this patch a little differently using block_all_signals, unblock_all_signals. This wouldnt need the #ifdeffery + no changes in kernel/signal.c

No, Please don't. block_all_signals() must be killed. This interface simply do not work. At all. It is buggy as hell. I guess I should ping David Airlie again.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#120 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 26/26] uprobes: queue signals while thread is singlestepping.

Quoted 2 lines · Oleg Nesterov

Srikar, I am still reading this series, need more time to read this patch, but:

Okay,

Quoted 11 lines · Srikar Dronamraju
Quoted 4 lines

I did a rethink and implemented this patch a little differently using block_all_signals, unblock_all_signals. This wouldnt need the #ifdeffery + no changes in kernel/signal.c

No, Please don't. block_all_signals() must be killed. This interface simply do not work. At all. It is buggy as hell. I guess I should ping David Airlie again.

I could use sigprocmask instead of block_all_signals.

The patch (that I sent out as part of v5 patchset) uses per task pending sigqueue and start queueing the signals when the task singlesteps. After completion of singlestep, walks thro the pending signals.

But I was thinking if I should block signals instead of queueing them in a different sigqueue. So Idea is to block signals just before the task enables singlestep and unblock after task disables singlestep.

Instead of using block_all_signals, I could use sigprocmask to achieve the same.

Which approach do you suggest or do you have any other approach to look at?

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

Re: [PATCH v5 3.1.0-rc4-tip 26/26] uprobes: queue signals while thread is singlestepping.

Quoted 5 lines · Srikar Dronamraju

The patch (that I sent out as part of v5 patchset) uses per task pending sigqueue and start queueing the signals when the task singlesteps. After completion of singlestep, walks thro the pending signals.

Yes, I see. Doesn't look very nice ;)

Quoted 3 lines

But I was thinking if I should block signals instead of queueing them in a different sigqueue. So Idea is to block signals just before the task enables singlestep and unblock after task disables singlestep.

Agreed, this looks much, much better. In both cases the task is current, it is safe to change ->blocked.

But please avoid sigprocmask(), we have set_current_blocked().

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#122 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 26/26] uprobes: queue signals while thread is singlestepping.

Quoted 5 lines · Oleg Nesterov

Agreed, this looks much, much better. In both cases the task is current, it is safe to change ->blocked.

But please avoid sigprocmask(), we have set_current_blocked().

Sure, I will use set_current_blocked().

While we are here, do you suggest I re-use current->saved_sigmask and hence use set_restore_sigmask() while resetting the sigmask?

I see saved_sigmask being used just before task sleeps and restored when task is scheduled back. So I dont see a case where using saved_sigmask in uprobes could conflict with its current usage.

However if you prefer we use a different sigmask to save and restore, I can make it part of the utask structure.

Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#123 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 26/26] uprobes: queue signals while thread is singlestepping.

Quoted 7 lines · Srikar Dronamraju

While we are here, do you suggest I re-use current->saved_sigmask and hence use set_restore_sigmask() while resetting the sigmask?

I see saved_sigmask being used just before task sleeps and restored when task is scheduled back. So I dont see a case where using saved_sigmask in uprobes could conflict with its current usage.

Yes, I think this is possible, and probably you do not even need set_restore_sigmask().

But. There are some problems with this approach too.

Firstly, even if you block all signals, there are other reasons for TIF_SIGPENDING which you can't control. For example, the task can be frozen or it can stop in UTASK_SSTEP state. Not good, if we have enough threads, this can lead to the "soft" deadlock. Say, a group stop can never finish because a thread sleeps in xol_wait_event() "forever".

Another problem is that it is not possible to block the "implicit" SIGKILL sent by exec/exit_group/etc. This mean the task can exit without sstep_complete/xol_free_insn_slot/etc. Mostly this is fine, we have free_uprobe_utask()->xol_free_insn_slot(). But in theory this can deadlock afaics. Suppose that the coredumping is in progress, the killed UTASK_SSTEP task hangs in exit_mm() waiting for other threads. If we have enough threads like this, we can deadlock with another thread sleeping in xol_wait_event().

This can be fixed, we can move free_uprobe_utask() from put_task_struct() to mm_release(). Btw, imho this makes sense anyway, why should a zombie thread abuse a slot?

However the first problem looks nasty, even if it is not very serious. And, otoh, it doesn't look right to block SIGKILL, the task can loop forever executing the xol insn (see below).

What do you think about the patch below? On top of 25/26, uncompiled, untested. With this patch the task simply refuses to react to TIF_SIGPENDING until sstep_complete().

This relies on the fact that do_notify_resume() calls uprobe_notify_resume() before do_signal(), I guess this is safe because we have other reasons for this order.

And, unless I missed something, this makes free_uprobe_utask()->xol_free_insn_slot() unnecessary.

HOWEVER! I simply do not know what should we do if the probed insn is something like asm("1:; jmp 1b;"). IIUC, in this sstep_complete() never returns true. The patch also adds the fatal_signal_pending() check to make this task killlable, but the problem is: whatever we do, I do not think it is correct to disable/delay the signals in this case. With any approach.

What do you think? Maybe we should simply disallow to probe such insns?

Once again, the change in sstep_complete() is "off-topic", this is another problem we should solve somehow.

Oleg.

kernel/signal.c +9 -0
@@ -2141,6 +2141,15 @@ int get_signal_to_deliver(siginfo_t *inf
 	struct signal_struct *signal = current->signal;
 	int signr;
 
+#ifdef CONFIG_UPROBES
+	if (unlikely(current->utask &&
+			current->utask->state != UTASK_RUNNING)) {
+		WARN_ON_ONCE(current->utask->state != UTASK_SSTEP);
+		clear_thread_flag(TIF_SIGPENDING);
+		return 0;
+	}
+#endif
+
 relock:
 	/*
 	 * We'll jump back here after any time we were stopped in TASK_STOPPED.
kernel/uprobes.c +3 -3
@@ -1331,7 +1331,8 @@ static bool sstep_complete(struct uprobe
 	 * If we have executed out of line, Instruction pointer
 	 * cannot be same as virtual address of XOL slot.
 	 */
-	if (vaddr == current->utask->xol_vaddr)
+	if (vaddr == current->utask->xol_vaddr &&
+			!__fatal_signal_pending(current))
 		return false;
 	post_xol(uprobe, regs);
 	return true;
@@ -1390,8 +1391,7 @@ void uprobe_notify_resume(struct pt_regs
 			utask->state = UTASK_RUNNING;
 			user_disable_single_step(current);
 			xol_free_insn_slot(current);
-
-			/* TODO Stop queueing signals. */
+			recalc_sigpending();
 		}
 	}
 	return;

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#124 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 26/26] uprobes: queue signals while thread is singlestepping.

Quoted 9 lines · Oleg Nesterov

HOWEVER! I simply do not know what should we do if the probed insn is something like asm("1:; jmp 1b;"). IIUC, in this sstep_complete() never returns true. The patch also adds the fatal_signal_pending() check to make this task killlable, but the problem is: whatever we do, I do not think it is correct to disable/delay the signals in this case. With any approach.

What do you think? Maybe we should simply disallow to probe such insns?

Or. Could you explain why we can't simply remove the
"if (vaddr == current->utask->xol_vaddr)" check from sstep_complete() ?

In some sense, imho this looks more correct for "rep" or jmp/call self. The task will trap again on the same (original) address, and handler_chain() will be called to notify the consumers.

But. I am really, really ignorant in this area, I am almost sure this is not that simple.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#125 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 26/26] uprobes: queue signals while thread is singlestepping.

Quoted 11 lines
Quoted 8 lines

HOWEVER! I simply do not know what should we do if the probed insn is something like asm("1:; jmp 1b;"). IIUC, in this sstep_complete() never returns true. The patch also adds the fatal_signal_pending() check to make this task killlable, but the problem is: whatever we do, I do not think it is correct to disable/delay the signals in this case. With any approach.

What do you think? Maybe we should simply disallow to probe such insns?

Or. Could you explain why we can't simply remove the
"if (vaddr == current->utask->xol_vaddr)" check from sstep_complete() ?

Yes, we could remove the check and rely on just the DIE_DEBUG to say that singlestep has occurred. This was mostly needed when we were not handling signals on singlestep.

Quoted 7 lines

In some sense, imho this looks more correct for "rep" or jmp/call self. The task will trap again on the same (original) address, and handler_chain() will be called to notify the consumers.

But. I am really, really ignorant in this area, I am almost sure this is not that simple.

Thats being modest.

Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#126 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 26/26] uprobes: queue signals while thread is singlestepping.

In reply to Oleg Nesterov
Quoted 12 lines · Oleg Nesterov
Quoted 7 lines · Srikar Dronamraju

While we are here, do you suggest I re-use current->saved_sigmask and hence use set_restore_sigmask() while resetting the sigmask?

I see saved_sigmask being used just before task sleeps and restored when task is scheduled back. So I dont see a case where using saved_sigmask in uprobes could conflict with its current usage.

Yes, I think this is possible, and probably you do not even need set_restore_sigmask().

Quoted 9 lines

But. There are some problems with this approach too.

Firstly, even if you block all signals, there are other reasons for TIF_SIGPENDING which you can't control. For example, the task can be frozen or it can stop in UTASK_SSTEP state. Not good, if we have enough threads, this can lead to the "soft" deadlock. Say, a group stop can never finish because a thread sleeps in xol_wait_event() "forever".

My idea was to block signals just across singlestep only. I.e we dont block signals while we contend for the slot.

Quoted 8 lines

Another problem is that it is not possible to block the "implicit" SIGKILL sent by exec/exit_group/etc. This mean the task can exit without sstep_complete/xol_free_insn_slot/etc. Mostly this is fine, we have free_uprobe_utask()->xol_free_insn_slot(). But in theory this can deadlock afaics. Suppose that the coredumping is in progress, the killed UTASK_SSTEP task hangs in exit_mm() waiting for other threads. If we have enough threads like this, we can deadlock with another thread sleeping in xol_wait_event().

Shouldnt the behaviour be the same as threads that did a select,sigsuspend?

Quoted 5 lines

This can be fixed, we can move free_uprobe_utask() from put_task_struct() to mm_release(). Btw, imho this makes sense anyway, why should a zombie thread abuse a slot?

Yes,, makes sense. Will make this change.

Quoted 10 lines

However the first problem looks nasty, even if it is not very serious. And, otoh, it doesn't look right to block SIGKILL, the task can loop forever executing the xol insn (see below).

What do you think about the patch below? On top of 25/26, uncompiled, untested. With this patch the task simply refuses to react to TIF_SIGPENDING until sstep_complete().

Your patch looks very simple and clean.
Will test this patch and revert.

Quoted 6 lines

This relies on the fact that do_notify_resume() calls uprobe_notify_resume() before do_signal(), I guess this is safe because we have other reasons for this order.

And, unless I missed something, this makes free_uprobe_utask()->xol_free_insn_slot() unnecessary.

What if a fatal (SIGKILL) signal was delivered only to that thread even before it singlestepped? or a fatal signal for a thread-group but more than one thread-group share the mm?

Quoted 11 lines

HOWEVER! I simply do not know what should we do if the probed insn is something like asm("1:; jmp 1b;"). IIUC, in this sstep_complete() never returns true. The patch also adds the fatal_signal_pending() check to make this task killlable, but the problem is: whatever we do, I do not think it is correct to disable/delay the signals in this case. With any approach.

What do you think? Maybe we should simply disallow to probe such insns?

Yes, we should disable such probes, but iam not sure we can detect such probes with the current instruction analyzer.

Masami, can we detect them (instructions that jump back to the same address as they are executing?)

Quoted 4 lines

Once again, the change in sstep_complete() is "off-topic", this is another problem we should solve somehow.

Agree.

you have already commented why blocking signals is a problem, but I still thought I will post the patch that I had to let you know what I was thinking before I saw your patch.

While task is processing a singlestep due to uprobes breakpoint hit, block signals from the time it enables singlestep to the time it disables singlestep.

---
 kernel/uprobes.c |   38 ++++++++++++++++++++++++++++++++------
 1 files changed, 32 insertions(+), 6 deletions(-)
kernel/uprobes.c +32 -6
index 5067979..bc3e178 100644
@@ -1366,6 +1366,26 @@ static bool sstep_complete(struct uprobe *uprobe, struct pt_regs *regs)
 }
 
 /*
+ * While we are handling breakpoint / singlestep, ensure that a
+ * SIGTRAP is not delivered to the task.
+ */
+static void __clear_trap_flag(void)
+{
+	sigdelset(&current->pending.signal, SIGTRAP);
+	sigdelset(&current->signal->shared_pending.signal, SIGTRAP);
+}
+
+static void clear_trap_flag(void)
+{
+	if (!test_and_clear_thread_flag(TIF_SIGPENDING))
+		return;
+
+	spin_lock_irq(&current->sighand->siglock);
+	__clear_trap_flag();
+	spin_unlock_irq(&current->sighand->siglock);
+}
+
+/*
  * uprobe_notify_resume gets called in task context just before returning
  * to userspace.
  *
@@ -1380,6 +1400,7 @@ void uprobe_notify_resume(struct pt_regs *regs)
 	struct mm_struct *mm;
 	struct uprobe *u = NULL;
 	unsigned long probept;
+	sigset_t masksigs;
 
 	utask = current->utask;
 	mm = current->mm;
@@ -1401,13 +1422,18 @@ void uprobe_notify_resume(struct pt_regs *regs)
 			if (!utask)
 				goto cleanup_ret;
 		}
-		/* TODO Start queueing signals. */
 		utask->active_uprobe = u;
 		handler_chain(u, regs);
 		utask->state = UTASK_SSTEP;
-		if (!pre_ssout(u, regs, probept))
+		if (!pre_ssout(u, regs, probept)) {
+			sigfillset(&masksigs);
+			sigdelsetmask(&masksigs,
+					sigmask(SIGKILL)|sigmask(SIGSTOP));
+			current->saved_sigmask = current->blocked;
+			set_current_blocked(&masksigs);
+			clear_trap_flag();
 			user_enable_single_step(current);
-		else
+		} else
 			/* Cannot Singlestep; re-execute the instruction. */
 			goto cleanup_ret;
 	} else if (utask->state == UTASK_SSTEP) {
@@ -1418,8 +1444,8 @@ void uprobe_notify_resume(struct pt_regs *regs)
 			utask->state = UTASK_RUNNING;
 			user_disable_single_step(current);
 			xol_free_insn_slot(current);
-
-			/* TODO Stop queueing signals. */
+			clear_trap_flag();
+			set_restore_sigmask();
 		}
 	}
 	return;
@@ -1433,7 +1459,7 @@ void uprobe_notify_resume(struct pt_regs *regs)
 		put_uprobe(u);
 		set_instruction_pointer(regs, probept);
 	} else
-		/*TODO Return SIGTRAP signal */
+		send_sig(SIGTRAP, current, 0);
 }
 
 /*

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#127 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 26/26] uprobes: queue signals while thread is singlestepping.

Quoted 16 lines · Srikar Dronamraju
Quoted 12 lines · Oleg Nesterov

Yes, I think this is possible, and probably you do not even need set_restore_sigmask().

But. There are some problems with this approach too.

Firstly, even if you block all signals, there are other reasons for TIF_SIGPENDING which you can't control. For example, the task can be frozen or it can stop in UTASK_SSTEP state. Not good, if we have enough threads, this can lead to the "soft" deadlock. Say, a group stop can never finish because a thread sleeps in xol_wait_event() "forever".

Quoted 2 lines

My idea was to block signals just across singlestep only. I.e we dont block signals while we contend for the slot.

Yes, yes, I see. But, once again, this can only protect from kill().

The task can stop even if you block all signals, another thread can initiate the group stop and set JOBCTL_STOP_PENDING + TIF_SIGPENDING. And note that it can stop _before_ it returns to user mode to step over the xol insn.

In theory the tasks like this can consume all slots, and if we have yet another thread waiting in xol_wait_event(), we deadlock. Although in this case SIGCONT helps, but this group stop can never finish.

Quoted 11 lines
Quoted 8 lines

Another problem is that it is not possible to block the "implicit" SIGKILL sent by exec/exit_group/etc. This mean the task can exit without sstep_complete/xol_free_insn_slot/etc. Mostly this is fine, we have free_uprobe_utask()->xol_free_insn_slot(). But in theory this can deadlock afaics. Suppose that the coredumping is in progress, the killed UTASK_SSTEP task hangs in exit_mm() waiting for other threads. If we have enough threads like this, we can deadlock with another thread sleeping in xol_wait_event().

Shouldnt the behaviour be the same as threads that did a select,sigsuspend?

Hmm. I don't understand... Could you explain?

Firstly, select/sigsuspend can't block SIGKILL, but this doesn't matter. My point was, the task can exit in UTASK_SSTEP state, and without xol_free_insn_slot(). And this (in theory) can lead to the "real" deadlock.

Quoted 6 lines
Quoted 6 lines

However the first problem looks nasty, even if it is not very serious. And, otoh, it doesn't look right to block SIGKILL, the task can loop forever executing the xol insn (see below).

Quoted 7 lines
Quoted 4 lines

What do you think about the patch below? On top of 25/26, uncompiled, untested. With this patch the task simply refuses to react to TIF_SIGPENDING until sstep_complete().

Your patch looks very simple and clean.
Will test this patch and revert.

Great. I'll think a bit more and send you the "final" version tomorrow. Assuming we can change sstep_complete() as we discussed, it doesn't need fatal_signal_pending().

HOWEVER. There is yet another problem. Another thread can, say, unmap() xol_vma. In this case we should ensure that the task can't fault in an endless loop.

Quoted 4 lines
Quoted 2 lines

And, unless I missed something, this makes free_uprobe_utask()->xol_free_insn_slot() unnecessary.

What if a fatal (SIGKILL) signal was delivered only to that thread

this is not possible, in this case all threads are killed. But,

Quoted 2 lines

or a fatal signal for a thread-group but more
than one thread-group share the mm?

Yes, this is possible.

Sorry for confusion. Yes, if we have the fatal_signal_pending() check in sstep_complete(), then we do need free_uprobe_utask()->xol_free_insn_slot(). But this check was added only to illustrate another problem with the self-repeating insns.

And. With "HOWEVER" above, we probably need this xol_free anyway.

Quoted 7 lines

you have already commented why blocking signals is a problem, but I still thought I will post the patch that I had to let you know what I was thinking before I saw your patch.

While task is processing a singlestep due to uprobes breakpoint hit, block signals from the time it enables singlestep to the time it disables singlestep.

OK, it is too late for me today, I'll take a look tomorrow.

This approach has some advantages too, perhaps we should make something "in between".

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#128 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 26/26] uprobes: queue signals while thread is singlestepping.

Quoted 12 lines

Yes, yes, I see. But, once again, this can only protect from kill().

The task can stop even if you block all signals, another thread can initiate the group stop and set JOBCTL_STOP_PENDING + TIF_SIGPENDING. And note that it can stop _before_ it returns to user mode to step over the xol insn.

In theory the tasks like this can consume all slots, and if we have yet another thread waiting in xol_wait_event(), we deadlock. Although in this case SIGCONT helps, but this group stop can never finish.

Okay.

Quoted 12 lines
Quoted 11 lines
Quoted 8 lines

Another problem is that it is not possible to block the "implicit" SIGKILL sent by exec/exit_group/etc. This mean the task can exit without sstep_complete/xol_free_insn_slot/etc. Mostly this is fine, we have free_uprobe_utask()->xol_free_insn_slot(). But in theory this can deadlock afaics. Suppose that the coredumping is in progress, the killed UTASK_SSTEP task hangs in exit_mm() waiting for other threads. If we have enough threads like this, we can deadlock with another thread sleeping in xol_wait_event().

Shouldnt the behaviour be the same as threads that did a select,sigsuspend?

Quoted 6 lines

Hmm. I don't understand... Could you explain?

Firstly, select/sigsuspend can't block SIGKILL, but this doesn't matter. My point was, the task can exit in UTASK_SSTEP state, and without xol_free_insn_slot(). And this (in theory) can lead to the "real" deadlock.

I think we should be okay if the test exits in UTASK_SSTEP state. All I thought we needed to do was block it from doing anything except exit or singlestep. Our exit hook should cleanup any references that we hold.

Quoted 12 lines
Quoted 12 lines
Quoted 11 lines

However the first problem looks nasty, even if it is not very serious. And, otoh, it doesn't look right to block SIGKILL, the task can loop forever executing the xol insn (see below).

What do you think about the patch below? On top of 25/26, uncompiled, untested. With this patch the task simply refuses to react to TIF_SIGPENDING until sstep_complete().

Quoted 6 lines
Quoted 2 lines

Your patch looks very simple and clean.
Will test this patch and revert.

Great. I'll think a bit more and send you the "final" version tomorrow. Assuming we can change sstep_complete() as we discussed, it doesn't need fatal_signal_pending().

Okay.

Quoted 5 lines

HOWEVER. There is yet another problem. Another thread can, say, unmap() xol_vma. In this case we should ensure that the task can't fault in an endless loop.

Hmm should we add a check in unmap() to see if the vma that we are trying to unmap is the xol_vma and if so return?
Our assumption has been that once an xol_vma has been created, it should be around till the process gets killed.

Quoted 23 lines
Quoted 4 lines
Quoted 2 lines

And, unless I missed something, this makes free_uprobe_utask()->xol_free_insn_slot() unnecessary.

What if a fatal (SIGKILL) signal was delivered only to that thread

this is not possible, in this case all threads are killed. But,

Quoted 2 lines

or a fatal signal for a thread-group but more
than one thread-group share the mm?

Yes, this is possible.

Sorry for confusion. Yes, if we have the fatal_signal_pending() check in sstep_complete(), then we do need free_uprobe_utask()->xol_free_insn_slot(). But this check was added only to illustrate another problem with the self-repeating insns.

And. With "HOWEVER" above, we probably need this xol_free anyway.

Quoted 4 lines

you have already commented why blocking signals is a problem, but I still thought I will post the patch that I had to let you know what I was thinking before I saw your patch.

Quoted 9 lines
Quoted 3 lines

While task is processing a singlestep due to uprobes breakpoint hit, block signals from the time it enables singlestep to the time it disables singlestep.

OK, it is too late for me today, I'll take a look tomorrow.

This approach has some advantages too, perhaps we should make something "in between".

Okay.

Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#129 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 26/26] uprobes: queue signals while thread is singlestepping.

Quoted 2 lines · Srikar Dronamraju

I think we should be okay if the test exits in UTASK_SSTEP state.

Yes, and afaics we can't avoid this case, at least currently.

But we should move free_uprobe_utask() to mm_release(), or somewhere else before mm->core_state check in exit_mm().

My main concern is stop/freeze in UTASK_SSTEP state. If nothing else, debugger can attach to the stopped task and disable the stepping. Or SIGKILL, it should work in this case.

Quoted 5 lines
Quoted 3 lines

Great. I'll think a bit more and send you the "final" version tomorrow. Assuming we can change sstep_complete() as we discussed, it doesn't need fatal_signal_pending().

Okay.

Sorry. I was busy today. Tomorrow ;)

Quoted 6 lines
Quoted 3 lines

HOWEVER. There is yet another problem. Another thread can, say, unmap() xol_vma. In this case we should ensure that the task can't fault in an endless loop.

Hmm should we add a check in unmap() to see if the vma that we are trying to unmap is the xol_vma and if so return?

Oh, I am not sure. You know, I _think_ that perhaps we should do something diferent in the long term. In particular, this xol page should not have vma at all. This way we shouldn't worry about unmap/remap/mprotect. But even if this is possible (I am not really sure), I do not think we should do this right now.

Quoted 2 lines

Our assumption has been that once an xol_vma has been created, it should be around till the process gets killed.

Yes, I see. But afaics this assumption is currently wrong. This means that we should ensure the evil application can't exploit this fact.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#130 lore raw

Re: [PATCH v5 3.1.0-rc4-tip 26/26] uprobes: queue signals while thread is singlestepping.

In reply to Srikar Dronamraju
Quoted 15 lines · Srikar Dronamraju
kernel/uprobes.c +8 -0
@@ -1366,6 +1366,26 @@ static bool sstep_complete(struct uprobe *uprobe, struct pt_regs *regs)
 }
 
 /*
+ * While we are handling breakpoint / singlestep, ensure that a
+ * SIGTRAP is not delivered to the task.
+ */
+static void __clear_trap_flag(void)
+{
+	sigdelset(&current->pending.signal, SIGTRAP);
+	sigdelset(&current->signal->shared_pending.signal, SIGTRAP);
+}

kernel/uprobes.c static bool sstep_complete(struct uprobe *uprobe, struct pt_regs *regs) · line 1386

Quoted 10 lines
kernel/uprobes.c +10 -0
+
+static void clear_trap_flag(void)
+{
+	if (!test_and_clear_thread_flag(TIF_SIGPENDING))
+		return;
+
+	spin_lock_irq(&current->sighand->siglock);
+	__clear_trap_flag();
+	spin_unlock_irq(&current->sighand->siglock);
+}

And this is called before and after the step.

Confused... For what? What makes SIGTRAP special? Where does this signal come from? If you meant do_debug() this seems impossible, uprobe_exception_notify(DIE_DEBUG) returns NOTIFY_STOP.

I certainly missed something.

Quoted 5 lines
kernel/uprobes.c +0 -1
@@ -1401,13 +1422,18 @@ void uprobe_notify_resume(struct pt_regs *regs)
 			if (!utask)
 				goto cleanup_ret;
 		}
-		/* TODO Start queueing signals. */

kernel/uprobes.c void uprobe_notify_resume(struct pt_regs *regs) · line 1433

Quoted 10 lines
kernel/uprobes.c +6 -1
 		utask->active_uprobe = u;
 		handler_chain(u, regs);
 		utask->state = UTASK_SSTEP;
-		if (!pre_ssout(u, regs, probept))
+		if (!pre_ssout(u, regs, probept)) {
+			sigfillset(&masksigs);
+			sigdelsetmask(&masksigs,
+					sigmask(SIGKILL)|sigmask(SIGSTOP));
+			current->saved_sigmask = current->blocked;
+			set_current_blocked(&masksigs);

OK, we already discussed the problems with this approach.

kernel/uprobes.c void uprobe_notify_resume(struct pt_regs *regs) · line 1434

Quoted 1 line
kernel/uprobes.c +1 -0
+			clear_trap_flag();

In any case unneeded, we already blocked SIGTRAP.

kernel/uprobes.c void uprobe_notify_resume(struct pt_regs *regs) · line 1447

Quoted 7 lines
kernel/uprobes.c +1 -2
@@ -1418,8 +1444,8 @@ void uprobe_notify_resume(struct pt_regs *regs)
 			utask->state = UTASK_RUNNING;
 			user_disable_single_step(current);
 			xol_free_insn_slot(current);
-
-			/* TODO Stop queueing signals. */
+			clear_trap_flag();

This is what I can't understand.

kernel/uprobes.c void uprobe_notify_resume(struct pt_regs *regs) · line 1448

Quoted 1 line
kernel/uprobes.c +1 -0
+			set_restore_sigmask();

No, this is not right. If we have a pending signal, the signal handler will run with the almost-all-blocked mask we set before.

And this is overkill anyway, you could simply do
set_current_blocked(&current->saved_sigmask).

->saved_sigmask is only used when we return from syscall, so uprobes can (ab)use it safely.

kernel/uprobes.c void uprobe_notify_resume(struct pt_regs *regs) · line 1462

Quoted 6 lines
kernel/uprobes.c +1 -1
@@ -1433,7 +1459,7 @@ void uprobe_notify_resume(struct pt_regs *regs)
 		put_uprobe(u);
 		set_instruction_pointer(regs, probept);
 	} else
-		/*TODO Return SIGTRAP signal */
+		send_sig(SIGTRAP, current, 0);

This change looks "offtopic" to the problems we are discussing.

Or I missed something and this is connected to the clear_trap_flag() somehow?

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#131 lore raw
In reply to Srikar Dronamraju
Quoted 1 line · Srikar Dronamraju

- Uses i_mutex instead of uprobes_mutex.

What for exactly? I'm pretty strict against introducing even more uses for i_mutex, it's already way to overloaded with different meanings.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#132 lore raw
Quoted 7 lines · Christoph Hellwig
Quoted 1 line · Srikar Dronamraju

- Uses i_mutex instead of uprobes_mutex.

What for exactly? I'm pretty strict against introducing even more uses for i_mutex, it's already way to overloaded with different meanings.

There could be multiple simultaneous requests for adding/removing a probe for the same location i.e same inode + same offset. These requests will have to be serialized.

To serialize this we had used uprobes specific mutex (uprobes_mutex) in the last patchset. However using uprobes_mutex will mean we will be serializing requests for unrelated files. I.e if we get a request to probe libpthread while we are inserting/deleting a probe on libc, then we used to make the libpthread request wait unnecessarily. This also means that I dont need to introduce yet another lock.

After using i_mutex, these two requests can run in parallel.

I had proposed this while answering one of the comments in the last patchset. Since I didnt hear any complaints, I went ahead and implemented this.

I could use any other inode/file/mapping based sleepable lock that is of higher order than mmap_sem. Can you please let me know if we have alternatives.

Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#133 lore raw
Quoted 3 lines · Srikar Dronamraju

I could use any other inode/file/mapping based sleepable lock that is of higher order than mmap_sem. Can you please let me know if we have alternatives.

Please do not overload unrelated locks for this, but add a specific one.

There's two options:

(a) add it to the inode (conditionally)
(b) use global, hashed locks

I think (b) is good enough as adding/removing probes isn't exactly the most critical fast path.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#134 lore raw
Quoted 7 lines · Christoph Hellwig
Quoted 3 lines · Srikar Dronamraju

I could use any other inode/file/mapping based sleepable lock that is of higher order than mmap_sem. Can you please let me know if we have alternatives.

Please do not overload unrelated locks for this, but add a specific one.

Quoted 8 lines

There's two options:

(a) add it to the inode (conditionally)
(b) use global, hashed locks

I think (b) is good enough as adding/removing probes isn't exactly the most critical fast path.

Agree, I will replace the i_mutex with a uprobes specific hash locks. I will make this change as part of next patchset.

Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#135 lore raw

PATCH 1/1 (Was: Uprobes patchset with perf probe support)

In reply to Srikar Dronamraju

Hello.

I was trying to make the signal patches we discussed, but I have noticed other problems.

Please see the suggested initial fixes, more to come. Of course there were not tested ;) Please review.

And. It it not that I suggest to add this series, just it is much simpler to me to write the patch with the changelog to explain what I mean.

IOW, if you agree with these fixes, please incorporate them into the next version. Feel free to redo.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#136 lore raw

PATCH 1/1 uprobes: write_opcode: the new page needs PG_uptodate

write_opcode()->__replace_page() installs the new anonymous page, this new_page is PageSwapBacked() and it can be swapped out.

However it forgets to do SetPageUptodate(), fix write_opcode().

For example, this is needed if do_swap_page() finds that orginial page in the the swap cache (and doesn't try to read it back), in this case it returns VM_FAULT_SIGBUS.

---
 kernel/uprobes.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)
kernel/uprobes.c +2 -0
index 3928bcc..52b20c8 100644
@@ -200,6 +200,8 @@ static int write_opcode(struct task_struct *tsk, struct uprobe * uprobe,
 		goto put_out;
 	}
 
+	__SetPageUptodate(new_page);
+
 	/*
 	 * lock page will serialize against do_wp_page()'s
 	 * PageAnon() handling
Signature · 9 lines
1.5.5.1


--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#137 lore raw

Re: [PATCH 1/X] uprobes: write_opcode: the new page needs PG_uptodate

Quoted 17 lines · Oleg Nesterov

write_opcode()->__replace_page() installs the new anonymous page, this new_page is PageSwapBacked() and it can be swapped out.

However it forgets to do SetPageUptodate(), fix write_opcode().

For example, this is needed if do_swap_page() finds that orginial page in the the swap cache (and doesn't try to read it back), in this case it returns VM_FAULT_SIGBUS.

---
 kernel/uprobes.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)
kernel/uprobes.c +0 -0
index 3928bcc..52b20c8 100644

kernel/uprobes.c static int write_opcode(struct task_struct *tsk, struct uprobe * uprobe, · line 204

Quoted 5 lines
kernel/uprobes.c +2 -0
@@ -200,6 +200,8 @@ static int write_opcode(struct task_struct *tsk, struct uprobe * uprobe,
 		goto put_out;
 	}
 
+	__SetPageUptodate(new_page);
+

Agree.

Quoted 3 lines
kernel/uprobes.c +0 -0
 	/*
 	 * lock page will serialize against do_wp_page()'s
 	 * PageAnon() handling
Signature · 2 lines
Thanks and Regards
Srikar
maintainer
#138 lore raw

PATCH 1/1 uprobes: write_opcode() needs put_page(new_page) unconditionally

In reply to Oleg Nesterov

Every write_opcode()->__replace_page() leaks the new page on success.

We have the reference after alloc_page_vma(), then __replace_page() does another get_page() for the new mapping, we need put_page(new_page) in any case.

Alternatively we could remove __replace_page()->get_page() but it is better to change write_opcode(). This way it is simpler to unify the code with ksm.c:replace_page() and we can simplify the error handling in write_opcode(), the patch simply adds a single page_cache_release() under "unlock_out" label.

---
 kernel/uprobes.c |   15 +++++----------
 1 files changed, 5 insertions(+), 10 deletions(-)
kernel/uprobes.c +5 -10
index 52b20c8..fd9c8e3 100644
@@ -193,15 +193,12 @@ static int write_opcode(struct task_struct *tsk, struct uprobe * uprobe,
 	if (vaddr != (unsigned long) addr)
 		goto put_out;
 
-	/* Allocate a page */
+	ret = -ENOMEM;
 	new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr);
-	if (!new_page) {
-		ret = -ENOMEM;
+	if (!new_page)
 		goto put_out;
-	}
 
 	__SetPageUptodate(new_page);
-
 	/*
 	 * lock page will serialize against do_wp_page()'s
 	 * PageAnon() handling
@@ -220,18 +217,16 @@ static int write_opcode(struct task_struct *tsk, struct uprobe * uprobe,
 	kunmap_atomic(vaddr_old);
 
 	ret = anon_vma_prepare(vma);
-	if (ret) {
-		page_cache_release(new_page);
+	if (ret)
 		goto unlock_out;
-	}
 
 	lock_page(new_page);
 	ret = __replace_page(vma, old_page, new_page);
 	unlock_page(new_page);
-	if (ret != 0)
-		page_cache_release(new_page);
+
 unlock_out:
 	unlock_page(old_page);
+	page_cache_release(new_page);
 
 put_out:
 	put_page(old_page); /* we did a get_page in the beginning */
Signature · 9 lines
1.5.5.1


--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#139 lore raw

Re: [PATCH 2/X] uprobes: write_opcode() needs put_page(new_page) unconditionally

Quoted 11 lines

Every write_opcode()->__replace_page() leaks the new page on success.

We have the reference after alloc_page_vma(), then __replace_page() does another get_page() for the new mapping, we need put_page(new_page) in any case.

Alternatively we could remove __replace_page()->get_page() but it is better to change write_opcode(). This way it is simpler to unify the code with ksm.c:replace_page() and we can simplify the error handling in write_opcode(), the patch simply adds a single page_cache_release() under "unlock_out" label.

I have folded this change and your other suggested changes into my patches.

Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#140 lore raw

PATCH 1/1 uprobes: xol_add_vma: fix ->uprobes_xol_area initialization

In reply to Oleg Nesterov

xol_add_vma() can race with another thread which sets ->uprobes_xol_area, in this case we can't rely on per-thread task_lock() and we should unmap xol_vma.

Move the setting of mm->uprobes_xol_area into xol_add_vma(), it has to take mmap_sem for writing anyway, this also simplifies the code.

Change xol_add_vma() to do do_munmap() if it fails after do_mmap_pgoff().

---
 kernel/uprobes.c |   34 ++++++++++++++++------------------
 1 files changed, 16 insertions(+), 18 deletions(-)
kernel/uprobes.c +16 -18
index fd9c8e3..6fe2b20 100644
@@ -1049,18 +1049,18 @@ static int xol_add_vma(struct uprobes_xol_area *area)
 	struct vm_area_struct *vma;
 	struct mm_struct *mm;
 	unsigned long addr;
-	int ret = -ENOMEM;
+	int ret;
 
 	mm = get_task_mm(current);
 	if (!mm)
 		return -ESRCH;
 
 	down_write(&mm->mmap_sem);
-	if (mm->uprobes_xol_area) {
-		ret = -EALREADY;
+	ret = -EALREADY;
+	if (mm->uprobes_xol_area)
 		goto fail;
-	}
 
+	ret = -ENOMEM;
 	/*
 	 * Find the end of the top mapping and skip a page.
 	 * If there is no space for PAGE_SIZE above
@@ -1078,15 +1078,19 @@ static int xol_add_vma(struct uprobes_xol_area *area)
 
 	if (addr & ~PAGE_MASK)
 		goto fail;
-	vma = find_vma(mm, addr);
 
+	vma = find_vma(mm, addr);
 	/* Don't expand vma on mremap(). */
 	vma->vm_flags |= VM_DONTEXPAND | VM_DONTCOPY;
 	area->vaddr = vma->vm_start;
 	if (get_user_pages(current, mm, area->vaddr, 1, 1, 1, &area->page,
-				&vma) > 0)
-		ret = 0;
+				&vma) != 1) {
+		do_munmap(mm, addr, PAGE_SIZE);
+		goto fail;
+	}
 
+	mm->uprobes_xol_area = area;
+	ret = 0;
 fail:
 	up_write(&mm->mmap_sem);
 	mmput(mm);
@@ -1102,7 +1106,7 @@ fail:
  */
 static struct uprobes_xol_area *xol_alloc_area(void)
 {
-	struct uprobes_xol_area *area = NULL;
+	struct uprobes_xol_area *area;
 
 	area = kzalloc(sizeof(*area), GFP_KERNEL);
 	if (unlikely(!area))
@@ -1110,22 +1114,16 @@ static struct uprobes_xol_area *xol_alloc_area(void)
 
 	area->bitmap = kzalloc(BITS_TO_LONGS(UINSNS_PER_PAGE) * sizeof(long),
 								GFP_KERNEL);
-
 	if (!area->bitmap)
 		goto fail;
 
 	init_waitqueue_head(&area->wq);
 	spin_lock_init(&area->slot_lock);
-	if (!xol_add_vma(area) && !current->mm->uprobes_xol_area) {
-		task_lock(current);
-		if (!current->mm->uprobes_xol_area) {
-			current->mm->uprobes_xol_area = area;
-			task_unlock(current);
-			return area;
-		}
-		task_unlock(current);
-	}
 
+	if (xol_add_vma(area))
+		goto fail;
+
+	return area;
 fail:
 	kfree(area->bitmap);
 	kfree(area);
Signature · 9 lines
1.5.5.1


--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#141 lore raw

PATCH 1/1 uprobes: xol_add_vma: misc cleanups

In reply to Oleg Nesterov

1. get_task_mm(current)/mmput is not needed, we can use ->mm directly.

It can't be NULL or use_mm'ed(), otherwise we are buggy anyway.

2. use IS_ERR_VALUE() after do_mmap_pgoff().

3. No need to read vma->vm_start, it must be equal to addr returned by do_mmap_pgoff().

4. No need to pass vmas => &vma to get_user_pages().

---
 kernel/uprobes.c |   13 +++++--------
 1 files changed, 5 insertions(+), 8 deletions(-)
kernel/uprobes.c +5 -8
index 6fe2b20..5c2554c 100644
@@ -1051,9 +1051,7 @@ static int xol_add_vma(struct uprobes_xol_area *area)
 	unsigned long addr;
 	int ret;
 
-	mm = get_task_mm(current);
-	if (!mm)
-		return -ESRCH;
+	mm = current->mm;
 
 	down_write(&mm->mmap_sem);
 	ret = -EALREADY;
@@ -1076,24 +1074,23 @@ static int xol_add_vma(struct uprobes_xol_area *area)
 	addr = do_mmap_pgoff(NULL, addr, PAGE_SIZE, PROT_EXEC, MAP_PRIVATE, 0);
 	revert_creds(curr_cred);
 
-	if (addr & ~PAGE_MASK)
+	if (IS_ERR_VALUE(addr))
 		goto fail;
 
 	vma = find_vma(mm, addr);
 	/* Don't expand vma on mremap(). */
 	vma->vm_flags |= VM_DONTEXPAND | VM_DONTCOPY;
-	area->vaddr = vma->vm_start;
-	if (get_user_pages(current, mm, area->vaddr, 1, 1, 1, &area->page,
-				&vma) != 1) {
+	if (get_user_pages(current, mm, addr, 1, 1, 1,
+					&area->page, NULL) != 1) {
 		do_munmap(mm, addr, PAGE_SIZE);
 		goto fail;
 	}
 
+	area->vaddr = addr;
 	mm->uprobes_xol_area = area;
 	ret = 0;
 fail:
 	up_write(&mm->mmap_sem);
-	mmput(mm);
 	return ret;
 }
 
Signature · 9 lines
1.5.5.1


--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#142 lore raw

PATCH 1/1 uprobes: xol_alloc_area() needs memory barriers

In reply to Oleg Nesterov

If xol_get_insn_slot() or xol_alloc_area() races with another thread doing xol_add_vma() it is not safe to dereference ->uprobes_xol_area.

Add the necessary wmb/read_barrier_depends pair, this ensures that xol_get_insn_slot() always sees the properly initialized memory.

Other users of ->uprobes_xol_area look fine, they can't race with xol_add_vma() this way. xol_free_insn_slot() checks utask->xol_vaddr, and free_uprobes_xol_area() is calles by mmput().

Except: valid_vma() is racy but it should not use ->uprobes_xol_area as we discussed.

---
 kernel/uprobes.c |   15 ++++++++++++---
 1 files changed, 12 insertions(+), 3 deletions(-)
kernel/uprobes.c +12 -3
index 5c2554c..b59af3b 100644
@@ -1087,6 +1087,7 @@ static int xol_add_vma(struct uprobes_xol_area *area)
 	}
 
 	area->vaddr = addr;
+	smp_wmb();	/* pairs with get_uprobes_xol_area() */
 	mm->uprobes_xol_area = area;
 	ret = 0;
 fail:
@@ -1094,6 +1095,14 @@ fail:
 	return ret;
 }
 
+static inline
+struct uprobes_xol_area *get_uprobes_xol_area(struct mm_struct *mm)
+{
+	struct uprobes_xol_area *area = mm->uprobes_xol_area;
+	smp_read_barrier_depends();	/* pairs with wmb in xol_add_vma() */
+	return area;
+}
+
 /*
  * xol_alloc_area - Allocate process's uprobes_xol_area.
  * This area will be used for storing instructions for execution out of
@@ -1124,7 +1133,7 @@ static struct uprobes_xol_area *xol_alloc_area(void)
 fail:
 	kfree(area->bitmap);
 	kfree(area);
-	return current->mm->uprobes_xol_area;
+	return get_uprobes_xol_area(current->mm);
 }
 
 /*
@@ -1183,17 +1192,17 @@ static unsigned long xol_take_insn_slot(struct uprobes_xol_area *area)
 static unsigned long xol_get_insn_slot(struct uprobe *uprobe,
 					unsigned long slot_addr)
 {
-	struct uprobes_xol_area *area = current->mm->uprobes_xol_area;
+	struct uprobes_xol_area *area;
 	unsigned long offset;
 	void *vaddr;
 
+	area = get_uprobes_xol_area(current->mm);
 	if (!area) {
 		area = xol_alloc_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 · 9 lines
1.5.5.1


--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#143 lore raw

PATCH 1/1 uprobes: reimplement xol_add_vma() via install_special_mapping()

In reply to Oleg Nesterov

I apologize in advance if this was already discussed, but I just can't understand why xol_add_vma() does not use install_special_mapping(). Unless I missed something this should work and this has the following advantages:

- we can avoid override_creds() hacks, install_special_mapping() fools security_file_mmap() passing prot/flags = 0

- no need to play with vma after do_mmap_pgoff()

- no need for get_user_pages(FOLL_WRITE/FOLL_FORCE) hack

- no need for do_munmap() if get_user_pages() fails

- this protects us from mprotect(READ/WRITE)

- this protects from MADV_DONTNEED, the page will be correctly re-instantiated from area->page

- this makes xol_vma more "cheap", swapper can't see this page and we avoid the meaningless add_to_swap/pageout.

Note that, before this patch, area->page can't be removed from the swap cache anyway (we have the reference). And it must not, uprobes modifies this page directly.

Note on vm_flags:

- we do not use VM_DONTEXPAND, install_special_mapping() adds it

- VM_IO protects from MADV_DOFORK

- I am not sure, may be some archs need VM_READ along with EXEC?

Anything else I have missed?
---

 kernel/uprobes.c |   42 +++++++++++++++++++-----------------------
 1 files changed, 19 insertions(+), 23 deletions(-)
kernel/uprobes.c +19 -23
index b59af3b..038f21c 100644
@@ -1045,53 +1045,49 @@ void munmap_uprobe(struct vm_area_struct *vma)
 /* Slot allocation for XOL */
 static int xol_add_vma(struct uprobes_xol_area *area)
 {
-	const struct cred *curr_cred;
 	struct vm_area_struct *vma;
 	struct mm_struct *mm;
-	unsigned long addr;
+	unsigned long addr_hint;
 	int ret;
 
+	area->page = alloc_page(GFP_HIGHUSER);
+	if (!area->page)
+		return -ENOMEM;
+
 	mm = current->mm;
 
 	down_write(&mm->mmap_sem);
 	ret = -EALREADY;
 	if (mm->uprobes_xol_area)
 		goto fail;
-
-	ret = -ENOMEM;
 	/*
 	 * Find the end of the top mapping and skip a page.
-	 * If there is no space for PAGE_SIZE above
-	 * that, mmap will ignore our address hint.
-	 *
-	 * override credentials otherwise anonymous memory might
-	 * not be granted execute permission when the selinux
-	 * security hooks have their way.
+	 * If there is no space for PAGE_SIZE above that,
+	 * this hint will be ignored.
 	 */
 	vma = rb_entry(rb_last(&mm->mm_rb), struct vm_area_struct, vm_rb);
-	addr = vma->vm_end + PAGE_SIZE;
-	curr_cred = override_creds(&init_cred);
-	addr = do_mmap_pgoff(NULL, addr, PAGE_SIZE, PROT_EXEC, MAP_PRIVATE, 0);
-	revert_creds(curr_cred);
+	addr_hint = vma->vm_end + PAGE_SIZE;
 
-	if (IS_ERR_VALUE(addr))
+	area->vaddr = get_unmapped_area(NULL, addr_hint, PAGE_SIZE, 0, 0);
+	if (IS_ERR_VALUE(area->vaddr)) {
+		ret = area->vaddr;
 		goto fail;
+	}
 
-	vma = find_vma(mm, addr);
-	/* Don't expand vma on mremap(). */
-	vma->vm_flags |= VM_DONTEXPAND | VM_DONTCOPY;
-	if (get_user_pages(current, mm, addr, 1, 1, 1,
-					&area->page, NULL) != 1) {
-		do_munmap(mm, addr, PAGE_SIZE);
+	ret = install_special_mapping(mm, area->vaddr, PAGE_SIZE,
+					VM_EXEC|VM_MAYEXEC | VM_DONTCOPY|VM_IO,
+					&area->page);
+	if (ret)
 		goto fail;
-	}
 
-	area->vaddr = addr;
 	smp_wmb();	/* pairs with get_uprobes_xol_area() */
 	mm->uprobes_xol_area = area;
 	ret = 0;
 fail:
 	up_write(&mm->mmap_sem);
+	if (ret)
+		__free_page(area->page);
+
 	return ret;
 }
 

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#144 lore raw

Re: [PATCH 6/X] uprobes: reimplement xol_add_vma() via install_special_mapping()

Quoted 4 lines

I apologize in advance if this was already discussed, but I just can't understand why xol_add_vma() does not use install_special_mapping(). Unless I missed something this should work and this has the following advantages:

The override_creds was based on what Stephen Smalley suggested
https://lkml.org/lkml/2011/4/20/224

At that time Peter had suggested install_special_mapping(). However the consensus was to go with Stephen's suggestion of override_creds.

Quoted 15 lines

- we can avoid override_creds() hacks, install_special_mapping() fools security_file_mmap() passing prot/flags = 0

- no need to play with vma after do_mmap_pgoff()

- no need for get_user_pages(FOLL_WRITE/FOLL_FORCE) hack

- no need for do_munmap() if get_user_pages() fails

- this protects us from mprotect(READ/WRITE)

- this protects from MADV_DONTNEED, the page will be correctly re-instantiated from area->page

Quoted 6 lines

- this makes xol_vma more "cheap", swapper can't see this page and we avoid the meaningless add_to_swap/pageout.

Note that, before this patch, area->page can't be removed from the swap cache anyway (we have the reference). And it must not, uprobes modifies this page directly.

Stephan, Eric,

Would you agree with Oleg's observation that we would be better off using install_special_mapping rather than using override_creds.

To give you some more information about the problem.

Uprobes will be a in-kernel debugging facility that provides singlestepping out of line. To achieve this, it will create a per-mm vma which is not mapped to any file. However this vma has to be executable.

Slots are made in this executable vma, and one slot can be used to single step a original instruction.

This executable vma that we are creating is not for any particular binary but would have to be created dynamically as and when an application is debugged. For example, if we were to debug malloc call in libc, we would end up adding xol vma to all the live processes in the system.

Since selinux wasnt happy to have an anonymous vma attached, we would create a pseudo file using shmem_file_setup. However after comments from Peter and Stephan's suggestions we started using override_creds. Peter and Oleg suggest that we use install_special_mapping.

Are you okay with using install_special_mapping instead of override_creds()?

Signature · 2 lines
Thanks and Regards
Srikar
Quoted 83 lines

Note on vm_flags:

- we do not use VM_DONTEXPAND, install_special_mapping() adds it

- VM_IO protects from MADV_DOFORK

- I am not sure, may be some archs need VM_READ along with EXEC?

Anything else I have missed?
---

 kernel/uprobes.c |   42 +++++++++++++++++++-----------------------
 1 files changed, 19 insertions(+), 23 deletions(-)
kernel/uprobes.c +16 -23
index b59af3b..038f21c 100644
@@ -1045,53 +1045,49 @@ void munmap_uprobe(struct vm_area_struct *vma)
 /* Slot allocation for XOL */
 static int xol_add_vma(struct uprobes_xol_area *area)
 {
-	const struct cred *curr_cred;
 	struct vm_area_struct *vma;
 	struct mm_struct *mm;
-	unsigned long addr;
+	unsigned long addr_hint;
 	int ret;
 
+	area->page = alloc_page(GFP_HIGHUSER);
+	if (!area->page)
+		return -ENOMEM;
+
 	mm = current->mm;
 
 	down_write(&mm->mmap_sem);
 	ret = -EALREADY;
 	if (mm->uprobes_xol_area)
 		goto fail;
-
-	ret = -ENOMEM;
 	/*
 	 * Find the end of the top mapping and skip a page.
-	 * If there is no space for PAGE_SIZE above
-	 * that, mmap will ignore our address hint.
-	 *
-	 * override credentials otherwise anonymous memory might
-	 * not be granted execute permission when the selinux
-	 * security hooks have their way.
+	 * If there is no space for PAGE_SIZE above that,
+	 * this hint will be ignored.
 	 */
 	vma = rb_entry(rb_last(&mm->mm_rb), struct vm_area_struct, vm_rb);
-	addr = vma->vm_end + PAGE_SIZE;
-	curr_cred = override_creds(&init_cred);
-	addr = do_mmap_pgoff(NULL, addr, PAGE_SIZE, PROT_EXEC, MAP_PRIVATE, 0);
-	revert_creds(curr_cred);
+	addr_hint = vma->vm_end + PAGE_SIZE;
 
-	if (IS_ERR_VALUE(addr))
+	area->vaddr = get_unmapped_area(NULL, addr_hint, PAGE_SIZE, 0, 0);
+	if (IS_ERR_VALUE(area->vaddr)) {
+		ret = area->vaddr;
 		goto fail;
+	}
 
-	vma = find_vma(mm, addr);
-	/* Don't expand vma on mremap(). */
-	vma->vm_flags |= VM_DONTEXPAND | VM_DONTCOPY;
-	if (get_user_pages(current, mm, addr, 1, 1, 1,
-					&area->page, NULL) != 1) {
-		do_munmap(mm, addr, PAGE_SIZE);
+	ret = install_special_mapping(mm, area->vaddr, PAGE_SIZE,
+					VM_EXEC|VM_MAYEXEC | VM_DONTCOPY|VM_IO,
+					&area->page);
+	if (ret)
 		goto fail;
-	}
 
-	area->vaddr = addr;
 	smp_wmb();	/* pairs with get_uprobes_xol_area() */

kernel/uprobes.c void munmap_uprobe(struct vm_area_struct *vma) · line 1093

Quoted 10 lines
kernel/uprobes.c +3 -0
 	mm->uprobes_xol_area = area;
 	ret = 0;
 fail:
 	up_write(&mm->mmap_sem);
+	if (ret)
+		__free_page(area->page);
+
 	return ret;
 }
 

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#145 lore raw

Re: [PATCH 6/X] uprobes: reimplement xol_add_vma() via install_special_mapping()

Quoted 54 lines · Srikar Dronamraju
Quoted 4 lines

I apologize in advance if this was already discussed, but I just can't understand why xol_add_vma() does not use install_special_mapping(). Unless I missed something this should work and this has the following advantages:

The override_creds was based on what Stephen Smalley suggested
https://lkml.org/lkml/2011/4/20/224

At that time Peter had suggested install_special_mapping(). However the consensus was to go with Stephen's suggestion of override_creds.

Quoted 15 lines

- we can avoid override_creds() hacks, install_special_mapping() fools security_file_mmap() passing prot/flags = 0

- no need to play with vma after do_mmap_pgoff()

- no need for get_user_pages(FOLL_WRITE/FOLL_FORCE) hack

- no need for do_munmap() if get_user_pages() fails

- this protects us from mprotect(READ/WRITE)

- this protects from MADV_DONTNEED, the page will be correctly re-instantiated from area->page

Quoted 6 lines

- this makes xol_vma more "cheap", swapper can't see this page and we avoid the meaningless add_to_swap/pageout.

Note that, before this patch, area->page can't be removed from the swap cache anyway (we have the reference). And it must not, uprobes modifies this page directly.

Stephan, Eric,

Would you agree with Oleg's observation that we would be better off using install_special_mapping rather than using override_creds.

To give you some more information about the problem.

Uprobes will be a in-kernel debugging facility that provides singlestepping out of line. To achieve this, it will create a per-mm vma which is not mapped to any file. However this vma has to be executable.

Slots are made in this executable vma, and one slot can be used to single step a original instruction.

This executable vma that we are creating is not for any particular binary but would have to be created dynamically as and when an application is debugged. For example, if we were to debug malloc call in libc, we would end up adding xol vma to all the live processes in the system.

Quoted 7 lines

Since selinux wasnt happy to have an anonymous vma attached, we would create a pseudo file using shmem_file_setup. However after comments from Peter and Stephan's suggestions we started using override_creds. Peter and Oleg suggest that we use install_special_mapping.

Are you okay with using install_special_mapping instead of override_creds()?

That's fine with me. But I'm still not clear on how you are controlling the use of this facility from userspace, which is my primary concern. Who gets to enable/disable this facility, and what check is applied between the process that enables it and the target process(es) that are affected by it? Is it subject to the same checks as ptrace?

Signature · 2 lines
Stephen Smalley
National Security Agency
maintainer
#146 lore raw

Re: [PATCH 6/X] uprobes: reimplement xol_add_vma() via install_special_mapping()

Quoted 10 lines · Stephen Smalley
Quoted 8 lines

Since selinux wasnt happy to have an anonymous vma attached, we would create a pseudo file using shmem_file_setup. However after comments from Peter and Stephan's suggestions we started using override_creds. Peter and Oleg suggest that we use install_special_mapping.

Are you okay with using install_special_mapping instead of override_creds()?

That's fine with me.

Good.

Quoted 2 lines

But I'm still not clear on how you are controlling
the use of this facility from userspace, which is my primary concern.

Yes, but just in case... Any security check in xol_add_vma() is pointless. The task is already "owned" by uprobes when xol_add_vma() is called.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#147 lore raw

PATCH 1/1 uprobes: xol_add_vma: simply use TASK_SIZE as a hint

In reply to Oleg Nesterov

I don't understand why xol_add_vma() abuses mm->mm_rb to find the highest mapping. We can simply use TASK_SIZE-PAGE_SIZE a hint.

If this area is already occupied, the hint will be ignored with or without this change. Otherwise the result is "obviously better" and the code becomes simpler.

---

 kernel/uprobes.c |   13 ++++---------
 1 files changed, 4 insertions(+), 9 deletions(-)
kernel/uprobes.c +4 -9
index 038f21c..b876977 100644
@@ -1045,9 +1045,7 @@ void munmap_uprobe(struct vm_area_struct *vma)
 /* Slot allocation for XOL */
 static int xol_add_vma(struct uprobes_xol_area *area)
 {
-	struct vm_area_struct *vma;
 	struct mm_struct *mm;
-	unsigned long addr_hint;
 	int ret;
 
 	area->page = alloc_page(GFP_HIGHUSER);
@@ -1060,15 +1058,12 @@ static int xol_add_vma(struct uprobes_xol_area *area)
 	ret = -EALREADY;
 	if (mm->uprobes_xol_area)
 		goto fail;
+
 	/*
-	 * Find the end of the top mapping and skip a page.
-	 * If there is no space for PAGE_SIZE above that,
-	 * this hint will be ignored.
+	 * Try to map as high as possible, this is only a hint.
 	 */
-	vma = rb_entry(rb_last(&mm->mm_rb), struct vm_area_struct, vm_rb);
-	addr_hint = vma->vm_end + PAGE_SIZE;
-
-	area->vaddr = get_unmapped_area(NULL, addr_hint, PAGE_SIZE, 0, 0);
+	area->vaddr = get_unmapped_area(NULL, TASK_SIZE - PAGE_SIZE,
+					PAGE_SIZE, 0, 0);
 	if (IS_ERR_VALUE(area->vaddr)) {
 		ret = area->vaddr;
 		goto fail;

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#148 lore raw

PATCH 1/1 (Was: Uprobes patchset with perf probe support)

In reply to Oleg Nesterov

Hello.

Signal patches for uprobes. Note that there are more issues than we discussed before, we can not simply delay/block/whatever the signal.

This needs a bit more cleanups (and perhaps minor fixes), but I _think_ this should work. Well, I hope ;)

On top of 1-7 I sent before.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#149 lore raw

PATCH 1/1 uprobes: kill sstep_complete()

Kill sstep_complete(), change uprobe_notify_resume() to use post_xol() unconditionally.

As we already discussed, it is wrong to assume that regs->ip always changes after the step. rep or jmp/call to self for example. We know that this task has already done the step, we can rely on DIE_DEBUG notification.

---
 kernel/uprobes.c |   37 +++++++++----------------------------
 1 files changed, 9 insertions(+), 28 deletions(-)
kernel/uprobes.c +9 -28
index a323e0a..135b9a2 100644
@@ -1321,24 +1321,6 @@ static int pre_ssout(struct uprobe *uprobe, struct pt_regs *regs,
 }
 
 /*
- * Verify from Instruction Pointer if singlestep has indeed occurred.
- * If Singlestep has occurred, then do post singlestep fix-ups.
- */
-static bool sstep_complete(struct uprobe *uprobe, struct pt_regs *regs)
-{
-	unsigned long vaddr = instruction_pointer(regs);
-
-	/*
-	 * If we have executed out of line, Instruction pointer
-	 * cannot be same as virtual address of XOL slot.
-	 */
-	if (vaddr == current->utask->xol_vaddr)
-		return false;
-	post_xol(uprobe, regs);
-	return true;
-}
-
-/*
  * uprobe_notify_resume gets called in task context just before returning
  * to userspace.
  *
@@ -1374,7 +1356,7 @@ void uprobe_notify_resume(struct pt_regs *regs)
 			if (!utask)
 				goto cleanup_ret;
 		}
-		/* TODO Start queueing signals. */
+
 		utask->active_uprobe = u;
 		handler_chain(u, regs);
 		utask->state = UTASK_SSTEP;
@@ -1385,15 +1367,14 @@ void uprobe_notify_resume(struct pt_regs *regs)
 			goto cleanup_ret;
 	} else if (utask->state == UTASK_SSTEP) {
 		u = utask->active_uprobe;
-		if (sstep_complete(u, regs)) {
-			put_uprobe(u);
-			utask->active_uprobe = NULL;
-			utask->state = UTASK_RUNNING;
-			user_disable_single_step(current);
-			xol_free_insn_slot(current);
-
-			/* TODO Stop queueing signals. */
-		}
+
+		post_xol(u, regs);	/* TODO: check result? */
+
+		put_uprobe(u);
+		utask->active_uprobe = NULL;
+		utask->state = UTASK_RUNNING;
+		user_disable_single_step(current);
+		xol_free_insn_slot(current);
 	}
 	return;
 
Signature · 9 lines
1.5.5.1


--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#150 lore raw

PATCH 1/1 uprobes: introduce UTASK_SSTEP_ACK state

In reply to Oleg Nesterov

Introduce the new state, UTASK_SSTEP_ACK. uprobe_post_notifier() sets this state like uprobe_bkpt_notifier() sets UTASK_BP_HIT.

Change uprobe_notify_resume() to always do the post_xol() logic if state != UTASK_BP_HIT and WARN() if the utask->state is wrong.

This makes the state transitions more explicit. The current code returns silently if, say, state == UTASK_RUNNING. But this must not happen, we should complain in this case. And, with the new state we know for sure that DIE_DEBUG was triggered.

---
 include/linux/uprobes.h |    3 ++-
 kernel/uprobes.c        |   15 ++++++++-------
 2 files changed, 10 insertions(+), 8 deletions(-)
include/linux/uprobes.h +2 -1
index a407d17..1591c7c 100644
@@ -74,7 +74,8 @@ struct uprobe {
 enum uprobe_task_state {
 	UTASK_RUNNING,
 	UTASK_BP_HIT,
-	UTASK_SSTEP
+	UTASK_SSTEP,
+	UTASK_SSTEP_ACK,
 };
 
 /*
kernel/uprobes.c +8 -7
index 135b9a2..5fd72b8 100644
@@ -1365,10 +1365,13 @@ void uprobe_notify_resume(struct pt_regs *regs)
 		else
 			/* Cannot Singlestep; re-execute the instruction. */
 			goto cleanup_ret;
-	} else if (utask->state == UTASK_SSTEP) {
+	} else {
 		u = utask->active_uprobe;
 
-		post_xol(u, regs);	/* TODO: check result? */
+		if (utask->state == UTASK_SSTEP_ACK)
+			post_xol(u, regs);	/* TODO: check result? */
+		else
+			WARN_ON_ONCE(1);
 
 		put_uprobe(u);
 		utask->active_uprobe = NULL;
@@ -1416,15 +1419,13 @@ int uprobe_bkpt_notifier(struct pt_regs *regs)
  */
 int uprobe_post_notifier(struct pt_regs *regs)
 {
-	struct uprobe *uprobe;
-	struct uprobe_task *utask;
+	struct uprobe_task *utask = current->utask;
 
-	if (!current->mm || !current->utask || !current->utask->active_uprobe)
+	if (!current->mm || !utask || !utask->active_uprobe)
 		/* task is currently not uprobed */
 		return 0;
 
-	utask = current->utask;
-	uprobe = utask->active_uprobe;
+	utask->state = UTASK_SSTEP_ACK;
 	set_thread_flag(TIF_UPROBE);
 	return 1;
 }
Signature · 9 lines
1.5.5.1


--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#151 lore raw

PATCH 1/1 uprobes: x86: introduce xol_was_trapped()

In reply to Oleg Nesterov

After the previous patch, we postpone the signals until we execute the probed insn. This is simply wrong if xol insn traps and generates the signal itself. Say, SIGILL/SIGSEGV/etc.

This patch only adds xol_was_trapped() to detect this case. It assumes that anything like do_page_fault/do_trap/etc sets thread.trap_no != -1.

We add uprobe_task_arch_info->saved_trap_no and change pre_xol/post_xol to save/restore thread.trap_no, xol_was_trapped() simply checks that ->trap_no is not equal to UPROBE_TRAP_NO == -1 set by pre_xol().

---
 arch/x86/include/asm/uprobes.h |    2 ++
 arch/x86/kernel/uprobes.c      |   20 ++++++++++++++++++++
 2 files changed, 22 insertions(+), 0 deletions(-)
arch/x86/include/asm/uprobes.h +2 -0
index 1c30cfd..f0fbdab 100644
@@ -39,6 +39,7 @@ struct uprobe_arch_info {
 
 struct uprobe_task_arch_info {
 	unsigned long saved_scratch_register;
+	unsigned long saved_trap_no;
 };
 #else
 struct uprobe_arch_info {};
@@ -49,6 +50,7 @@ extern int analyze_insn(struct task_struct *tsk, struct uprobe *uprobe);
 extern void set_instruction_pointer(struct pt_regs *regs, unsigned long vaddr);
 extern int pre_xol(struct uprobe *uprobe, struct pt_regs *regs);
 extern int post_xol(struct uprobe *uprobe, struct pt_regs *regs);
+extern bool xol_was_trapped(struct task_struct *tsk);
 extern int uprobe_exception_notify(struct notifier_block *self,
 				       unsigned long val, void *data);
 #endif	/* _ASM_UPROBES_H */
arch/x86/kernel/uprobes.c +20 -0
index e2e7882..c861c27 100644
@@ -395,6 +395,8 @@ void set_instruction_pointer(struct pt_regs *regs, unsigned long vaddr)
 	regs->ip = vaddr;
 }
 
+#define	UPROBE_TRAP_NO	-1ul
+
 /*
  * pre_xol - prepare to execute out of line.
  * @uprobe: the probepoint information.
@@ -410,6 +412,9 @@ int pre_xol(struct uprobe *uprobe, struct pt_regs *regs)
 {
 	struct uprobe_task_arch_info *tskinfo = &current->utask->tskinfo;
 
+	tskinfo->saved_trap_no = current->thread.trap_no;
+	current->thread.trap_no = UPROBE_TRAP_NO;
+
 	regs->ip = current->utask->xol_vaddr;
 	if (uprobe->fixups & UPROBES_FIX_RIP_AX) {
 		tskinfo->saved_scratch_register = regs->ax;
@@ -425,6 +430,11 @@ int pre_xol(struct uprobe *uprobe, struct pt_regs *regs)
 #else
 int pre_xol(struct uprobe *uprobe, struct pt_regs *regs)
 {
+	struct uprobe_task_arch_info *tskinfo = &current->utask->tskinfo;
+
+	tskinfo->saved_trap_no = current->thread.trap_no;
+	current->thread.trap_no = UPROBE_TRAP_NO;
+
 	regs->ip = current->utask->xol_vaddr;
 	return 0;
 }
@@ -493,6 +503,14 @@ static void handle_riprel_post_xol(struct uprobe *uprobe,
 }
 #endif
 
+bool xol_was_trapped(struct task_struct *tsk)
+{
+	if (tsk->thread.trap_no != UPROBE_TRAP_NO)
+		return true;
+
+	return false;
+}
+
 /*
  * Called after single-stepping. To avoid the SMP problems that can
  * occur when we temporarily put back the original opcode to
@@ -523,6 +541,8 @@ int post_xol(struct uprobe *uprobe, struct pt_regs *regs)
 	int result = 0;
 	long correction;
 
+	current->thread.trap_no = utask->tskinfo.saved_trap_no;
+
 	correction = (long)(utask->vaddr - utask->xol_vaddr);
 	handle_riprel_post_xol(uprobe, regs, &correction);
 	if (uprobe->fixups & UPROBES_FIX_IP)
Signature · 9 lines
1.5.5.1


--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#152 lore raw

Re: [PATCH 11/X] uprobes: x86: introduce xol_was_trapped()

arch/x86/include/asm/uprobes.h struct uprobe_arch_info { · line 45

Quoted 12 lines
arch/x86/include/asm/uprobes.h +1 -0
index 1c30cfd..f0fbdab 100644
@@ -39,6 +39,7 @@ struct uprobe_arch_info {
 
 struct uprobe_task_arch_info {
 	unsigned long saved_scratch_register;
+	unsigned long saved_trap_no;
 };
 #else
 struct uprobe_arch_info {};

one nit
I had to add saved_trap_no to #else part (i.e uprobe_arch_info ).

--
Thanks and Regards
Srikar

maintainer
#153 lore raw

Re: [PATCH 11/X] uprobes: x86: introduce xol_was_trapped()

Quoted 7 lines · Srikar Dronamraju
Quoted 7 lines
arch/x86/include/asm/uprobes.h +0 -0
index 1c30cfd..f0fbdab 100644
@@ -39,6 +39,7 @@ struct uprobe_arch_info {
 
Quoted 10 lines

arch/x86/include/asm/uprobes.h struct uprobe_arch_info { · line 45

Quoted 6 lines
arch/x86/include/asm/uprobes.h +1 -0
 struct uprobe_task_arch_info {
 	unsigned long saved_scratch_register;
+	unsigned long saved_trap_no;
 };
 #else
 struct uprobe_arch_info {};

one nit
I had to add saved_trap_no to #else part (i.e uprobe_arch_info ).

Yes, thanks, I didn't notice this is for X86_64 only.

And just in case, please feel free to rename/redo/whatever.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#154 lore raw

PATCH 1/1 uprobes: introduce uprobe_deny_signal()

In reply to Oleg Nesterov

A not-UTASK_RUNNING task obviously can't handle the signals, neither it should stop/freeze/etc. It must not even exit if it was SIGKILL'ed (see the next changes).

This patch adds the new hook, uprobe_deny_signal(), called by get_signal_to_deliver(). It simply clears TIF_SIGPENDING to ensure that this thread can do nothing connected to signals until it becomes UTASK_RUNNING.

We also change post_xol() path to do recalc_sigpending() before return to user-mode, this ensures the signal can't be lost.

NOTE! Without the next changes this patch is buggy.

---
 include/linux/uprobes.h |    5 +++++
 kernel/signal.c         |    3 +++
 kernel/uprobes.c        |   23 +++++++++++++++++++++++
 3 files changed, 31 insertions(+), 0 deletions(-)
include/linux/uprobes.h +5 -0
index 1591c7c..27928e5 100644
@@ -134,6 +134,7 @@ extern unsigned long __weak get_uprobe_bkpt_addr(struct pt_regs *regs);
 extern int uprobe_post_notifier(struct pt_regs *regs);
 extern int uprobe_bkpt_notifier(struct pt_regs *regs);
 extern void uprobe_notify_resume(struct pt_regs *regs);
+extern bool uprobe_deny_signal(void);
 #else /* CONFIG_UPROBES is not defined */
 static inline int register_uprobe(struct inode *inode, loff_t offset,
 				struct uprobe_consumer *consumer)
@@ -154,6 +155,10 @@ static inline void munmap_uprobe(struct vm_area_struct *vma)
 static inline void uprobe_notify_resume(struct pt_regs *regs)
 {
 }
+static inline bool uprobe_deny_signal(void)
+{
+	return false;
+}
 static inline unsigned long get_uprobe_bkpt_addr(struct pt_regs *regs)
 {
 	return 0;
kernel/signal.c +3 -0
index 291c970..788b494 100644
@@ -2141,6 +2141,9 @@ int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,
 	struct signal_struct *signal = current->signal;
 	int signr;
 
+	if (unlikely(uprobe_deny_signal()))
+		return 0;
+
 relock:
 	/*
 	 * We'll jump back here after any time we were stopped in TASK_STOPPED.
kernel/uprobes.c +23 -0
index 5fd72b8..d6f4508 100644
@@ -1320,6 +1320,25 @@ static int pre_ssout(struct uprobe *uprobe, struct pt_regs *regs,
 	return -EFAULT;
 }
 
+bool uprobe_deny_signal(void)
+{
+	struct task_struct *tsk = current;
+	struct uprobe_task *utask = tsk->utask;
+
+	if (likely(!utask || !utask->active_uprobe))
+		return false;
+
+	WARN_ON_ONCE(utask->state != UTASK_SSTEP);
+
+	if (signal_pending(tsk)) {
+		spin_lock_irq(&tsk->sighand->siglock);
+		clear_tsk_thread_flag(tsk, TIF_SIGPENDING);
+		spin_unlock_irq(&tsk->sighand->siglock);
+	}
+
+	return true;
+}
+
 /*
  * uprobe_notify_resume gets called in task context just before returning
  * to userspace.
@@ -1378,6 +1397,10 @@ void uprobe_notify_resume(struct pt_regs *regs)
 		utask->state = UTASK_RUNNING;
 		user_disable_single_step(current);
 		xol_free_insn_slot(current);
+
+		spin_lock_irq(&current->sighand->siglock);
+		recalc_sigpending(); /* see uprobe_deny_signal() */
+		spin_unlock_irq(&current->sighand->siglock);
 	}
 	return;
 
Signature · 9 lines
1.5.5.1


--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#155 lore raw

PATCH 1/1 uprobes: x86: introduce abort_xol()

In reply to Oleg Nesterov

A separate "patch", just to emphasize that I do not know what actually abort_xol() should do! I do not understand this asm magic.

This patch simply changes regs->ip back to the probed insn, obviously this is not enough to handle UPROBES_FIX_*. Please take care.

If it is not clear, abort_xol() is needed when we should re-execute the original insn (replaced with int3), see the next patch.

---
 arch/x86/include/asm/uprobes.h |    1 +
 arch/x86/kernel/uprobes.c      |    9 +++++++++
 2 files changed, 10 insertions(+), 0 deletions(-)
arch/x86/include/asm/uprobes.h +1 -0
index f0fbdab..6209da1 100644
@@ -51,6 +51,7 @@ extern void set_instruction_pointer(struct pt_regs *regs, unsigned long vaddr);
 extern int pre_xol(struct uprobe *uprobe, struct pt_regs *regs);
 extern int post_xol(struct uprobe *uprobe, struct pt_regs *regs);
 extern bool xol_was_trapped(struct task_struct *tsk);
+extern void abort_xol(struct pt_regs *regs);
 extern int uprobe_exception_notify(struct notifier_block *self,
 				       unsigned long val, void *data);
 #endif	/* _ASM_UPROBES_H */
arch/x86/kernel/uprobes.c +9 -0
index c861c27..bc11a89 100644
@@ -511,6 +511,15 @@ bool xol_was_trapped(struct task_struct *tsk)
 	return false;
 }
 
+void abort_xol(struct pt_regs *regs)
+{
+	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	// !!! Dear Srikar and Ananth, please implement me !!!
+	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	struct uprobe_task *utask = current->utask;
+	regs->ip = utask->vaddr;
+}
+
 /*
  * Called after single-stepping. To avoid the SMP problems that can
  * occur when we temporarily put back the original opcode to
Signature · 9 lines
1.5.5.1


--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#156 lore raw

Re: [PATCH 12/X] uprobes: x86: introduce abort_xol()

Hey Oleg,

Quoted 11 lines

A separate "patch", just to emphasize that I do not know what actually abort_xol() should do! I do not understand this asm magic.

This patch simply changes regs->ip back to the probed insn, obviously this is not enough to handle UPROBES_FIX_*. Please take care.

If it is not clear, abort_xol() is needed when we should re-execute the original insn (replaced with int3), see the next patch.

We should be removing the breakpoint in abort_xol(). Otherwise if we just set the instruction pointer to int3 and signal a sigill, then the user may be confused why a breakpoint is generating SIGILL.

Quoted 22 lines
---
 arch/x86/include/asm/uprobes.h |    1 +
 arch/x86/kernel/uprobes.c      |    9 +++++++++
 2 files changed, 10 insertions(+), 0 deletions(-)
arch/x86/include/asm/uprobes.h +1 -0
index f0fbdab..6209da1 100644
@@ -51,6 +51,7 @@ extern void set_instruction_pointer(struct pt_regs *regs, unsigned long vaddr);
 extern int pre_xol(struct uprobe *uprobe, struct pt_regs *regs);
 extern int post_xol(struct uprobe *uprobe, struct pt_regs *regs);
 extern bool xol_was_trapped(struct task_struct *tsk);
+extern void abort_xol(struct pt_regs *regs);
 extern int uprobe_exception_notify(struct notifier_block *self,
 				       unsigned long val, void *data);
 #endif	/* _ASM_UPROBES_H */
arch/x86/kernel/uprobes.c +0 -0
index c861c27..bc11a89 100644

arch/x86/kernel/uprobes.c bool xol_was_trapped(struct task_struct *tsk) · line 520

Quoted 10 lines
arch/x86/kernel/uprobes.c +7 -0
@@ -511,6 +511,15 @@ bool xol_was_trapped(struct task_struct *tsk)
 	return false;
 }
 
+void abort_xol(struct pt_regs *regs)
+{
+	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	// !!! Dear Srikar and Ananth, please implement me !!!
+	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	struct uprobe_task *utask = current->utask;
+	regs->ip = utask->vaddr;

nit:
Shouldnt we be setting the ip to the next instruction after this instruction?

arch/x86/kernel/uprobes.c bool xol_was_trapped(struct task_struct *tsk) · line 525

Quoted 5 lines
arch/x86/kernel/uprobes.c +2 -0
+}
+
 /*
  * Called after single-stepping. To avoid the SMP problems that can
  * occur when we temporarily put back the original opcode to

I have applied all your patches and ran tests, the tests are all passing.

I will fold them into my patches and send them out.

Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#157 lore raw

Re: [PATCH 12/X] uprobes: x86: introduce abort_xol()

Quoted 6 lines · Srikar Dronamraju
Quoted 4 lines

If it is not clear, abort_xol() is needed when we should re-execute the original insn (replaced with int3), see the next patch.

We should be removing the breakpoint in abort_xol().

Why? See also below.

Quoted 3 lines

Otherwise if we just set the instruction pointer to int3 and signal a sigill, then the user may be confused why a breakpoint is generating SIGILL.

Which user?

gdb? Of course it can be confused. But it can be confused in any case.

Quoted 11 lines

arch/x86/kernel/uprobes.c bool xol_was_trapped(struct task_struct *tsk) · line 520

Quoted 7 lines
arch/x86/kernel/uprobes.c +7 -0
+void abort_xol(struct pt_regs *regs)
+{
+	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	// !!! Dear Srikar and Ananth, please implement me !!!
+	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	struct uprobe_task *utask = current->utask;
+	regs->ip = utask->vaddr;

nit:
Shouldnt we be setting the ip to the next instruction after this instruction?

Not sure...

We should restart the same insn. Say, if the probed insn was "*(int*)0 = 0", it should be executed again after SIGSEGV. Unless the task was killed by this signal.

And in this case we should call uprobe_consumer()->handler() again, we shouldn't remove "int3".

Quoted 4 lines

I have applied all your patches and ran tests, the tests are all passing.

I will fold them into my patches and send them out.

Great, thanks.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#158 lore raw

Re: [PATCH 12/X] uprobes: x86: introduce abort_xol()

In reply to Srikar Dronamraju

On Fri, Oct 21, 2011 at 08:12:07PM +0530, Srikar Dronamraju wrote:

...

Quoted 34 lines
Quoted 3 lines

If it is not clear, abort_xol() is needed when we should re-execute the original insn (replaced with int3), see the next patch.

We should be removing the breakpoint in abort_xol(). Otherwise if we just set the instruction pointer to int3 and signal a sigill, then the user may be confused why a breakpoint is generating SIGILL.

Quoted 25 lines
---
 arch/x86/include/asm/uprobes.h |    1 +
 arch/x86/kernel/uprobes.c      |    9 +++++++++
 2 files changed, 10 insertions(+), 0 deletions(-)
arch/x86/include/asm/uprobes.h +1 -0
index f0fbdab..6209da1 100644
@@ -51,6 +51,7 @@ extern void set_instruction_pointer(struct pt_regs *regs, unsigned long vaddr);
 extern int pre_xol(struct uprobe *uprobe, struct pt_regs *regs);
 extern int post_xol(struct uprobe *uprobe, struct pt_regs *regs);
 extern bool xol_was_trapped(struct task_struct *tsk);
+extern void abort_xol(struct pt_regs *regs);
 extern int uprobe_exception_notify(struct notifier_block *self,
 				       unsigned long val, void *data);
 #endif	/* _ASM_UPROBES_H */
arch/x86/kernel/uprobes.c +0 -0
index c861c27..bc11a89 100644
@@ -511,6 +511,15 @@ bool xol_was_trapped(struct task_struct *tsk)
 	return false;
 }
 
Quoted 11 lines

arch/x86/kernel/uprobes.c bool xol_was_trapped(struct task_struct *tsk) · line 520

Quoted 7 lines
arch/x86/kernel/uprobes.c +7 -0
+void abort_xol(struct pt_regs *regs)
+{
+	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	// !!! Dear Srikar and Ananth, please implement me !!!
+	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	struct uprobe_task *utask = current->utask;
+	regs->ip = utask->vaddr;

nit:
Shouldnt we be setting the ip to the next instruction after this instruction?

No, since we should re-execute the original instruction after removing the breakpoint.

Also, wrt ip being set to the next instruction on a breakpoint hit, that's arch specific. For instance, on x86, it points to the next instruction, while on powerpc, the nip points to the breakpoint vaddr at the time of exception.

Ananth

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#159 lore raw

Re: [PATCH 12/X] uprobes: x86: introduce abort_xol()

Quoted 15 lines · Ananth N Mavinakayanahalli
Quoted 11 lines · Srikar Dronamraju

arch/x86/kernel/uprobes.c bool xol_was_trapped(struct task_struct *tsk) · line 520

Quoted 7 lines
arch/x86/kernel/uprobes.c +7 -0
+void abort_xol(struct pt_regs *regs)
+{
+	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	// !!! Dear Srikar and Ananth, please implement me !!!
+	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	struct uprobe_task *utask = current->utask;
+	regs->ip = utask->vaddr;

nit:
Shouldnt we be setting the ip to the next instruction after this instruction?

Quoted 1 line

No, since we should re-execute the original instruction

Yes,

Quoted 2 lines

after removing
the breakpoint.

No? we should not remove this uprobe?

Quoted 2 lines

Also, wrt ip being set to the next instruction on a breakpoint hit, that's arch specific.

Probably yes, I am not sure. But:

Quoted 2 lines

For instance, on x86, it points to the next
instruction,

No?

	/**
	 * get_uprobe_bkpt_addr - compute address of bkpt given post-bkpt regs
	 * @regs: Reflects the saved state of the task after it has hit a breakpoint
	 * instruction.
	 * Return the address of the breakpoint instruction.
	 */
	unsigned long __weak get_uprobe_bkpt_addr(struct pt_regs *regs)
	{
		return instruction_pointer(regs) - UPROBES_BKPT_INSN_SIZE;
	}

Yes, initially regs->ip points to the next insn after int3, but utask->vaddr == get_uprobe_bkpt_addr() == addr of int3.

Right?

Quoted 2 lines

while on powerpc, the nip points to the breakpoint vaddr at the time of exception.

I think get_uprobe_bkpt_addr() should be consistent on every arch. That is why (I think) it is __weak.

Anyway, abort_xol() has to be arch-specific.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#160 lore raw

test-case (Was: [PATCH 12/X] uprobes: x86: introduce abort_xol())

Quoted 17 lines · Oleg Nesterov
Quoted 15 lines · Ananth N Mavinakayanahalli
Quoted 11 lines · Srikar Dronamraju

arch/x86/kernel/uprobes.c bool xol_was_trapped(struct task_struct *tsk) · line 520

Quoted 7 lines
arch/x86/kernel/uprobes.c +7 -0
+void abort_xol(struct pt_regs *regs)
+{
+	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	// !!! Dear Srikar and Ananth, please implement me !!!
+	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	struct uprobe_task *utask = current->utask;
+	regs->ip = utask->vaddr;

nit:
Shouldnt we be setting the ip to the next instruction after this instruction?

Quoted 3 lines
Quoted 1 line

No, since we should re-execute the original instruction

Yes,

In case it was not clear, I meant "agree with your 'No'".

Quoted 26 lines
Quoted 2 lines

after removing
the breakpoint.

No? we should not remove this uprobe?

Quoted 2 lines

Also, wrt ip being set to the next instruction on a breakpoint hit, that's arch specific.

Probably yes, I am not sure. But:

Quoted 2 lines

For instance, on x86, it points to the next
instruction,

No?

	/**
	 * get_uprobe_bkpt_addr - compute address of bkpt given post-bkpt regs
	 * @regs: Reflects the saved state of the task after it has hit a breakpoint
	 * instruction.
	 * Return the address of the breakpoint instruction.
	 */
	unsigned long __weak get_uprobe_bkpt_addr(struct pt_regs *regs)
	{
		return instruction_pointer(regs) - UPROBES_BKPT_INSN_SIZE;
	}
Quoted 2 lines

Yes, initially regs->ip points to the next insn after int3, but utask->vaddr == get_uprobe_bkpt_addr() == addr of int3.

Ananth, Srikar, I'd suggest this test-case:

	#include <stdio.h>
	#include <signal.h>
	#include <ucontext.h>

	void *fault_insn;

	static inline void *uc_ip(struct ucontext *ctxt)
	{
		return (void*)ctxt->uc_mcontext.gregs[16];
	}

	void segv(int sig, siginfo_t *info, void *ctxt)
	{
		static int cnt;

		printf("SIGSEGV! ip=%p addr=%p\n", uc_ip(ctxt), info->si_addr);

		if (uc_ip(ctxt) != fault_insn)
			printf("ERR!! wrong ip\n");
		if (info->si_addr != (void*)0x12345678)
			printf("ERR!! wrong addr\n");

		if (++cnt == 3)
			signal(SIGSEGV, SIG_DFL);
	}

	int main(void)
	{
		struct sigaction sa = {
			.sa_sigaction	= segv,
			.sa_flags	= SA_SIGINFO,
		};

		sigaction(SIGSEGV, &sa, NULL);

		fault_insn = &&label;

	label:
		asm volatile ("movl $0x0,0x12345678");

		return 0;
	}

result:

	$ ulimit -c unlimited
	$ ./segv
	SIGSEGV! ip=0x4006eb addr=0x12345678
	SIGSEGV! ip=0x4006eb addr=0x12345678
	SIGSEGV! ip=0x4006eb addr=0x12345678
	Segmentation fault (core dumped)
	$ gdb -c ./core.1826
	...
	Program terminated with signal 11, Segmentation fault.
	#0  0x00000000004006eb in ?? ()

Now. If you insert uprobe at asm("movl") insn, result should be the same or the patches I sent are wrong. In particular, the addr in the coredump should be correct too. And consumer->handler() should be called 3 times too. This insn is really executed 3 times.

I have no idea how can I test this.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#161 lore raw

Re: test-case (Was: [PATCH 12/X] uprobes: x86: introduce abort_xol())

Quoted 61 lines

Ananth, Srikar, I'd suggest this test-case:

	#include <stdio.h>
	#include <signal.h>
	#include <ucontext.h>

	void *fault_insn;

	static inline void *uc_ip(struct ucontext *ctxt)
	{
		return (void*)ctxt->uc_mcontext.gregs[16];
	}

	void segv(int sig, siginfo_t *info, void *ctxt)
	{
		static int cnt;

		printf("SIGSEGV! ip=%p addr=%p\n", uc_ip(ctxt), info->si_addr);

		if (uc_ip(ctxt) != fault_insn)
			printf("ERR!! wrong ip\n");
		if (info->si_addr != (void*)0x12345678)
			printf("ERR!! wrong addr\n");

		if (++cnt == 3)
			signal(SIGSEGV, SIG_DFL);
	}

	int main(void)
	{
		struct sigaction sa = {
			.sa_sigaction	= segv,
			.sa_flags	= SA_SIGINFO,
		};

		sigaction(SIGSEGV, &sa, NULL);

		fault_insn = &&label;

	label:
		asm volatile ("movl $0x0,0x12345678");

		return 0;
	}

result:

	$ ulimit -c unlimited
	$ ./segv
	SIGSEGV! ip=0x4006eb addr=0x12345678
	SIGSEGV! ip=0x4006eb addr=0x12345678
	SIGSEGV! ip=0x4006eb addr=0x12345678
	Segmentation fault (core dumped)
	$ gdb -c ./core.1826
	...
	Program terminated with signal 11, Segmentation fault.
	#0  0x00000000004006eb in ?? ()
Quoted 7 lines

Now. If you insert uprobe at asm("movl") insn, result should be the same or the patches I sent are wrong. In particular, the addr in the coredump should be correct too. And consumer->handler() should be called 3 times too. This insn is really executed 3 times.

I have no idea how can I test this.

I have tested this on both x86_32 and x86_64 and can confirm that the behaviour is same with or without uprobes placed at that instruction. This is on the uprobes code with your changes.

However on x86_32; the output is different from x86_64.

On x86_32 (I have additionally printed the uc_ip and fault_insn.

SIGSEGV! ip=0x10246 addr=0x12345678
ERR!! wrong ip uc_ip(ctxt) = 10246 fault_insn = 804856c SIGSEGV! ip=0x10246 addr=0x12345678
ERR!! wrong ip uc_ip(ctxt) = 10246 fault_insn = 804856c SIGSEGV! ip=0x10246 addr=0x12345678
ERR!! wrong ip uc_ip(ctxt) = 10246 fault_insn = 804856c Segmentation fault

the fault_insn matches the address shown in disassemble of gdb. I still trying to dig up what uc_ip is and why its different on x86_32.

On x86_64 the result is what you pasted above.

Also I was thinking on your suggestion of making abort_xol a weak function. In which case we could have architecture independent function in kernel/uprobes.c which is just a wrapper for set_instruction_pointer.

void __weak abort_xol(struct pt_regs *regs, struct uprobe_task *utask)

{
	set_instruction_pointer(regs, utask->vaddr);	
}

where it would called from uprobe_notify_resume() as

	abort_xol(regs, utask);

If other archs would want to do something else, they could override abort_xol definition.

Signature · 9 lines
Thanks and Regards
Srikar

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#162 lore raw

Re: test-case (Was: [PATCH 12/X] uprobes: x86: introduce abort_xol())

Quoted 10 lines · Srikar Dronamraju
Quoted 7 lines
	static inline void *uc_ip(struct ucontext *ctxt)
	{
		return (void*)ctxt->uc_mcontext.gregs[16];
	}

...

I have tested this on both x86_32 and x86_64 and can confirm that the behaviour is same with or without uprobes placed at that instruction. This is on the uprobes code with your changes.

Great, thanks.

Quoted 6 lines

However on x86_32; the output is different from x86_64.

On x86_32 (I have additionally printed the uc_ip and fault_insn.

SIGSEGV! ip=0x10246 addr=0x12345678
ERR!! wrong ip uc_ip(ctxt) = 10246 fault_insn = 804856c

Yep. uc_ip() is not correct on x86_32. Sorry, I forgot to mention this.

I was really surprised when I wrote this test. I simply can't understand how can I play with ucontext in the user-space. I guess uc_ip() should use REG_EIP instead of 16, but I wasn't able to compile it even if I added __USE_GNU. It would be even better to use sigcontext instead of the ugly mcontext_t, but this looks "impossible". The kernel is much simpler ;)

Quoted 1 line

I still trying to dig up what uc_ip is and why its different on x86_32.

See above. I guess it needs ctxt->uc_mcontext.gregs[14]. Or REG_EIP.

uc_ip() simply reads sigcontext->ip passed by setup_sigcontext().

Quoted 9 lines

Also I was thinking on your suggestion of making abort_xol a weak function. In which case we could have architecture independent function in kernel/uprobes.c which is just a wrapper for set_instruction_pointer.

void __weak abort_xol(struct pt_regs *regs, struct uprobe_task *utask)

{
	set_instruction_pointer(regs, utask->vaddr);
}
Quoted 6 lines

where it would called from uprobe_notify_resume() as

	abort_xol(regs, utask);

If other archs would want to do something else, they could override abort_xol definition.

I didn't suggest this ;) But looks reasonable to me. And afaics x86_32 can use this arch-independent function.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#163 lore raw

Re: [PATCH 12/X] uprobes: x86: introduce abort_xol()

In reply to Oleg Nesterov
Quoted 1 line · Oleg Nesterov

On 10/21, Ananth N Mavinakayanahalli wrote:

...

Quoted 4 lines
Quoted 2 lines

For instance, on x86, it points to the next
instruction,

No?

At exception entry, we'd not have done the following fixup...

Quoted 11 lines
	/**
	 * get_uprobe_bkpt_addr - compute address of bkpt given post-bkpt regs
	 * @regs: Reflects the saved state of the task after it has hit a breakpoint
	 * instruction.
	 * Return the address of the breakpoint instruction.
	 */
	unsigned long __weak get_uprobe_bkpt_addr(struct pt_regs *regs)
	{
		return instruction_pointer(regs) - UPROBES_BKPT_INSN_SIZE;
	}
Quoted 4 lines

Yes, initially regs->ip points to the next insn after int3, but utask->vaddr == get_uprobe_bkpt_addr() == addr of int3.

Right?

Yes, we fix it up so we point to the right (breakpoint) address.

Quoted 5 lines
Quoted 2 lines

while on powerpc, the nip points to the breakpoint vaddr at the time of exception.

I think get_uprobe_bkpt_addr() should be consistent on every arch. That is why (I think) it is __weak.

Yes, that is the intention.

Quoted 1 line

Anyway, abort_xol() has to be arch-specific.

Agree.

Ananth

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#164 lore raw

PATCH 1/1 uprobes: introduce UTASK_SSTEP_TRAPPED logic

In reply to Oleg Nesterov

Finally, add UTASK_SSTEP_TRAPPED state/code to handle the case when xol insn itself triggers the signal.

In this case we should restart the original insn even if the task is already SIGKILL'ed (say, the coredump should report the correct ip). This is even more important if the task has a handler for SIGSEGV/etc, The _same_ instruction should be repeated again after return from the signal handler, and SSTEP can never finish in this case.

So this patch changes uprobe_deny_signal() to set UTASK_SSTEP_TRAPPED and TIF_UPROBE. It also sets TIF_NOTIFY_RESUME, _afaics_ TIF_UPROBE alone is not enough to trigger do_notify_resume() in this case.

When uprobe_notify_resume() sees UTASK_SSTEP_TRAPPED it does abort_xol() instead of post_xol().

---
 arch/x86/kernel/uprobes.c |    1 +
 include/linux/uprobes.h   |    1 +
 kernel/uprobes.c          |    8 ++++++++
 3 files changed, 10 insertions(+), 0 deletions(-)
arch/x86/kernel/uprobes.c +1 -0
index bc11a89..73f58ad 100644
@@ -550,6 +550,7 @@ int post_xol(struct uprobe *uprobe, struct pt_regs *regs)
 	int result = 0;
 	long correction;
 
+	WARN_ON_ONCE(current->thread.trap_no != UPROBE_TRAP_NO);
 	current->thread.trap_no = utask->tskinfo.saved_trap_no;
 
 	correction = (long)(utask->vaddr - utask->xol_vaddr);
include/linux/uprobes.h +1 -0
index 27928e5..2b4bc8c 100644
@@ -76,6 +76,7 @@ enum uprobe_task_state {
 	UTASK_BP_HIT,
 	UTASK_SSTEP,
 	UTASK_SSTEP_ACK,
+	UTASK_SSTEP_TRAPPED,
 };
 
 /*
kernel/uprobes.c +8 -0
index d6f4508..aa5492a 100644
@@ -1334,6 +1334,12 @@ bool uprobe_deny_signal(void)
 		spin_lock_irq(&tsk->sighand->siglock);
 		clear_tsk_thread_flag(tsk, TIF_SIGPENDING);
 		spin_unlock_irq(&tsk->sighand->siglock);
+
+		if (xol_was_trapped(tsk)) {
+			utask->state = UTASK_SSTEP_TRAPPED;
+			set_tsk_thread_flag(tsk, TIF_UPROBE);
+			set_tsk_thread_flag(tsk, TIF_NOTIFY_RESUME);
+		}
 	}
 
 	return true;
@@ -1389,6 +1395,8 @@ void uprobe_notify_resume(struct pt_regs *regs)
 
 		if (utask->state == UTASK_SSTEP_ACK)
 			post_xol(u, regs);	/* TODO: check result? */
+		else if (utask->state == UTASK_SSTEP_TRAPPED)
+			abort_xol(regs);
 		else
 			WARN_ON_ONCE(1);
 
Signature · 9 lines
1.5.5.1


--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#165 lore raw

Re: [PATCH 13/X] uprobes: introduce UTASK_SSTEP_TRAPPED logic

Quoted 8 lines · Oleg Nesterov

Finally, add UTASK_SSTEP_TRAPPED state/code to handle the case when xol insn itself triggers the signal.

In this case we should restart the original insn even if the task is already SIGKILL'ed (say, the coredump should report the correct ip). This is even more important if the task has a handler for SIGSEGV/etc, The _same_ instruction should be repeated again after return from the signal handler, and SSTEP can never finish in this case.

Oleg,

Not sure I understand this completely...

When you say 'correct ip' you mean the original vaddr where we now have a uprobe breakpoint and not the xol copy, right?

Coredump needs to report the correct ip, but should it also not report correctly the instruction that caused the signal? Ergo, shouldn't we put the original instruction back at the uprobed vaddr?

Ananth

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#166 lore raw

Re: [PATCH 13/X] uprobes: introduce UTASK_SSTEP_TRAPPED logic

Quoted 11 lines · Ananth N Mavinakayanahalli
Quoted 8 lines · Oleg Nesterov

Finally, add UTASK_SSTEP_TRAPPED state/code to handle the case when xol insn itself triggers the signal.

In this case we should restart the original insn even if the task is already SIGKILL'ed (say, the coredump should report the correct ip). This is even more important if the task has a handler for SIGSEGV/etc, The _same_ instruction should be repeated again after return from the signal handler, and SSTEP can never finish in this case.

Quoted 3 lines

Oleg,

Not sure I understand this completely...

I hope you do not think I do ;)

Quoted 2 lines

When you say 'correct ip' you mean the original vaddr where we now have a uprobe breakpoint and not the xol copy, right?

Yes,

Quoted 3 lines

Coredump needs to report the correct ip, but should it also not report correctly the instruction that caused the signal? Ergo, shouldn't we put the original instruction back at the uprobed vaddr?

OK, now I see what you mean. I was confused by the "restore the original instruction before _restart_" suggestion.

Agreed! it would be nice to "hide" these int3's if we dump the core, but I think this is a bit off-topic. It makes sense to do this in any case, even if the core-dumping was triggered by another thread/insn. It makes sense to remove all int3's, not only at regs->ip location. But how can we do this? This is nontrivial.

And. Even worse. Suppose that you do "gdb probed_application". Now you see int3's in the disassemble output. What can we do?

I think we can do nothing, at least currently. This just reflects the fact that uprobe connects to inode, not to process/mm/etc.

What do you think?

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#167 lore raw

Re: [PATCH 13/X] uprobes: introduce UTASK_SSTEP_TRAPPED logic

Quoted 12 lines · Oleg Nesterov
Quoted 11 lines · Ananth N Mavinakayanahalli
Quoted 8 lines · Oleg Nesterov

Finally, add UTASK_SSTEP_TRAPPED state/code to handle the case when xol insn itself triggers the signal.

In this case we should restart the original insn even if the task is already SIGKILL'ed (say, the coredump should report the correct ip). This is even more important if the task has a handler for SIGSEGV/etc, The _same_ instruction should be repeated again after return from the signal handler, and SSTEP can never finish in this case.

Quoted 5 lines
Quoted 3 lines

Oleg,

Not sure I understand this completely...

I hope you do not think I do ;)

I think you understand it better than you think you do :-)

Quoted 9 lines
Quoted 2 lines

When you say 'correct ip' you mean the original vaddr where we now have a uprobe breakpoint and not the xol copy, right?

Yes,

Quoted 3 lines

Coredump needs to report the correct ip, but should it also not report correctly the instruction that caused the signal? Ergo, shouldn't we put the original instruction back at the uprobed vaddr?

Quoted 8 lines

OK, now I see what you mean. I was confused by the "restore the original instruction before _restart_" suggestion.

Agreed! it would be nice to "hide" these int3's if we dump the core, but I think this is a bit off-topic. It makes sense to do this in any case, even if the core-dumping was triggered by another thread/insn. It makes sense to remove all int3's, not only at regs->ip location. But how can we do this? This is nontrivial.

I don't think that is a problem.. see below...

Quoted 2 lines

And. Even worse. Suppose that you do "gdb probed_application". Now you see int3's in the disassemble output. What can we do?

In this case, nothing.

Quoted 4 lines

I think we can do nothing, at least currently. This just reflects the fact that uprobe connects to inode, not to process/mm/etc.

What do you think?

Thinking further on this, in the normal 'running gdb on a core' case, we won't have this problem, as the binary that we point gdb to, will be a pristine one, without the uprobe int3s, right?

Ananth

maintainer
#168 lore raw

Re: [PATCH 13/X] uprobes: introduce UTASK_SSTEP_TRAPPED logic

Quoted 16 lines · Ananth N Mavinakayanahalli
Quoted 6 lines · Oleg Nesterov

Agreed! it would be nice to "hide" these int3's if we dump the core, but I think this is a bit off-topic. It makes sense to do this in any case, even if the core-dumping was triggered by another thread/insn. It makes sense to remove all int3's, not only at regs->ip location. But how can we do this? This is nontrivial.

I don't think that is a problem.. see below...

Quoted 2 lines

And. Even worse. Suppose that you do "gdb probed_application". Now you see int3's in the disassemble output. What can we do?

In this case, nothing.

Quoted 8 lines
Quoted 4 lines

I think we can do nothing, at least currently. This just reflects the fact that uprobe connects to inode, not to process/mm/etc.

What do you think?

Thinking further on this, in the normal 'running gdb on a core' case, we won't have this problem, as the binary that we point gdb to, will be a pristine one, without the uprobe int3s, right?

Not sure I understand.

I meant, if we have a binary with uprobes (iow, register_uprobe() installed uprobes into that file), then gdb will see int3's with or without the core. Or you can add uprobe into glibc, say you can probe getpid(). Now (again, with or without the core) disassemble shows that getpid() starts with int3.

But I guess you meant something else...

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#169 lore raw

Re: [PATCH 13/X] uprobes: introduce UTASK_SSTEP_TRAPPED logic

Quoted 6 lines · Oleg Nesterov
Quoted 4 lines · Ananth N Mavinakayanahalli

Thinking further on this, in the normal 'running gdb on a core' case, we won't have this problem, as the binary that we point gdb to, will be a pristine one, without the uprobe int3s, right?

Quoted 8 lines

Not sure I understand.

I meant, if we have a binary with uprobes (iow, register_uprobe() installed uprobes into that file), then gdb will see int3's with or without the core. Or you can add uprobe into glibc, say you can probe getpid(). Now (again, with or without the core) disassemble shows that getpid() starts with int3.

But I guess you meant something else...

No, you are right... my inference was wrong. On a core with a uprobe with an explicit raise(SIGABRT) does show the breakpoint.

(gdb) disassemble start_thread2
Dump of assembler code for function start_thread2:
0x0000000000400831 <+0>: int3

   0x0000000000400832 <+1>:	mov    %rsp,%rbp
   0x0000000000400835 <+4>:	sub    $0x10,%rsp
   0x0000000000400839 <+8>:	mov    %rdi,-0x8(%rbp)

0x000000000040083d <+12>: callq 0x400650 <getpid@plt>

Now, I guess we need to agree on what is the acceptable behavior in the uprobes case. What's your suggestion?

Ananth

maintainer
#170 lore raw

Re: [PATCH 13/X] uprobes: introduce UTASK_SSTEP_TRAPPED logic

Quoted 6 lines · Ananth N Mavinakayanahalli

No, you are right... my inference was wrong. On a core with a uprobe with an explicit raise(SIGABRT) does show the breakpoint.

(gdb) disassemble start_thread2
Dump of assembler code for function start_thread2:

Quoted 8 lines
   0x0000000000400831 <+0>:	int3
   0x0000000000400832 <+1>:	mov    %rsp,%rbp
   0x0000000000400835 <+4>:	sub    $0x10,%rsp
   0x0000000000400839 <+8>:	mov    %rdi,-0x8(%rbp)
   0x000000000040083d <+12>:	callq  0x400650 <getpid@plt>

Now, I guess we need to agree on what is the acceptable behavior in the uprobes case. What's your suggestion?

Well, personally I think this is acceptable.

Once again, uprobes were designed to be "system wide", and each uprobe connects to the file. This int3 reflects this fact. In any case, I do not see how we can hide these int3's. Perhaps we can fool ptrace/core, but I am not sure this would be really good, this can add more confusion. And the application itself can read its .text and see int3, what can we do?

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#171 lore raw

PATCH 1/1 uprobes: uprobe_deny_signal: check __fatal_signal_pending()

In reply to Oleg Nesterov

Change uprobe_deny_signal() to check __fatal_signal_pending() along with xol_was_trapped().

Normally this is not really needed but this is safer. And this makes more clear the fact that even SIGKILL is handled via UTASK_SSTEP_TRAPPED. Once again, SIGKILL can be pending because of the core-dumping, we should not exit with regs->ip pointing to ->xol_vaddr.

---
 kernel/uprobes.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
kernel/uprobes.c +1 -1
index aa5492a..9e9d4e4 100644
@@ -1335,7 +1335,7 @@ bool uprobe_deny_signal(void)
 		clear_tsk_thread_flag(tsk, TIF_SIGPENDING);
 		spin_unlock_irq(&tsk->sighand->siglock);
 
-		if (xol_was_trapped(tsk)) {
+		if (__fatal_signal_pending(tsk) || xol_was_trapped(tsk)) {
 			utask->state = UTASK_SSTEP_TRAPPED;
 			set_tsk_thread_flag(tsk, TIF_UPROBE);
 			set_tsk_thread_flag(tsk, TIF_NOTIFY_RESUME);
Signature · 9 lines
1.5.5.1


--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>