summaryrefslogtreecommitdiffstats
path: root/sys/amd64
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2010-01-23 11:45:35 +0000
committerkib <kib@FreeBSD.org>2010-01-23 11:45:35 +0000
commitddbe48bd563ccf41f12cf0b706b4a5c7637cccbe (patch)
treea22c8a20fb7f2ce9b1f9c1c3c2cdc8e817568cc0 /sys/amd64
parenta22fc66b6e80c44e0ad8bc552f3c955b8ab5d737 (diff)
downloadFreeBSD-src-ddbe48bd563ccf41f12cf0b706b4a5c7637cccbe.zip
FreeBSD-src-ddbe48bd563ccf41f12cf0b706b4a5c7637cccbe.tar.gz
For PT_TO_SCE stop that stops the ptraced process upon syscall entry,
syscall arguments are collected before ptracestop() is called. As a consequence, debugger cannot modify syscall or its arguments. For i386, amd64 and ia32 on amd64 MD syscall(), reread syscall number and arguments after ptracestop(), if debugger modified anything in the process environment. Since procfs stopeven requires number of syscall arguments in p_xstat, this cannot be solved by moving stop/trace point before argument fetching. Move the code to read arguments into separate function fetch_syscall_args() to avoid code duplication. Note that ktrace point for modified syscall is intentionally recorded twice, once with original arguments, and second time with the arguments set by debugger. PT_TO_SCX stop is executed after cpu_syscall_set_retval() already. Reported by: Ali Polatel <alip exherbo org> Briefly discussed with: jhb MFC after: 3 weeks
Diffstat (limited to 'sys/amd64')
-rw-r--r--sys/amd64/amd64/trap.c176
-rw-r--r--sys/amd64/ia32/ia32_syscall.c149
2 files changed, 200 insertions, 125 deletions
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);
}
OpenPOWER on IntegriCloud