[RFC] [PATCH 2/2] Single Step Out of Line(SSOL) assistance

Srinivasa D S srinivasa at in.ibm.com
Mon May 12 14:07:53 UTC 2008


This patch provides SSOL(Single step Out of Line) assistance to the user
processes.It contains code to copy original instruction to a separate SSOL
area. 

SSOL area:
copies of original instruction are stored in a per-process SSOL area,
which is a little VM area created on each probed process's address space.
setup_ssol_vma() mmaps "n" bytes and then sets vmflags of vma, so that
ssol area is not copied over fork and not resized with mremap.
	ssol area is divided into MAX_SSOL_SLOTS (calculated as
PAGE_SIZE/MAX_INSN_SIZE) slots and each slot stores an instruction.
alloc_ssol_slot() allocates a slot to user_bp object. 

Slot allocation algorithm:
Currently, a simple algorithm to allocate/free slots in SSOL area is used.
It searches for the free slots available in SSOL area and returns slot if
it finds one, else returns error code.
 
Even though we copy instruction to SSOL area, dealing with signal when
breakpoint is hit and implementing single stepping on SSOL area is governed
by the user and attached patch doesn't have the code for it. But patch
provides certain features that can be used by clients for smoother
implementation of SSOL, like user can make use of mutex provided in ssol_slot 
to prevent concurrent read/writes of slot area.

Since insert_user_bp()/remove_user_bp() is called from a quiesce handler 
of a thread when all of its sibling threads are quiesced, no care has been 
taken to protect the data structures. 

Restrictions
============
1) When process P1 is in STOPPED state and if another process P2 tries to
quiesce P1, utrace will run the report_quiesce() in P2 context and not in P1
context.Since creation of SSOL area in a process P1 has to be executed in 
P1 process context, We reject any attempt to create new SSOL area if
report_quiesce() is not called from same process context.

todo
====
1) Since we store an instruction per slot, we can insert maximum of
MAX_SSOL_SLOTS of breakpoints on a process. But there is a scope to increase
it further.

Signed-off-by: Srinivasa DS <srinivasa at in.ibm.com>


---
 include/linux/user_breakpoint.h |   36 +++++++++++
 init/Kconfig                    |    8 +-
 kernel/Makefile                 |    2 
 kernel/user_breakpoint.c        |   17 +++++
 kernel/user_ssol.c              |  125 
++++++++++++++++++++++++++++++++++++++++
 5 files changed, 184 insertions(+), 4 deletions(-)

Index: linux-2.6.25-rc6/kernel/Makefile
===================================================================
--- linux-2.6.25-rc6.orig/kernel/Makefile
+++ linux-2.6.25-rc6/kernel/Makefile
@@ -54,7 +54,7 @@ obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
 obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
 obj-$(CONFIG_KPROBES) += kprobes.o
 obj-$(CONFIG_UTRACE) += utrace.o
-obj-$(CONFIG_USER_BP_ASSIST) += user_breakpoint.o
+obj-$(CONFIG_USER_BP_ASSIST) += user_breakpoint.o user_ssol.o
 obj-$(CONFIG_DETECT_SOFTLOCKUP) += softlockup.o
 obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
 obj-$(CONFIG_SECCOMP) += seccomp.o
Index: linux-2.6.25-rc6/kernel/user_breakpoint.c
===================================================================
--- linux-2.6.25-rc6.orig/kernel/user_breakpoint.c
+++ linux-2.6.25-rc6/kernel/user_breakpoint.c
@@ -63,6 +63,9 @@ static struct ubp_per_process *create_ub
 		INIT_HLIST_HEAD(&ubp_proc->ubp_table[i]);
 	INIT_HLIST_NODE(&ubp_proc->hlist);
 	ubp_proc->tgid = p->tgid;
+	ubp_proc->sarea = init_ssol();
+	if (!ubp_proc->sarea)
+		return ERR_PTR(-ENOMEM);
 	spin_lock_irqsave(&ubp_process_table_lock, flags);
 	hlist_add_head(&ubp_proc->hlist,
 		&ubp_process_table[hash_long(p->tgid, BP_HASH_BITS)]);
@@ -165,6 +168,12 @@ int insert_user_bp(unsigned long vaddr, 
 
 	ubp_proc = find_ubp_process(p);
 	if (!ubp_proc) {
+		if (p->tgid != current->tgid) {
+			printk("Process %d is either already quiesced or in "
+				"stopped state and cannot initiate SSOL \n",
+								   p->tgid);
+			return -EINVAL;
+		}
 		ubp_proc = create_ubp_process(p);
 		if (IS_ERR(ubp_proc))
 			return -ENOMEM;
@@ -192,6 +201,13 @@ int insert_user_bp(unsigned long vaddr, 
 					"in %d \n", bp->vaddr, p->tgid);
 		goto bp_fail;
 	}
+
+	ret = alloc_ssol_slot(ubp_proc->sarea, bp);
+	if (ret) {
+		printk(KERN_ERR "Failed to get a slot in SSOL area \n");
+		goto bp_fail;
+	}
+
 	len = set_bp(bp, p);
 	if (len < BP_INSN_SIZE) {
 		printk(KERN_ERR "failed to insert breakpoint instruction at %d"
@@ -235,6 +251,7 @@ int remove_user_bp(unsigned long vaddr, 
 		send_sig(SIGKILL, tsk, 0);
 	}
 	hlist_del(&bp->bpt_node);
+	free_ssol_slot(bp, ubp_proc->sarea);
 	kfree(bp);
 
 	return 0;
Index: linux-2.6.25-rc6/kernel/user_ssol.c
===================================================================
--- /dev/null
+++ linux-2.6.25-rc6/kernel/user_ssol.c
@@ -0,0 +1,125 @@
+/*
+ *  Setup SSOL area.
+ *  kernel/user_ssol.c
+ *
+ * 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, 2006
+ */
+#include <linux/pagemap.h>
+#include <linux/mm.h>
+#include <linux/err.h>
+#include <linux/user_breakpoint.h>
+#include <asm/cacheflush.h>
+#include <asm/mman.h>
+
+unsigned long setup_ssol_vma(unsigned long nbytes)
+{
+	unsigned long addr;
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+
+	BUG_ON(nbytes & ~PAGE_MASK);
+
+	down_write(&mm->mmap_sem);
+
+	/* Find the end of the top mapping and skip a page.*/
+	vma = rb_entry(rb_last(&mm->mm_rb), struct vm_area_struct, vm_rb);
+	addr = vma->vm_end + PAGE_SIZE;
+	addr = do_mmap_pgoff(NULL, addr, nbytes, PROT_EXEC,
+				 MAP_PRIVATE|MAP_ANONYMOUS, 0);
+	if (addr & ~PAGE_MASK) {
+		up_write(&mm->mmap_sem);
+		printk(KERN_ERR "Breakpoint failed to allocate a vma for"
+				"pid %d for SSOL.\n", current->pid);
+		return (addr | PAGE_MASK);
+	}
+
+	vma = find_vma(mm, addr);
+	if (!vma) {
+		up_write(&mm->mmap_sem);
+		return -ENOMEM;
+	}
+	/* avoid vma copy on fork() and don't expand when mremap() */
+	vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND;
+
+	up_write(&mm->mmap_sem);
+	return addr;
+}
+
+struct ssol_area *init_ssol(void)
+{
+	struct ssol_area *area;
+	struct ssol_slot *slot;
+	int i;
+	char *slot_addr;
+
+	area = kzalloc(sizeof(struct ssol_area), GFP_USER);
+	if (!area)
+		return NULL;
+
+	area->insn_area = (user_opcode_t *) setup_ssol_vma(PAGE_SIZE);
+	if (IS_ERR(area->insn_area))
+		return NULL;
+
+	area->nslots = MAX_SSOL_SLOTS;
+	area->slots = kzalloc(sizeof(*slot) * area->nslots, GFP_USER);
+	if (!area->slots) {
+		area->insn_area = ERR_PTR(-ENOMEM);
+		return NULL;
+	}
+	slot_addr = (char *) area->insn_area;
+	for (i = 0; i < area->nslots; i++) {
+		slot = &area->slots[i];
+		slot->occupied = false;
+		slot->insn = (user_opcode_t *) slot_addr;
+		slot_addr += MAX_UINSN_BYTES;
+	}
+	return area;
+}
+
+int alloc_ssol_slot(struct ssol_area *area, struct ubp *bp)
+{
+	struct ssol_slot *slot;
+	int i, len;
+
+	for (i = 0; i < area->nslots; i++) {
+		slot = &area->slots[i];
+		if (!slot->occupied)
+			break;
+	}
+	if (i == area->nslots)
+		return -EFAULT;
+
+	bp->slot = slot;
+	slot->occupied = true;
+	len = access_process_vm(current, (unsigned long)slot->insn,
+				bp->insn, MAX_UINSN_BYTES, 1);
+	if (unlikely(len < MAX_UINSN_BYTES)) {
+		printk(KERN_ERR "Failed to copy instruction at %#lx"
+			" to SSOL area (%#lx)\n", bp->vaddr,
+				(unsigned long) area->slots);
+		return -EFAULT;
+	}
+	return 0;
+}
+
+void free_ssol_slot(struct ubp *bp, struct ssol_area *area)
+{
+
+	if (bp->slot)
+		bp->slot->occupied = false;
+}
+
Index: linux-2.6.25-rc6/include/linux/user_breakpoint.h
===================================================================
--- linux-2.6.25-rc6.orig/include/linux/user_breakpoint.h
+++ linux-2.6.25-rc6/include/linux/user_breakpoint.h
@@ -24,15 +24,18 @@
 #include <linux/types.h>
 #include <asm/user_breakpoint.h>
 #include <asm/uaccess.h>
+#include <asm/kprobes.h>
 
 #define BP_HASH_BITS 5
 #define BP_TABLE_SIZE (1 << BP_HASH_BITS)
+#define MAX_SSOL_SLOTS	(PAGE_SIZE/MAX_INSN_SIZE)
 
 typedef unsigned int user_opcode_t;
 
 struct ubp {
 	user_opcode_t opcode;
 	user_opcode_t insn[MAX_UINSN_BYTES / sizeof(user_opcode_t)];
+	struct ssol_slot *slot;
 	unsigned long vaddr;
 	struct hlist_node bpt_node;
 };
@@ -44,6 +47,39 @@ struct ubp_per_process {
 	struct hlist_head ubp_table[BP_TABLE_SIZE];
 	struct hlist_node hlist;   /* node for global user breakpoints table */
 	pid_t tgid;
+	struct ssol_area *sarea;
 };
+/*
+ * SSOL provides an opportunity to do single stepping of an out of line
+ * copy of the instruction. This is done by storing the original instrction
+ * in a separate area within the process's address space and single stepping
+ * on it.
+ */
+
+/*
+ * SSOL area represents the mmaped area in the process address space for
+ * storing the original instructions at breakpoint location.
+ */
+struct ssol_area {
+	__user user_opcode_t *insn_area;        /* instructions in SSOL area */
+	int nslots;
+	struct ssol_slot *slots;
+};
+
+/*
+ * ssol_slot represents each instruction in the SSOL area
+ */
+struct ssol_slot {
+	__user user_opcode_t  *insn;    /* instruction in the SSOL area */
+	bool occupied;  /* bool to indicate the status of slot */
+	/*
+	 * mutex to prevent concurrent read/write on a slot
+	 */
+	struct mutex slot_mutex;
+};
+
+struct ssol_area *init_ssol(void);
+int alloc_ssol_slot(struct ssol_area *area, struct ubp *bp);
+void free_ssol_slot(struct ubp *bp, struct ssol_area *area);
 
 #endif     /*_USER_BREAKPOINT_H */
Index: linux-2.6.25-rc6/init/Kconfig
===================================================================
--- linux-2.6.25-rc6.orig/init/Kconfig
+++ linux-2.6.25-rc6/init/Kconfig
@@ -894,9 +894,11 @@ config USER_BP_ASSIST
 	depends on  UTRACE
 	help
 	  This option provides infrastructure to insert/remove
-	  breakpoints on user processes. UTRACE exploiters can
-	  make use of this mechanism for insertion/removal of
-	  breakpoints.
+	  breakpoints  on user processes. It also offers assistance
+	  in single stepping out of line (SSOL) on the original
+	  instruction by copying the original instruction to a
+    	  separate SSOL area. UTRACE exploiters can make use of
+	  this mechanism for insertion/removal of breakpoints.
 	  Say Y here if you want to enable USER_BP_ASSIST.
 endmenu
 




More information about the utrace-devel mailing list