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 <[EMAIL PROTECTED]> --- 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