ptrace bug or feature?

Kostik Belousov kostikbel at gmail.com
Sun Jan 17 17:43:00 UTC 2010


On Sat, Jan 16, 2010 at 09:01:37PM +0200, Ali Polatel wrote:
> Hey everyone,
> 
> Problem: ptrace's PT_SETREGS request can't alter system calls.
> Code: http://alip.github.com/code/ptrace-freebsd-deny.c
> Expected: The file foo.bar shouldn't be created.
> Got: The file is created. Other efforts like replacing
> PT_GETREGS/PT_SETREGS calls with PT_KILL doesn't help, the file is
> created nevertheless.
> 
> I'm inclined to call this a bug but I can't be sure.
> Any comments appreciated.
> TIA

It may be a missed feature, not a bug. There is obvious hack value
in ability to modify syscall arguments from the debugger.

Do you know whether other operating systems allow this ?

I was around this code today, and wrote the patch for i386, amd64
and ia32 on amd64. Other architectures need to be handled.

diff --git a/sys/amd64/amd64/trap.c b/sys/amd64/amd64/trap.c
index df301d1..bd7ee63 100644
--- a/sys/amd64/amd64/trap.c
+++ b/sys/amd64/amd64/trap.c
@@ -885,95 +885,131 @@ dblfault_handler(struct trapframe *frame)
 	panic("double fault");
 }
 
-/*
- *	syscall -	system call request C handler
- *
- *	A system call is essentially treated as a trap.
- */
-void
-syscall(struct trapframe *frame)
-{
-	caddr_t params;
+struct syscall_args {
+	u_int code;
 	struct sysent *callp;
-	struct thread *td = curthread;
-	struct proc *p = td->td_proc;
-	register_t orig_tf_rflags;
-	int error;
-	int narg;
 	register_t args[8];
 	register_t *argp;
-	u_int code;
-	int reg, regcnt;
-	ksiginfo_t ksi;
-
-	PCPU_INC(cnt.v_syscall);
+	int narg;
+};
 
-#ifdef DIAGNOSTIC
-	if (ISPL(frame->tf_cs) != SEL_UPL) {
-		panic("syscall");
-		/* NOT REACHED */
-	}
-#endif
+static int
+fetch_syscall_args(struct thread *td, struct syscall_args *sa)
+{
+	struct proc *p;
+	struct trapframe *frame;
+	caddr_t params;
+	int reg, regcnt, error;
 
+	p = td->td_proc;
+	frame = td->td_frame;
 	reg = 0;
 	regcnt = 6;
-	td->td_pticks = 0;
-	td->td_frame = frame;
-	if (td->td_ucred != p->p_ucred) 
-		cred_update_thread(td);
+
 	params = (caddr_t)frame->tf_rsp + sizeof(register_t);
-	code = frame->tf_rax;
-	orig_tf_rflags = frame->tf_rflags;
+	sa->code = frame->tf_rax;
 
 	if (p->p_sysent->sv_prepsyscall) {
-		(*p->p_sysent->sv_prepsyscall)(frame, (int *)args, &code, &params);
+		(*p->p_sysent->sv_prepsyscall)(frame, (int *)sa->args,
+		    &sa->code, &params);
 	} else {
-		if (code == SYS_syscall || code == SYS___syscall) {
-			code = frame->tf_rdi;
+		if (sa->code == SYS_syscall || sa->code == SYS___syscall) {
+			sa->code = frame->tf_rdi;
 			reg++;
 			regcnt--;
 		}
 	}
-
  	if (p->p_sysent->sv_mask)
- 		code &= p->p_sysent->sv_mask;
+ 		sa->code &= p->p_sysent->sv_mask;
 
- 	if (code >= p->p_sysent->sv_size)
- 		callp = &p->p_sysent->sv_table[0];
+ 	if (sa->code >= p->p_sysent->sv_size)
+ 		sa->callp = &p->p_sysent->sv_table[0];
   	else
- 		callp = &p->p_sysent->sv_table[code];
+ 		sa->callp = &p->p_sysent->sv_table[sa->code];
 
-	narg = callp->sy_narg;
-	KASSERT(narg <= sizeof(args) / sizeof(args[0]),
+	sa->narg = sa->callp->sy_narg;
+	KASSERT(sa->narg <= sizeof(sa->args) / sizeof(sa->args[0]),
 	    ("Too many syscall arguments!"));
 	error = 0;
-	argp = &frame->tf_rdi;
-	argp += reg;
-	bcopy(argp, args, sizeof(args[0]) * regcnt);
-	if (narg > regcnt) {
+	sa->argp = &frame->tf_rdi;
+	sa->argp += reg;
+	bcopy(sa->argp, sa->args, sizeof(sa->args[0]) * regcnt);
+	if (sa->narg > regcnt) {
 		KASSERT(params != NULL, ("copyin args with no params!"));
-		error = copyin(params, &args[regcnt],
-	    		(narg - regcnt) * sizeof(args[0]));
+		error = copyin(params, &sa->args[regcnt],
+	    	    (sa->narg - regcnt) * sizeof(sa->args[0]));
 	}
-	argp = &args[0];
+	sa->argp = &sa->args[0];
 
+	/*
+	 * This may result in two records if debugger modified
+	 * registers or memory during sleep at stop/ptrace point.
+	 */
 #ifdef KTRACE
 	if (KTRPOINT(td, KTR_SYSCALL))
-		ktrsyscall(code, narg, argp);
+		ktrsyscall(sa->code, sa->narg, sa->argp);
 #endif
+	return (error);
+}
 
-	CTR4(KTR_SYSC, "syscall enter thread %p pid %d proc %s code %d", td,
-	    td->td_proc->p_pid, td->td_name, code);
+/*
+ *	syscall -	system call request C handler
+ *
+ *	A system call is essentially treated as a trap.
+ */
+void
+syscall(struct trapframe *frame)
+{
+	struct thread *td;
+	struct proc *p;
+	struct syscall_args sa;
+	register_t orig_tf_rflags;
+	int error;
+	ksiginfo_t ksi;
 
+	PCPU_INC(cnt.v_syscall);
+	td = curthread;
+	p = td->td_proc;
 	td->td_syscalls++;
 
+#ifdef DIAGNOSTIC
+	if (ISPL(frame->tf_cs) != SEL_UPL) {
+		panic("syscall");
+		/* NOT REACHED */
+	}
+#endif
+
+	td->td_pticks = 0;
+	td->td_frame = frame;
+	if (td->td_ucred != p->p_ucred) 
+		cred_update_thread(td);
+	orig_tf_rflags = frame->tf_rflags;
+	if (p->p_flag & P_TRACED) {
+		PROC_LOCK(p);
+		td->td_dbgflags &= ~TDB_USERWR;
+		PROC_UNLOCK(p);
+	}
+	error = fetch_syscall_args(td, &sa);
+
+	CTR4(KTR_SYSC, "syscall enter thread %p pid %d proc %s code %d", td,
+	    td->td_proc->p_pid, td->td_name, sa.code);
+
 	if (error == 0) {
 		td->td_retval[0] = 0;
 		td->td_retval[1] = frame->tf_rdx;
 
-		STOPEVENT(p, S_SCE, narg);
-
+		STOPEVENT(p, S_SCE, sa.narg);
 		PTRACESTOP_SC(p, td, S_PT_SCE);
+		if (td->td_dbgflags & TDB_USERWR) {
+			/*
+			 * Reread syscall number and arguments if
+			 * debugger modified registers or memory.
+			 */
+			error = fetch_syscall_args(td, &sa);
+			if (error != 0)
+				goto retval;
+			td->td_retval[1] = frame->tf_rdx;
+		}
 
 #ifdef KDTRACE_HOOKS
 		/*
@@ -981,13 +1017,13 @@ syscall(struct trapframe *frame)
 		 * callback and if there is a probe active for the
 		 * syscall 'entry', process the probe.
 		 */
-		if (systrace_probe_func != NULL && callp->sy_entry != 0)
-			(*systrace_probe_func)(callp->sy_entry, code, callp,
-			    args);
+		if (systrace_probe_func != NULL && sa.callp->sy_entry != 0)
+			(*systrace_probe_func)(sa.callp->sy_entry, sa.code,
+			    sa.callp, sa.args);
 #endif
 
-		AUDIT_SYSCALL_ENTER(code, td);
-		error = (*callp->sy_call)(td, argp);
+		AUDIT_SYSCALL_ENTER(sa.code, td);
+		error = (*sa.callp->sy_call)(td, sa.argp);
 		AUDIT_SYSCALL_EXIT(error, td);
 
 		/* Save the latest error return value. */
@@ -999,12 +1035,12 @@ syscall(struct trapframe *frame)
 		 * callback and if there is a probe active for the
 		 * syscall 'return', process the probe.
 		 */
-		if (systrace_probe_func != NULL && callp->sy_return != 0)
-			(*systrace_probe_func)(callp->sy_return, code, callp,
-			    args);
+		if (systrace_probe_func != NULL && sa.callp->sy_return != 0)
+			(*systrace_probe_func)(sa.callp->sy_return, sa.code,
+			    sa.callp, sa.args);
 #endif
 	}
-
+ retval:
 	cpu_set_syscall_retval(td, error);
 
 	/*
@@ -1023,14 +1059,16 @@ syscall(struct trapframe *frame)
 	 * Check for misbehavior.
 	 */
 	WITNESS_WARN(WARN_PANIC, NULL, "System call %s returning",
-	    (code >= 0 && code < SYS_MAXSYSCALL) ? syscallnames[code] : "???");
+	    (sa.code >= 0 && sa.code < SYS_MAXSYSCALL) ?
+	     syscallnames[sa.code] : "???");
 	KASSERT(td->td_critnest == 0,
 	    ("System call %s returning in a critical section",
-	    (code >= 0 && code < SYS_MAXSYSCALL) ? syscallnames[code] : "???"));
+	    (sa.code >= 0 && sa.code < SYS_MAXSYSCALL) ?
+	     syscallnames[sa.code] : "???"));
 	KASSERT(td->td_locks == 0,
 	    ("System call %s returning with %d locks held",
-	    (code >= 0 && code < SYS_MAXSYSCALL) ? syscallnames[code] : "???",
-	    td->td_locks));
+	    (sa.code >= 0 && sa.code < SYS_MAXSYSCALL) ?
+	     syscallnames[sa.code] : "???", td->td_locks));
 
 	/*
 	 * Handle reschedule and other end-of-syscall issues
@@ -1038,11 +1076,11 @@ syscall(struct trapframe *frame)
 	userret(td, frame);
 
 	CTR4(KTR_SYSC, "syscall exit thread %p pid %d proc %s code %d", td,
-	    td->td_proc->p_pid, td->td_name, code);
+	    td->td_proc->p_pid, td->td_name, sa.code);
 
 #ifdef KTRACE
 	if (KTRPOINT(td, KTR_SYSRET))
-		ktrsysret(code, error, td->td_retval[0]);
+		ktrsysret(sa.code, error, td->td_retval[0]);
 #endif
 
 	/*
@@ -1050,7 +1088,7 @@ syscall(struct trapframe *frame)
 	 * register set.  If we ever support an emulation where this
 	 * is not the case, this code will need to be revisited.
 	 */
-	STOPEVENT(p, S_SCX, code);
+	STOPEVENT(p, S_SCX, sa.code);
 
 	PTRACESTOP_SC(p, td, S_PT_SCX);
 }
diff --git a/sys/amd64/ia32/ia32_syscall.c b/sys/amd64/ia32/ia32_syscall.c
index 5e20876..aa1ae6c 100644
--- a/sys/amd64/ia32/ia32_syscall.c
+++ b/sys/amd64/ia32/ia32_syscall.c
@@ -88,101 +88,136 @@ extern const char *freebsd32_syscallnames[];
 
 void ia32_syscall(struct trapframe *frame);	/* Called from asm code */
 
-void
-ia32_syscall(struct trapframe *frame)
-{
+struct ia32_syscall_args {
+	u_int code;
 	caddr_t params;
-	int i;
 	struct sysent *callp;
-	struct thread *td = curthread;
-	struct proc *p = td->td_proc;
-	register_t orig_tf_rflags;
-	int error;
+	u_int64_t args64[8];
 	int narg;
+};
+
+static int
+fetch_ia32_syscall_args(struct thread *td, struct ia32_syscall_args *sa)
+{
+	struct proc *p;
+	struct trapframe *frame;
 	u_int32_t args[8];
-	u_int64_t args64[8];
-	u_int code;
-	ksiginfo_t ksi;
+	int error, i;
 
-	PCPU_INC(cnt.v_syscall);
-	td->td_pticks = 0;
-	td->td_frame = frame;
-	if (td->td_ucred != p->p_ucred) 
-		cred_update_thread(td);
-	params = (caddr_t)frame->tf_rsp + sizeof(u_int32_t);
-	code = frame->tf_rax;
-	orig_tf_rflags = frame->tf_rflags;
+	p = td->td_proc;
+	frame = td->td_frame;
+
+	sa->params = (caddr_t)frame->tf_rsp + sizeof(u_int32_t);
+	sa->code = frame->tf_rax;
 
 	if (p->p_sysent->sv_prepsyscall) {
 		/*
 		 * The prep code is MP aware.
 		 */
-		(*p->p_sysent->sv_prepsyscall)(frame, args, &code, &params);
+		(*p->p_sysent->sv_prepsyscall)(frame, args, &sa->code,
+		    &sa->params);
 	} else {
 		/*
 		 * Need to check if this is a 32 bit or 64 bit syscall.
 		 * fuword is MP aware.
 		 */
-		if (code == SYS_syscall) {
+		if (sa->code == SYS_syscall) {
 			/*
 			 * Code is first argument, followed by actual args.
 			 */
-			code = fuword32(params);
-			params += sizeof(int);
-		} else if (code == SYS___syscall) {
+			sa->code = fuword32(sa->params);
+			sa->params += sizeof(int);
+		} else if (sa->code == SYS___syscall) {
 			/*
 			 * Like syscall, but code is a quad, so as to maintain
 			 * quad alignment for the rest of the arguments.
 			 * We use a 32-bit fetch in case params is not
 			 * aligned.
 			 */
-			code = fuword32(params);
-			params += sizeof(quad_t);
+			sa->code = fuword32(sa->params);
+			sa->params += sizeof(quad_t);
 		}
 	}
-
  	if (p->p_sysent->sv_mask)
- 		code &= p->p_sysent->sv_mask;
-
- 	if (code >= p->p_sysent->sv_size)
- 		callp = &p->p_sysent->sv_table[0];
+ 		sa->code &= p->p_sysent->sv_mask;
+ 	if (sa->code >= p->p_sysent->sv_size)
+ 		sa->callp = &p->p_sysent->sv_table[0];
   	else
- 		callp = &p->p_sysent->sv_table[code];
-
-	narg = callp->sy_narg;
+ 		sa->callp = &p->p_sysent->sv_table[sa->code];
+	sa->narg = sa->callp->sy_narg;
 
-	/*
-	 * copyin and the ktrsyscall()/ktrsysret() code is MP-aware
-	 */
-	if (params != NULL && narg != 0)
-		error = copyin(params, (caddr_t)args,
-		    (u_int)(narg * sizeof(int)));
+	if (sa->params != NULL && sa->narg != 0)
+		error = copyin(sa->params, (caddr_t)args,
+		    (u_int)(sa->narg * sizeof(int)));
 	else
 		error = 0;
 
-	for (i = 0; i < narg; i++)
-		args64[i] = args[i];
+	for (i = 0; i < sa->narg; i++)
+		sa->args64[i] = args[i];
 
 #ifdef KTRACE
 	if (KTRPOINT(td, KTR_SYSCALL))
-		ktrsyscall(code, narg, args64);
+		ktrsyscall(sa->code, sa->narg, sa->args64);
 #endif
+
+	return (error);
+}
+
+void
+ia32_syscall(struct trapframe *frame)
+{
+	struct thread *td;
+	struct proc *p;
+	struct ia32_syscall_args sa;
+	register_t orig_tf_rflags;
+	int error;
+	ksiginfo_t ksi;
+
+	PCPU_INC(cnt.v_syscall);
+	td = curthread;
+	p = td->td_proc;
+	td->td_syscalls++;
+
+	td->td_pticks = 0;
+	td->td_frame = frame;
+	if (td->td_ucred != p->p_ucred) 
+		cred_update_thread(td);
+	orig_tf_rflags = frame->tf_rflags;
+	if (p->p_flag & P_TRACED) {
+		PROC_LOCK(p);
+		td->td_dbgflags &= ~TDB_USERWR;
+		PROC_UNLOCK(p);
+	}
+	error = fetch_ia32_syscall_args(td, &sa);
+
 	CTR4(KTR_SYSC, "syscall enter thread %p pid %d proc %s code %d", td,
-	    td->td_proc->p_pid, td->td_proc->p_comm, code);
+	    td->td_proc->p_pid, td->td_name, sa.code);
 
 	if (error == 0) {
 		td->td_retval[0] = 0;
 		td->td_retval[1] = frame->tf_rdx;
 
-		STOPEVENT(p, S_SCE, narg);
-
+		STOPEVENT(p, S_SCE, sa.narg);
 		PTRACESTOP_SC(p, td, S_PT_SCE);
+		if (td->td_dbgflags & TDB_USERWR) {
+			/*
+			 * Reread syscall number and arguments if
+			 * debugger modified registers or memory.
+			 */
+			error = fetch_ia32_syscall_args(td, &sa);
+			if (error != 0)
+				goto retval;
+			td->td_retval[1] = frame->tf_rdx;
+		}
 
-		AUDIT_SYSCALL_ENTER(code, td);
-		error = (*callp->sy_call)(td, args64);
+		AUDIT_SYSCALL_ENTER(sa.code, td);
+		error = (*sa.callp->sy_call)(td, sa.args64);
 		AUDIT_SYSCALL_EXIT(error, td);
-	}
 
+		/* Save the latest error return value. */
+		td->td_errno = error;
+	}
+ retval:
 	cpu_set_syscall_retval(td, error);
 
 	/*
@@ -201,14 +236,16 @@ ia32_syscall(struct trapframe *frame)
 	 * Check for misbehavior.
 	 */
 	WITNESS_WARN(WARN_PANIC, NULL, "System call %s returning",
-	    (code >= 0 && code < SYS_MAXSYSCALL) ? freebsd32_syscallnames[code] : "???");
+	    (sa.code >= 0 && sa.code < SYS_MAXSYSCALL) ?
+	     freebsd32_syscallnames[sa.code] : "???");
 	KASSERT(td->td_critnest == 0,
 	    ("System call %s returning in a critical section",
-	    (code >= 0 && code < SYS_MAXSYSCALL) ? freebsd32_syscallnames[code] : "???"));
+	    (sa.code >= 0 && sa.code < SYS_MAXSYSCALL) ?
+	     freebsd32_syscallnames[sa.code] : "???"));
 	KASSERT(td->td_locks == 0,
 	    ("System call %s returning with %d locks held",
-	    (code >= 0 && code < SYS_MAXSYSCALL) ? freebsd32_syscallnames[code] : "???",
-	    td->td_locks));
+	    (sa.code >= 0 && sa.code < SYS_MAXSYSCALL) ?
+	     freebsd32_syscallnames[sa.code] : "???", td->td_locks));
 
 	/*
 	 * Handle reschedule and other end-of-syscall issues
@@ -216,10 +253,10 @@ ia32_syscall(struct trapframe *frame)
 	userret(td, frame);
 
 	CTR4(KTR_SYSC, "syscall exit thread %p pid %d proc %s code %d", td,
-	    td->td_proc->p_pid, td->td_proc->p_comm, code);
+	    td->td_proc->p_pid, td->td_proc->p_comm, sa.code);
 #ifdef KTRACE
 	if (KTRPOINT(td, KTR_SYSRET))
-		ktrsysret(code, error, td->td_retval[0]);
+		ktrsysret(sa.code, error, td->td_retval[0]);
 #endif
 
 	/*
@@ -227,7 +264,7 @@ ia32_syscall(struct trapframe *frame)
 	 * register set.  If we ever support an emulation where this
 	 * is not the case, this code will need to be revisited.
 	 */
-	STOPEVENT(p, S_SCX, code);
+	STOPEVENT(p, S_SCX, sa.code);
  
 	PTRACESTOP_SC(p, td, S_PT_SCX);
 }
diff --git a/sys/i386/i386/trap.c b/sys/i386/i386/trap.c
index 1d3dc3b..305cfd2 100644
--- a/sys/i386/i386/trap.c
+++ b/sys/i386/i386/trap.c
@@ -969,97 +969,130 @@ dblfault_handler()
 	panic("double fault");
 }
 
-/*
- *	syscall -	system call request C handler
- *
- *	A system call is essentially treated as a trap.
- */
-void
-syscall(struct trapframe *frame)
-{
-	caddr_t params;
+struct syscall_args {
+	u_int code;
 	struct sysent *callp;
-	struct thread *td = curthread;
-	struct proc *p = td->td_proc;
-	register_t orig_tf_eflags;
-	int error;
-	int narg;
 	int args[8];
-	u_int code;
-	ksiginfo_t ksi;
+	register_t *argp;
+	int narg;
+};
 
-	PCPU_INC(cnt.v_syscall);
+static int
+fetch_syscall_args(struct thread *td, struct syscall_args *sa)
+{
+	struct proc *p;
+	struct trapframe *frame;
+	caddr_t params;
+	int error;
 
-#ifdef DIAGNOSTIC
-	if (ISPL(frame->tf_cs) != SEL_UPL) {
-		panic("syscall");
-		/* NOT REACHED */
-	}
-#endif
+	p = td->td_proc;
+	frame = td->td_frame;
 
-	td->td_pticks = 0;
-	td->td_frame = frame;
-	if (td->td_ucred != p->p_ucred) 
-		cred_update_thread(td);
 	params = (caddr_t)frame->tf_esp + sizeof(int);
-	code = frame->tf_eax;
-	orig_tf_eflags = frame->tf_eflags;
+	sa->code = frame->tf_eax;
 
 	if (p->p_sysent->sv_prepsyscall) {
-		(*p->p_sysent->sv_prepsyscall)(frame, args, &code, &params);
+		(*p->p_sysent->sv_prepsyscall)(frame, sa->args, &sa->code,
+		    &params);
 	} else {
 		/*
 		 * Need to check if this is a 32 bit or 64 bit syscall.
 		 */
-		if (code == SYS_syscall) {
+		if (sa->code == SYS_syscall) {
 			/*
 			 * Code is first argument, followed by actual args.
 			 */
-			code = fuword(params);
+			sa->code = fuword(params);
 			params += sizeof(int);
-		} else if (code == SYS___syscall) {
+		} else if (sa->code == SYS___syscall) {
 			/*
 			 * Like syscall, but code is a quad, so as to maintain
 			 * quad alignment for the rest of the arguments.
 			 */
-			code = fuword(params);
+			sa->code = fuword(params);
 			params += sizeof(quad_t);
 		}
 	}
 
  	if (p->p_sysent->sv_mask)
- 		code &= p->p_sysent->sv_mask;
-
- 	if (code >= p->p_sysent->sv_size)
- 		callp = &p->p_sysent->sv_table[0];
+ 		sa->code &= p->p_sysent->sv_mask;
+ 	if (sa->code >= p->p_sysent->sv_size)
+ 		sa->callp = &p->p_sysent->sv_table[0];
   	else
- 		callp = &p->p_sysent->sv_table[code];
-
-	narg = callp->sy_narg;
+ 		sa->callp = &p->p_sysent->sv_table[sa->code];
+	sa->narg = sa->callp->sy_narg;
 
-	if (params != NULL && narg != 0)
-		error = copyin(params, (caddr_t)args,
-		    (u_int)(narg * sizeof(int)));
+	if (params != NULL && sa->narg != 0)
+		error = copyin(params, (caddr_t)sa->args,
+		    (u_int)(sa->narg * sizeof(int)));
 	else
 		error = 0;
 		
 #ifdef KTRACE
 	if (KTRPOINT(td, KTR_SYSCALL))
-		ktrsyscall(code, narg, args);
+		ktrsyscall(sa->code, sa->narg, sa->args);
 #endif
+	return (error);
+}
 
-	CTR4(KTR_SYSC, "syscall enter thread %p pid %d proc %s code %d", td,
-	    td->td_proc->p_pid, td->td_name, code);
+/*
+ *	syscall -	system call request C handler
+ *
+ *	A system call is essentially treated as a trap.
+ */
+void
+syscall(struct trapframe *frame)
+{
+	struct thread *td;
+	struct proc *p;
+	struct syscall_args sa;
+	register_t orig_tf_eflags;
+	int error;
+	ksiginfo_t ksi;
 
+	PCPU_INC(cnt.v_syscall);
+	td = curthread;
+	p = td->td_proc;
 	td->td_syscalls++;
 
+#ifdef DIAGNOSTIC
+	if (ISPL(frame->tf_cs) != SEL_UPL) {
+		panic("syscall");
+		/* NOT REACHED */
+	}
+#endif
+
+	td->td_pticks = 0;
+	td->td_frame = frame;
+	if (td->td_ucred != p->p_ucred) 
+		cred_update_thread(td);
+	orig_tf_eflags = frame->tf_eflags;
+	if (p->p_flag & P_TRACED) {
+		PROC_LOCK(p);
+		td->td_dbgflags &= ~TDB_USERWR;
+		PROC_UNLOCK(p);
+	}
+	error = fetch_syscall_args(td, &sa);
+
+	CTR4(KTR_SYSC, "syscall enter thread %p pid %d proc %s code %d", td,
+	    td->td_proc->p_pid, td->td_name, sa.code);
+
 	if (error == 0) {
 		td->td_retval[0] = 0;
 		td->td_retval[1] = frame->tf_edx;
 
-		STOPEVENT(p, S_SCE, narg);
-
+		STOPEVENT(p, S_SCE, sa.narg);
 		PTRACESTOP_SC(p, td, S_PT_SCE);
+		if (td->td_dbgflags & TDB_USERWR) {
+			/*
+			 * Reread syscall number and arguments if
+			 * debugger modified registers or memory.
+			 */
+			error = fetch_syscall_args(td, &sa);
+			if (error != 0)
+				goto retval;
+			td->td_retval[1] = frame->tf_edx;
+		}
 
 #ifdef KDTRACE_HOOKS
 		/*
@@ -1067,13 +1100,13 @@ syscall(struct trapframe *frame)
 		 * callback and if there is a probe active for the
 		 * syscall 'entry', process the probe.
 		 */
-		if (systrace_probe_func != NULL && callp->sy_entry != 0)
-			(*systrace_probe_func)(callp->sy_entry, code, callp,
-			    args);
+		if (systrace_probe_func != NULL && sa.callp->sy_entry != 0)
+			(*systrace_probe_func)(sa.callp->sy_entry, sa.code,
+			    sa.callp, sa.args);
 #endif
 
-		AUDIT_SYSCALL_ENTER(code, td);
-		error = (*callp->sy_call)(td, args);
+		AUDIT_SYSCALL_ENTER(sa.code, td);
+		error = (*sa.callp->sy_call)(td, sa.args);
 		AUDIT_SYSCALL_EXIT(error, td);
 
 		/* Save the latest error return value. */
@@ -1085,12 +1118,12 @@ syscall(struct trapframe *frame)
 		 * callback and if there is a probe active for the
 		 * syscall 'return', process the probe.
 		 */
-		if (systrace_probe_func != NULL && callp->sy_return != 0)
-			(*systrace_probe_func)(callp->sy_return, code, callp,
-			    args);
+		if (systrace_probe_func != NULL && sa.callp->sy_return != 0)
+			(*systrace_probe_func)(sa.callp->sy_return, sa.code,
+			    sa.callp, sa.args);
 #endif
 	}
-
+ retval:
 	cpu_set_syscall_retval(td, error);
 
 	/*
@@ -1109,14 +1142,16 @@ syscall(struct trapframe *frame)
 	 * Check for misbehavior.
 	 */
 	WITNESS_WARN(WARN_PANIC, NULL, "System call %s returning",
-	    (code >= 0 && code < SYS_MAXSYSCALL) ? syscallnames[code] : "???");
+	    (sa.code >= 0 && sa.code < SYS_MAXSYSCALL) ?
+	     syscallnames[sa.code] : "???");
 	KASSERT(td->td_critnest == 0,
 	    ("System call %s returning in a critical section",
-	    (code >= 0 && code < SYS_MAXSYSCALL) ? syscallnames[code] : "???"));
+	    (sa.code >= 0 && sa.code < SYS_MAXSYSCALL) ?
+	     syscallnames[sa.code] : "???"));
 	KASSERT(td->td_locks == 0,
 	    ("System call %s returning with %d locks held",
-	    (code >= 0 && code < SYS_MAXSYSCALL) ? syscallnames[code] : "???",
-	    td->td_locks));
+	    (sa.code >= 0 && sa.code < SYS_MAXSYSCALL) ?
+	     syscallnames[sa.code] : "???", td->td_locks));
 
 	/*
 	 * Handle reschedule and other end-of-syscall issues
@@ -1124,11 +1159,11 @@ syscall(struct trapframe *frame)
 	userret(td, frame);
 
 	CTR4(KTR_SYSC, "syscall exit thread %p pid %d proc %s code %d", td,
-	    td->td_proc->p_pid, td->td_name, code);
+	    td->td_proc->p_pid, td->td_name, sa.code);
 
 #ifdef KTRACE
 	if (KTRPOINT(td, KTR_SYSRET))
-		ktrsysret(code, error, td->td_retval[0]);
+		ktrsysret(sa.code, error, td->td_retval[0]);
 #endif
 
 	/*
@@ -1136,7 +1171,7 @@ syscall(struct trapframe *frame)
 	 * register set.  If we ever support an emulation where this
 	 * is not the case, this code will need to be revisited.
 	 */
-	STOPEVENT(p, S_SCX, code);
+	STOPEVENT(p, S_SCX, sa.code);
 
 	PTRACESTOP_SC(p, td, S_PT_SCX);
 }
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
index dfc36ba..3c6394c 100644
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -816,6 +816,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
 
 	case PT_WRITE_I:
 	case PT_WRITE_D:
+		td2->td_dbgflags |= TDB_USERWR;
 		write = 1;
 		/* FALLTHROUGH */
 	case PT_READ_I:
@@ -884,6 +885,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
 			break;
 		case PIOD_WRITE_D:
 		case PIOD_WRITE_I:
+			td2->td_dbgflags |= TDB_USERWR;
 			uio.uio_rw = UIO_WRITE;
 			break;
 		default:
@@ -906,6 +908,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
 		goto sendsig;	/* in PT_CONTINUE above */
 
 	case PT_SETREGS:
+		td2->td_dbgflags |= TDB_USERWR;
 		error = PROC_WRITE(regs, td2, addr);
 		break;
 
@@ -914,6 +917,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
 		break;
 
 	case PT_SETFPREGS:
+		td2->td_dbgflags |= TDB_USERWR;
 		error = PROC_WRITE(fpregs, td2, addr);
 		break;
 
@@ -922,6 +926,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
 		break;
 
 	case PT_SETDBREGS:
+		td2->td_dbgflags |= TDB_USERWR;
 		error = PROC_WRITE(dbregs, td2, addr);
 		break;
 
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index 0ae36af..dd9efae 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -341,6 +341,7 @@ do {									\
 /* Userland debug flags */
 #define	TDB_SUSPEND	0x00000001 /* Thread is suspended by debugger */
 #define	TDB_XSIG	0x00000002 /* Thread is exchanging signal under trace */
+#define	TDB_USERWR	0x00000004 /* Debugger modified memory or registers */
 
 /*
  * "Private" flags kept in td_pflags:
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 196 bytes
Desc: not available
Url : http://lists.freebsd.org/pipermail/freebsd-hackers/attachments/20100117/9653e1f2/attachment.pgp


More information about the freebsd-hackers mailing list