patch-2.2.19 linux/arch/arm/kernel/ptrace.c
Next file: linux/arch/arm/kernel/signal.c
Previous file: linux/arch/arm/kernel/process.c
Back to the patch index
Back to the overall index
- Lines: 823
- Date:
Sun Mar 25 11:37:29 2001
- Orig file:
v2.2.18/arch/arm/kernel/ptrace.c
- Orig date:
Sun Mar 25 11:28:16 2001
diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.18/arch/arm/kernel/ptrace.c linux/arch/arm/kernel/ptrace.c
@@ -16,6 +16,9 @@
#include <asm/pgtable.h>
#include <asm/system.h>
+
+#define REG_PC 15
+#define REG_PSR 16
/*
* does not yet catch signals sent when the child dies.
* in exit.c or in signal.c.
@@ -25,11 +28,12 @@
* Breakpoint SWI instruction: SWI &9F0001
*/
#define BREAKINST 0xef9f0001
-#define PTRACE_GETREGS 12
-#define PTRACE_SETREGS 13
-#define PTRACE_GETFPREGS 14
-#define PTRACE_SETFPREGS 15
+/*
+ * Get the address of the live pt_regs for the specified task.
+ * These are saved onto the top kernel stack when the process
+ * is not running.
+ */
static inline struct pt_regs *
get_user_regs(struct task_struct *task)
{
@@ -299,38 +303,42 @@
return 0;
}
+#define write_tsk_long(chld, addr, val) write_long((chld), (addr), (val))
+#define read_tsk_long(chld, addr, val) read_long((chld), (addr), (val))
+
/*
* Get value of register `rn' (in the instruction)
*/
-static unsigned long ptrace_getrn (struct task_struct *child, unsigned long insn)
+static unsigned long
+ptrace_getrn(struct task_struct *child, unsigned long insn)
{
unsigned int reg = (insn >> 16) & 15;
unsigned long val;
+ val = get_stack_long(child, reg);
if (reg == 15)
- val = pc_pointer (get_stack_long (child, reg));
- else
- val = get_stack_long (child, reg);
+ val = pc_pointer(val + 8);
+
-printk ("r%02d=%08lX ", reg, val);
return val;
}
/*
* Get value of operand 2 (in an ALU instruction)
*/
-static unsigned long ptrace_getaluop2 (struct task_struct *child, unsigned long insn)
+static unsigned long
+ptrace_getaluop2(struct task_struct *child, unsigned long insn)
{
unsigned long val;
int shift;
int type;
-printk ("op2=");
+
if (insn & 1 << 25) {
val = insn & 255;
shift = (insn >> 8) & 15;
type = 3;
-printk ("(imm)");
+
} else {
val = get_stack_long (child, insn & 15);
@@ -340,9 +348,9 @@
shift = (insn >> 7) & 31;
type = (insn >> 5) & 3;
-printk ("(r%02ld)", insn & 15);
+
}
-printk ("sh%dx%d", type, shift);
+
switch (type) {
case 0: val <<= shift; break;
case 1: val >>= shift; break;
@@ -350,27 +358,28 @@
val = (((signed long)val) >> shift);
break;
case 3:
- __asm__ __volatile__("mov %0, %0, ror %1" : "=r" (val) : "0" (val), "r" (shift));
+ val = (val >> shift) | (val << (32 - shift));
break;
}
-printk ("=%08lX ", val);
+
return val;
}
/*
* Get value of operand 2 (in a LDR instruction)
*/
-static unsigned long ptrace_getldrop2 (struct task_struct *child, unsigned long insn)
+static unsigned long
+ptrace_getldrop2(struct task_struct *child, unsigned long insn)
{
unsigned long val;
int shift;
int type;
- val = get_stack_long (child, insn & 15);
+ val = get_stack_long(child, insn & 15);
shift = (insn >> 7) & 31;
type = (insn >> 5) & 3;
-printk ("op2=r%02ldsh%dx%d", insn & 15, shift, type);
+
switch (type) {
case 0: val <<= shift; break;
case 1: val >>= shift; break;
@@ -378,115 +387,88 @@
val = (((signed long)val) >> shift);
break;
case 3:
- __asm__ __volatile__("mov %0, %0, ror %1" : "=r" (val) : "0" (val), "r" (shift));
+ val = (val >> shift) | (val << (32 - shift));
break;
}
-printk ("=%08lX ", val);
+
return val;
}
-#undef pc_pointer
-#define pc_pointer(x) ((x) & 0x03fffffc)
-int ptrace_set_bpt (struct task_struct *child)
-{
- unsigned long insn, pc, alt;
- int i, nsaved = 0, res;
-
- pc = pc_pointer (get_stack_long (child, 15/*REG_PC*/));
-
- res = read_long (child, pc, &insn);
- if (res < 0)
- return res;
-
- child->tss.debug[nsaved++] = alt = pc + 4;
-printk ("ptrace_set_bpt: insn=%08lX pc=%08lX ", insn, pc);
- switch (insn & 0x0e100000) {
+
+static unsigned long
+get_branch_address(struct task_struct *child, unsigned long pc, unsigned long insn)
+{
+ unsigned long alt = 0;
+
+ switch (insn & 0x0e000000) {
case 0x00000000:
- case 0x00100000:
- case 0x02000000:
- case 0x02100000: /* data processing */
- printk ("data ");
- switch (insn & 0x01e0f000) {
- case 0x0000f000:
- alt = ptrace_getrn(child, insn) & ptrace_getaluop2(child, insn);
- break;
- case 0x0020f000:
- alt = ptrace_getrn(child, insn) ^ ptrace_getaluop2(child, insn);
- break;
- case 0x0040f000:
- alt = ptrace_getrn(child, insn) - ptrace_getaluop2(child, insn);
- break;
- case 0x0060f000:
- alt = ptrace_getaluop2(child, insn) - ptrace_getrn(child, insn);
- break;
- case 0x0080f000:
- alt = ptrace_getrn(child, insn) + ptrace_getaluop2(child, insn);
- break;
- case 0x00a0f000:
- alt = ptrace_getrn(child, insn) + ptrace_getaluop2(child, insn) +
- (get_stack_long (child, 16/*REG_PSR*/) & CC_C_BIT ? 1 : 0);
- break;
- case 0x00c0f000:
- alt = ptrace_getrn(child, insn) - ptrace_getaluop2(child, insn) +
- (get_stack_long (child, 16/*REG_PSR*/) & CC_C_BIT ? 1 : 0);
- break;
- case 0x00e0f000:
- alt = ptrace_getaluop2(child, insn) - ptrace_getrn(child, insn) +
- (get_stack_long (child, 16/*REG_PSR*/) & CC_C_BIT ? 1 : 0);
- break;
- case 0x0180f000:
- alt = ptrace_getrn(child, insn) | ptrace_getaluop2(child, insn);
- break;
- case 0x01a0f000:
- alt = ptrace_getaluop2(child, insn);
- break;
- case 0x01c0f000:
- alt = ptrace_getrn(child, insn) & ~ptrace_getaluop2(child, insn);
- break;
- case 0x01e0f000:
- alt = ~ptrace_getaluop2(child, insn);
+ case 0x02000000: {
+ /*
+ * data processing
+ */
+ long aluop1, aluop2, ccbit;
+
+ if ((insn & 0xf000) != 0xf000)
break;
+
+
+
+ aluop1 = ptrace_getrn(child, insn);
+ aluop2 = ptrace_getaluop2(child, insn);
+ ccbit = get_stack_long(child, REG_PSR) & CC_C_BIT ? 1 : 0;
+
+ switch (insn & 0x01e00000) {
+ case 0x00000000: alt = aluop1 & aluop2; break;
+ case 0x00200000: alt = aluop1 ^ aluop2; break;
+ case 0x00400000: alt = aluop1 - aluop2; break;
+ case 0x00600000: alt = aluop2 - aluop1; break;
+ case 0x00800000: alt = aluop1 + aluop2; break;
+ case 0x00a00000: alt = aluop1 + aluop2 + ccbit; break;
+ case 0x00c00000: alt = aluop1 - aluop2 + ccbit; break;
+ case 0x00e00000: alt = aluop2 - aluop1 + ccbit; break;
+ case 0x01800000: alt = aluop1 | aluop2; break;
+ case 0x01a00000: alt = aluop2; break;
+ case 0x01c00000: alt = aluop1 & ~aluop2; break;
+ case 0x01e00000: alt = ~aluop2; break;
}
break;
+ }
+
+ case 0x04000000:
+ case 0x06000000:
+ /*
+ * ldr
+ */
+ if ((insn & 0x0010f000) == 0x0010f000) {
+ unsigned long base;
- case 0x04100000: /* ldr */
- if ((insn & 0xf000) == 0xf000) {
-printk ("ldr ");
- alt = ptrace_getrn(child, insn);
+ base = ptrace_getrn(child, insn);
if (insn & 1 << 24) {
- if (insn & 1 << 23)
- alt += ptrace_getldrop2 (child, insn);
+ long aluop2;
+
+ if (insn & 0x02000000)
+ aluop2 = ptrace_getldrop2(child, insn);
else
- alt -= ptrace_getldrop2 (child, insn);
- }
- if (read_long (child, alt, &alt) < 0)
- alt = pc + 4; /* not valid */
- else
- alt = pc_pointer (alt);
- }
- break;
+ aluop2 = insn & 0xfff;
- case 0x06100000: /* ldr imm */
- if ((insn & 0xf000) == 0xf000) {
-printk ("ldrimm ");
- alt = ptrace_getrn(child, insn);
- if (insn & 1 << 24) {
if (insn & 1 << 23)
- alt += insn & 0xfff;
+ base += aluop2;
else
- alt -= insn & 0xfff;
+ base -= aluop2;
}
- if (read_long (child, alt, &alt) < 0)
- alt = pc + 4; /* not valid */
- else
- alt = pc_pointer (alt);
+
+ if (read_tsk_long(child, base, &alt) == 0)
+ alt = pc_pointer(alt);
}
break;
- case 0x08100000: /* ldm */
- if (insn & (1 << 15)) {
+ case 0x08000000:
+ /*
+ * ldm
+ */
+ if ((insn & 0x00108000) == 0x00108000) {
unsigned long base;
- int nr_regs;
-printk ("ldm ");
+ unsigned int nr_regs;
+
if (insn & (1 << 23)) {
nr_regs = insn & 65535;
@@ -506,23 +488,23 @@
nr_regs = 0;
}
- base = ptrace_getrn (child, insn);
+ base = ptrace_getrn(child, insn);
- if (read_long (child, base + nr_regs, &alt) < 0)
- alt = pc + 4; /* not valid */
- else
- alt = pc_pointer (alt);
+ if (read_tsk_long(child, base + nr_regs, &alt) == 0)
+ alt = pc_pointer(alt);
break;
}
break;
- case 0x0a000000:
- case 0x0a100000: { /* bl or b */
+ case 0x0a000000: {
+ /*
+ * bl or b
+ */
signed long displ;
-printk ("b/bl ");
+
/* It's a branch/branch link: instead of trying to
* figure out whether the branch will be taken or not,
- * we'll put a breakpoint at either location. This is
+ * we'll put a breakpoint at both locations. This is
* simpler, more reliable, and probably not a whole lot
* slower than the alternative approach of emulating the
* branch.
@@ -534,196 +516,230 @@
}
break;
}
-printk ("=%08lX\n", alt);
- if (alt != pc + 4)
- child->tss.debug[nsaved++] = alt;
- for (i = 0; i < nsaved; i++) {
- res = read_long (child, child->tss.debug[i], &insn);
- if (res >= 0) {
- child->tss.debug[i + 2] = insn;
- res = write_long (child, child->tss.debug[i], BREAKINST);
- }
- if (res < 0) {
- child->tss.debug[4] = 0;
- return res;
+ return alt;
+}
+
+static int
+add_breakpoint(struct task_struct *child, struct debug_info *dbg, unsigned long addr)
+{
+ int nr = dbg->nsaved;
+ int res = -EINVAL;
+
+ if (nr < 2) {
+ res = read_tsk_long(child, addr, &dbg->bp[nr].insn);
+ if (res == 0)
+ res = write_tsk_long(child, addr, BREAKINST);
+
+ if (res == 0) {
+ dbg->bp[nr].address = addr;
+ dbg->nsaved += 1;
}
+ } else
+ printk(KERN_DEBUG "add_breakpoint: too many breakpoints\n");
+
+ return res;
+}
+
+int ptrace_set_bpt(struct task_struct *child)
+{
+ unsigned long insn, pc;
+ int res;
+
+ pc = pc_pointer(get_stack_long(child, REG_PC));
+
+ res = read_long(child, pc, &insn);
+ if (!res) {
+ struct debug_info *dbg = &child->tss.debug;
+ unsigned long alt;
+
+ dbg->nsaved = 0;
+
+ alt = get_branch_address(child, pc, insn);
+
+ if (alt)
+ res = add_breakpoint(child, dbg, alt);
+
+ /*
+ * Note that we ignore the result of setting the above
+ * breakpoint since it may fail. When it does, this is
+ * not so much an error, but a forewarning that we will
+ * be receiving a prefetch abort shortly.
+ *
+ * If we don't set this breakpoint here, then we can
+ * loose control of the thread during single stepping.
+ */
+ if (!alt || predicate(insn) != PREDICATE_ALWAYS)
+ res = add_breakpoint(child, dbg, pc + 4);
}
- child->tss.debug[4] = nsaved;
- return 0;
+
+ return res;
}
-/* Ensure no single-step breakpoint is pending. Returns non-zero
+/*
+ * Ensure no single-step breakpoint is pending. Returns non-zero
* value if child was being single-stepped.
*/
int ptrace_cancel_bpt (struct task_struct *child)
{
- int i, nsaved = child->tss.debug[4];
+ struct debug_info *dbg = &child->tss.debug;
+ int i, nsaved = dbg->nsaved;
- child->tss.debug[4] = 0;
+ dbg->nsaved = 0;
if (nsaved > 2) {
- printk ("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved);
+ printk("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved);
nsaved = 2;
}
- for (i = 0; i < nsaved; i++)
- write_long (child, child->tss.debug[i], child->tss.debug[i + 2]);
+
+ for (i = 0; i < nsaved; i++) {
+ unsigned long tmp;
+
+ read_tsk_long(child, dbg->bp[i].address, &tmp);
+ if (tmp != BREAKINST)
+ printk(KERN_ERR "ptrace_cancel_bpt: weirdness\n");
+ write_tsk_long(child, dbg->bp[i].address, dbg->bp[i].insn);
+ }
return nsaved != 0;
}
-asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
+static int do_ptrace(int request, struct task_struct *child, long addr, long data)
{
- struct task_struct *child;
+ unsigned long tmp;
int ret;
- lock_kernel();
- ret = -EPERM;
- if (request == PTRACE_TRACEME) {
- /* are we already being traced? */
- if (current->flags & PF_PTRACED)
- goto out;
- /* set the ptrace bit in the process flags. */
- current->flags |= PF_PTRACED;
- ret = 0;
- goto out;
- }
- if (pid == 1) /* you may not mess with init */
- goto out;
- ret = -ESRCH;
- if (!(child = find_task_by_pid(pid)))
- goto out;
- ret = -EPERM;
- if (request == PTRACE_ATTACH) {
- if (child == current)
- goto out;
- if ((!child->dumpable ||
- (current->uid != child->euid) ||
- (current->uid != child->suid) ||
- (current->uid != child->uid) ||
- (current->gid != child->egid) ||
- (current->gid != child->sgid) ||
- (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
- goto out;
- /* the same process cannot be attached many times */
- if (child->flags & PF_PTRACED)
- goto out;
- child->flags |= PF_PTRACED;
- if (child->p_pptr != current) {
- REMOVE_LINKS(child);
- child->p_pptr = current;
- SET_LINKS(child);
- }
- send_sig(SIGSTOP, child, 1);
- ret = 0;
- goto out;
- }
- ret = -ESRCH;
- if (!(child->flags & PF_PTRACED))
- goto out;
- if (child->state != TASK_STOPPED) {
- if (request != PTRACE_KILL)
- goto out;
- }
- if (child->p_pptr != current)
- goto out;
-
switch (request) {
- case PTRACE_PEEKTEXT: /* read word at location addr. */
- case PTRACE_PEEKDATA: {
- unsigned long tmp;
-
- ret = read_long(child, addr, &tmp);
- if (ret >= 0)
- ret = put_user(tmp, (unsigned long *)data);
- goto out;
- }
-
- case PTRACE_PEEKUSR: { /* read the word at location addr in the USER area. */
- unsigned long tmp;
+ /*
+ * read word at location "addr" in the child process.
+ */
+ case PTRACE_PEEKTEXT:
+ case PTRACE_PEEKDATA:
+ ret = read_tsk_long(child, addr, &tmp);
+ if (!ret)
+ ret = put_user(tmp, (unsigned long *) data);
+ break;
+ /*
+ * read the word at location "addr" in the user registers.
+ */
+ case PTRACE_PEEKUSR:
ret = -EIO;
if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
- goto out;
+ break;
tmp = 0; /* Default return condition */
- if (addr < sizeof (struct pt_regs))
+ if (addr < sizeof(struct pt_regs))
tmp = get_stack_long(child, (int)addr >> 2);
ret = put_user(tmp, (unsigned long *)data);
- goto out;
- }
+ break;
- case PTRACE_POKETEXT: /* write the word at location addr. */
+ /*
+ * write the word at location addr.
+ */
+ case PTRACE_POKETEXT:
case PTRACE_POKEDATA:
- ret = write_long(child,addr,data);
- goto out;
+ ret = write_tsk_long(child, addr, data);
+ break;
- case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
+ /*
+ * write the word at location addr in the user registers.
+ */
+ case PTRACE_POKEUSR:
ret = -EIO;
if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
- goto out;
+ break;
- if (addr < sizeof (struct pt_regs))
+ if (addr < sizeof(struct pt_regs))
ret = put_stack_long(child, (int)addr >> 2, data);
- goto out;
+ break;
- case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
- case PTRACE_CONT: /* restart after signal. */
+ /*
+ * continue/restart and stop at next (return from) syscall
+ */
+ case PTRACE_SYSCALL:
+ case PTRACE_CONT:
ret = -EIO;
if ((unsigned long) data > _NSIG)
- goto out;
+ break;
if (request == PTRACE_SYSCALL)
child->flags |= PF_TRACESYS;
else
child->flags &= ~PF_TRACESYS;
child->exit_code = data;
- wake_up_process (child);
/* make sure single-step breakpoint is gone. */
- ptrace_cancel_bpt (child);
+ ptrace_cancel_bpt(child);
+ wake_up_process(child);
ret = 0;
- goto out;
+ break;
- /* make the child exit. Best I can do is send it a sigkill.
+ /*
+ * make the child exit. Best I can do is send it a sigkill.
* perhaps it should be put in the status that it wants to
* exit.
*/
case PTRACE_KILL:
- if (child->state == TASK_ZOMBIE) /* already dead */
- return 0;
- wake_up_process (child);
+ /* already dead */
+ ret = 0;
+ if (child->state == TASK_ZOMBIE)
+ break;
child->exit_code = SIGKILL;
/* make sure single-step breakpoint is gone. */
- ptrace_cancel_bpt (child);
+ ptrace_cancel_bpt(child);
+ wake_up_process(child);
ret = 0;
- goto out;
+ break;
- case PTRACE_SINGLESTEP: /* execute single instruction. */
+ /*
+ * execute single instruction.
+ */
+ case PTRACE_SINGLESTEP:
ret = -EIO;
if ((unsigned long) data > _NSIG)
- goto out;
- child->tss.debug[4] = -1;
+ break;
+ child->tss.debug.nsaved = -1;
child->flags &= ~PF_TRACESYS;
- wake_up_process(child);
child->exit_code = data;
/* give it a chance to run. */
+ wake_up_process(child);
ret = 0;
- goto out;
-
- case PTRACE_GETREGS:
- { /* Get all gp regs from the child. */
- unsigned char *stack;
+ break;
+ /*
+ * detach a process that was attached.
+ */
+ case PTRACE_DETACH:
+ ret = -EIO;
+ if ((unsigned long) data > _NSIG)
+ break;
+ child->flags &= ~(PF_PTRACED|PF_TRACESYS);
+ child->exit_code = data;
+ REMOVE_LINKS(child);
+ child->p_pptr = child->p_opptr;
+ SET_LINKS(child);
+ /* make sure single-step breakpoint is gone. */
+ ptrace_cancel_bpt (child);
+ wake_up_process (child);
ret = 0;
- stack = (unsigned char *)((unsigned long)child + 8192 - sizeof(struct pt_regs));
- if (copy_to_user((void *)data, stack,
+ break;
+
+ /*
+ * Get all gp regs from the child.
+ */
+ case PTRACE_GETREGS: {
+ struct pt_regs *regs = get_user_regs(child);
+
+ ret = 0;
+ if (copy_to_user((void *)data, regs,
sizeof(struct pt_regs)))
ret = -EFAULT;
- goto out;
- };
+ break;
+ }
- /* Set all gp regs in the child. */
- case PTRACE_SETREGS:
- {
+ /*
+ * Set all gp regs in the child.
+ */
+ case PTRACE_SETREGS: {
struct pt_regs newregs;
ret = -EFAULT;
@@ -737,44 +753,103 @@
ret = 0;
}
}
- goto out;
+ break;
}
+ /*
+ * Get the child FPU state.
+ */
case PTRACE_GETFPREGS:
- /* Get the child FPU state. */
- ret = 0;
- if (copy_to_user((void *)data, &child->tss.fpstate,
- sizeof(struct user_fp)))
- ret = -EFAULT;
- goto out;
-
- case PTRACE_SETFPREGS:
- /* Set the child FPU state. */
- ret = 0;
- if (copy_from_user(&child->tss.fpstate, (void *)data,
- sizeof(struct user_fp)))
- ret = -EFAULT;
- goto out;
+ ret = -EIO;
+ if (!access_ok(VERIFY_WRITE, (void *)data, sizeof(struct user_fp)))
+ break;
+
+ /* we should check child->used_math here */
+ ret = __copy_to_user((void *)data, &child->tss.fpstate,
+ sizeof(struct user_fp)) ? -EFAULT : 0;
+ break;
- case PTRACE_DETACH: /* detach a process that was attached. */
+ /*
+ * Set the child FPU state.
+ */
+ case PTRACE_SETFPREGS:
ret = -EIO;
- if ((unsigned long) data > _NSIG)
- goto out;
- child->flags &= ~(PF_PTRACED|PF_TRACESYS);
- wake_up_process (child);
- child->exit_code = data;
- REMOVE_LINKS(child);
- child->p_pptr = child->p_opptr;
- SET_LINKS(child);
- /* make sure single-step breakpoint is gone. */
- ptrace_cancel_bpt (child);
- ret = 0;
- goto out;
+ if (!access_ok(VERIFY_READ, (void *)data, sizeof(struct user_fp)))
+ break;
+
+ child->used_math = 1;
+ ret = __copy_from_user(&child->tss.fpstate, (void *)data,
+ sizeof(struct user_fp)) ? -EFAULT : 0;
+ break;
default:
ret = -EIO;
+ break;
+ }
+
+ return ret;
+}
+
+asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
+{
+ struct task_struct *child;
+ int ret;
+
+ lock_kernel();
+ ret = -EPERM;
+ if (request == PTRACE_TRACEME) {
+ /* are we already being traced? */
+ if (current->flags & PF_PTRACED)
goto out;
+ /* set the ptrace bit in the process flags. */
+ current->flags |= PF_PTRACED;
+ ret = 0;
+ goto out;
}
+ ret = -ESRCH;
+ if (!(child = find_task_by_pid(pid)))
+ goto out;
+
+ ret = -EPERM;
+ if (pid == 1) /* you may not mess with init */
+ goto out;
+
+ if (request == PTRACE_ATTACH) {
+ if (child == current)
+ goto out;
+ if ((!child->dumpable ||
+ (current->uid != child->euid) ||
+ (current->uid != child->suid) ||
+ (current->uid != child->uid) ||
+ (current->gid != child->egid) ||
+ (current->gid != child->sgid) ||
+ (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
+ goto out;
+ /* the same process cannot be attached many times */
+ if (child->flags & PF_PTRACED)
+ goto out;
+ child->flags |= PF_PTRACED;
+
+ if (child->p_pptr != current) {
+ REMOVE_LINKS(child);
+ child->p_pptr = current;
+ SET_LINKS(child);
+ }
+
+ send_sig(SIGSTOP, child, 1);
+ ret = 0;
+ goto out;
+ }
+ ret = -ESRCH;
+ if (!(child->flags & PF_PTRACED))
+ goto out;
+ if (child->state != TASK_STOPPED && request != PTRACE_KILL)
+ goto out;
+ if (child->p_pptr != current)
+ goto out;
+
+ ret = do_ptrace(request, child, addr, data);
+
out:
unlock_kernel();
return ret;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)