1 /* This file handles the process manager's part of debugging, using the 2 * ptrace system call. Most of the commands are passed on to the system 3 * task for completion. 4 * 5 * The debugging commands available are: 6 * T_STOP stop the process 7 * T_OK enable tracing by parent for this process 8 * T_GETINS return value from instruction space 9 * T_GETDATA return value from data space 10 * T_GETUSER return value from user process table 11 * T_SETINS set value in instruction space 12 * T_SETDATA set value in data space 13 * T_SETUSER set value in user process table 14 * T_RESUME resume execution 15 * T_EXIT exit 16 * T_STEP set trace bit 17 * T_SYSCALL trace system call 18 * T_ATTACH attach to an existing process 19 * T_DETACH detach from a traced process 20 * T_SETOPT set trace options 21 * T_GETRANGE get range of values 22 * T_SETRANGE set range of values 23 * 24 * The T_OK, T_ATTACH, T_EXIT, and T_SETOPT commands are handled here, and the 25 * T_RESUME, T_STEP, T_SYSCALL, and T_DETACH commands are partially handled 26 * here and completed by the system task. The rest are handled entirely by the 27 * system task. 28 */ 29 30 #include "pm.h" 31 #include <minix/com.h> 32 #include <minix/callnr.h> 33 #include <sys/ptrace.h> 34 #include <sys/wait.h> 35 #include <signal.h> 36 #include "mproc.h" 37 38 /*===========================================================================* 39 * do_trace * 40 *===========================================================================*/ 41 int do_trace() 42 { 43 register struct mproc *child; 44 struct ptrace_range pr; 45 int i, r, req; 46 47 req = m_in.m_lc_pm_ptrace.req; 48 49 /* The T_OK call is made by the child fork of the debugger before it execs 50 * the process to be traced. The T_ATTACH call is made by the debugger itself 51 * to attach to an existing process. 52 */ 53 switch (req) { 54 case T_OK: /* enable tracing by parent for this proc */ 55 if (mp->mp_tracer != NO_TRACER) return(EBUSY); 56 57 mp->mp_tracer = mp->mp_parent; 58 mp->mp_reply.m_pm_lc_ptrace.data = 0; 59 return(OK); 60 61 case T_ATTACH: /* attach to an existing process */ 62 if ((child = find_proc(m_in.m_lc_pm_ptrace.pid)) == NULL) return(ESRCH); 63 if (child->mp_flags & EXITING) return(ESRCH); 64 65 /* For non-root processes, user and group ID must match. */ 66 if (mp->mp_effuid != SUPER_USER && 67 (mp->mp_effuid != child->mp_effuid || 68 mp->mp_effgid != child->mp_effgid || 69 child->mp_effuid != child->mp_realuid || 70 child->mp_effgid != child->mp_realgid)) return(EPERM); 71 72 /* Only root may trace system servers. */ 73 if (mp->mp_effuid != SUPER_USER && (child->mp_flags & PRIV_PROC)) 74 return(EPERM); 75 76 /* System servers may not trace anyone. They can use sys_trace(). */ 77 if (mp->mp_flags & PRIV_PROC) return(EPERM); 78 79 /* Can't trace self, PM or VM. */ 80 if (child == mp || child->mp_endpoint == PM_PROC_NR || 81 child->mp_endpoint == VM_PROC_NR) return(EPERM); 82 83 /* Can't trace a process that is already being traced. */ 84 if (child->mp_tracer != NO_TRACER) return(EBUSY); 85 86 child->mp_tracer = who_p; 87 child->mp_trace_flags = TO_NOEXEC; 88 89 sig_proc(child, SIGSTOP, TRUE /*trace*/, FALSE /* ksig */); 90 91 mp->mp_reply.m_pm_lc_ptrace.data = 0; 92 return(OK); 93 94 case T_STOP: /* stop the process */ 95 /* This call is not exposed to user programs, because its effect can be 96 * achieved better by sending the traced process a signal with kill(2). 97 */ 98 return(EINVAL); 99 100 case T_READB_INS: /* special hack for reading text segments */ 101 if (mp->mp_effuid != SUPER_USER) return(EPERM); 102 if ((child = find_proc(m_in.m_lc_pm_ptrace.pid)) == NULL) return(ESRCH); 103 if (child->mp_flags & EXITING) return(ESRCH); 104 105 r = sys_trace(req, child->mp_endpoint, m_in.m_lc_pm_ptrace.addr, 106 &m_in.m_lc_pm_ptrace.data); 107 if (r != OK) return(r); 108 109 mp->mp_reply.m_pm_lc_ptrace.data = m_in.m_lc_pm_ptrace.data; 110 return(OK); 111 112 case T_WRITEB_INS: /* special hack for patching text segments */ 113 if (mp->mp_effuid != SUPER_USER) return(EPERM); 114 if ((child = find_proc(m_in.m_lc_pm_ptrace.pid)) == NULL) return(ESRCH); 115 if (child->mp_flags & EXITING) return(ESRCH); 116 117 #if 0 118 /* Should check for shared text */ 119 120 /* Make sure the text segment is not used as a source for shared 121 * text. 122 */ 123 child->mp_ino = 0; 124 child->mp_dev = 0; 125 child->mp_ctime = 0; 126 #endif 127 128 r = sys_trace(req, child->mp_endpoint, m_in.m_lc_pm_ptrace.addr, 129 &m_in.m_lc_pm_ptrace.data); 130 if (r != OK) return(r); 131 132 mp->mp_reply.m_pm_lc_ptrace.data = m_in.m_lc_pm_ptrace.data; 133 return(OK); 134 } 135 136 /* All the other calls are made by the tracing process to control execution 137 * of the child. For all these calls, the child must be stopped. 138 */ 139 if ((child = find_proc(m_in.m_lc_pm_ptrace.pid)) == NULL) return(ESRCH); 140 if (child->mp_flags & EXITING) return(ESRCH); 141 if (child->mp_tracer != who_p) return(ESRCH); 142 if (!(child->mp_flags & TRACE_STOPPED)) return(EBUSY); 143 144 switch (req) { 145 case T_EXIT: /* exit */ 146 child->mp_flags |= TRACE_EXIT; 147 148 /* Defer the exit if the traced process has a call pending. */ 149 if (child->mp_flags & (VFS_CALL | EVENT_CALL)) 150 child->mp_exitstatus = m_in.m_lc_pm_ptrace.data; /* save it */ 151 else 152 exit_proc(child, m_in.m_lc_pm_ptrace.data, 153 FALSE /*dump_core*/); 154 155 /* Do not reply to the caller until VFS has processed the exit 156 * request. 157 */ 158 return(SUSPEND); 159 160 case T_SETOPT: /* set trace options */ 161 child->mp_trace_flags = m_in.m_lc_pm_ptrace.data; 162 163 mp->mp_reply.m_pm_lc_ptrace.data = 0; 164 return(OK); 165 166 case T_GETRANGE: 167 case T_SETRANGE: /* get/set range of values */ 168 r = sys_datacopy(who_e, m_in.m_lc_pm_ptrace.addr, SELF, (vir_bytes)&pr, 169 (phys_bytes)sizeof(pr)); 170 if (r != OK) return(r); 171 172 if (pr.pr_space != TS_INS && pr.pr_space != TS_DATA) return(EINVAL); 173 if (pr.pr_size == 0 || pr.pr_size > LONG_MAX) return(EINVAL); 174 175 if (req == T_GETRANGE) 176 r = sys_vircopy(child->mp_endpoint, (vir_bytes) pr.pr_addr, 177 who_e, (vir_bytes) pr.pr_ptr, 178 (phys_bytes) pr.pr_size, 0); 179 else 180 r = sys_vircopy(who_e, (vir_bytes) pr.pr_ptr, 181 child->mp_endpoint, (vir_bytes) pr.pr_addr, 182 (phys_bytes) pr.pr_size, 0); 183 184 if (r != OK) return(r); 185 186 mp->mp_reply.m_pm_lc_ptrace.data = 0; 187 return(OK); 188 189 case T_DETACH: /* detach from traced process */ 190 if (m_in.m_lc_pm_ptrace.data < 0 || m_in.m_lc_pm_ptrace.data >= _NSIG) 191 return(EINVAL); 192 193 child->mp_tracer = NO_TRACER; 194 195 /* Let all tracer-pending signals through the filter. */ 196 for (i = 1; i < _NSIG; i++) { 197 if (sigismember(&child->mp_sigtrace, i)) { 198 sigdelset(&child->mp_sigtrace, i); 199 check_sig(child->mp_pid, i, FALSE /* ksig */); 200 } 201 } 202 203 if (m_in.m_lc_pm_ptrace.data > 0) { /* issue signal */ 204 sig_proc(child, m_in.m_lc_pm_ptrace.data, TRUE /*trace*/, 205 FALSE /* ksig */); 206 } 207 208 /* Resume the child as if nothing ever happened. */ 209 child->mp_flags &= ~TRACE_STOPPED; 210 child->mp_trace_flags = 0; 211 212 check_pending(child); 213 214 break; 215 216 case T_RESUME: 217 case T_STEP: 218 case T_SYSCALL: /* resume execution */ 219 if (m_in.m_lc_pm_ptrace.data < 0 || m_in.m_lc_pm_ptrace.data >= _NSIG) 220 return(EINVAL); 221 222 if (m_in.m_lc_pm_ptrace.data > 0) { /* issue signal */ 223 sig_proc(child, m_in.m_lc_pm_ptrace.data, FALSE /*trace*/, 224 FALSE /* ksig */); 225 } 226 227 /* If there are any other signals waiting to be delivered, 228 * feign a successful resumption. 229 */ 230 for (i = 1; i < _NSIG; i++) { 231 if (sigismember(&child->mp_sigtrace, i)) { 232 mp->mp_reply.m_pm_lc_ptrace.data = 0; 233 return(OK); 234 } 235 } 236 237 child->mp_flags &= ~TRACE_STOPPED; 238 239 check_pending(child); 240 241 break; 242 } 243 r = sys_trace(req, child->mp_endpoint, m_in.m_lc_pm_ptrace.addr, 244 &m_in.m_lc_pm_ptrace.data); 245 if (r != OK) return(r); 246 247 mp->mp_reply.m_pm_lc_ptrace.data = m_in.m_lc_pm_ptrace.data; 248 return(OK); 249 } 250 251 /*===========================================================================* 252 * trace_stop * 253 *===========================================================================*/ 254 void trace_stop(rmp, signo) 255 register struct mproc *rmp; 256 int signo; 257 { 258 /* A traced process got a signal so stop it. */ 259 260 register struct mproc *rpmp = mproc + rmp->mp_tracer; 261 int r; 262 263 r = sys_trace(T_STOP, rmp->mp_endpoint, 0L, (long *) 0); 264 if (r != OK) panic("sys_trace failed: %d", r); 265 266 rmp->mp_flags |= TRACE_STOPPED; 267 if (wait_test(rpmp, rmp)) { 268 /* TODO: rusage support */ 269 270 sigdelset(&rmp->mp_sigtrace, signo); 271 272 rpmp->mp_flags &= ~WAITING; /* parent is no longer waiting */ 273 rpmp->mp_reply.m_pm_lc_wait4.status = W_STOPCODE(signo); 274 reply(rmp->mp_tracer, rmp->mp_pid); 275 } 276 } 277