xref: /minix3/minix/servers/pm/trace.c (revision 637f688f0d4ccff0fa8a6ddafcd1d89fdfbc462d)
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(void)42 do_trace(void)
43 {
44   register struct mproc *child;
45   struct ptrace_range pr;
46   int i, r, req;
47 
48   req = m_in.m_lc_pm_ptrace.req;
49 
50   /* The T_OK call is made by the child fork of the debugger before it execs
51    * the process to be traced. The T_ATTACH call is made by the debugger itself
52    * to attach to an existing process.
53    */
54   switch (req) {
55   case T_OK:		/* enable tracing by parent for this proc */
56 	if (mp->mp_tracer != NO_TRACER) return(EBUSY);
57 
58 	mp->mp_tracer = mp->mp_parent;
59 	mp->mp_reply.m_pm_lc_ptrace.data = 0;
60 	return(OK);
61 
62   case T_ATTACH:	/* attach to an existing process */
63 	if ((child = find_proc(m_in.m_lc_pm_ptrace.pid)) == NULL) return(ESRCH);
64 	if (child->mp_flags & EXITING) return(ESRCH);
65 
66 	/* For non-root processes, user and group ID must match. */
67 	if (mp->mp_effuid != SUPER_USER &&
68 		(mp->mp_effuid != child->mp_effuid ||
69 		 mp->mp_effgid != child->mp_effgid ||
70 		 child->mp_effuid != child->mp_realuid ||
71 		 child->mp_effgid != child->mp_realgid)) return(EPERM);
72 
73 	/* Only root may trace system servers. */
74 	if (mp->mp_effuid != SUPER_USER && (child->mp_flags & PRIV_PROC))
75 		return(EPERM);
76 
77 	/* System servers may not trace anyone. They can use sys_trace(). */
78 	if (mp->mp_flags & PRIV_PROC) return(EPERM);
79 
80 	/* Can't trace self, PM or VM. */
81 	if (child == mp || child->mp_endpoint == PM_PROC_NR ||
82 		child->mp_endpoint == VM_PROC_NR) return(EPERM);
83 
84 	/* Can't trace a process that is already being traced. */
85 	if (child->mp_tracer != NO_TRACER) return(EBUSY);
86 
87 	child->mp_tracer = who_p;
88 	child->mp_trace_flags = TO_NOEXEC;
89 
90 	sig_proc(child, SIGSTOP, TRUE /*trace*/, FALSE /* ksig */);
91 
92 	mp->mp_reply.m_pm_lc_ptrace.data = 0;
93 	return(OK);
94 
95   case T_STOP:		/* stop the process */
96 	/* This call is not exposed to user programs, because its effect can be
97 	 * achieved better by sending the traced process a signal with kill(2).
98 	 */
99 	return(EINVAL);
100 
101   case T_READB_INS:	/* special hack for reading text segments */
102 	if (mp->mp_effuid != SUPER_USER) return(EPERM);
103 	if ((child = find_proc(m_in.m_lc_pm_ptrace.pid)) == NULL) return(ESRCH);
104 	if (child->mp_flags & EXITING) return(ESRCH);
105 
106 	r = sys_trace(req, child->mp_endpoint, m_in.m_lc_pm_ptrace.addr,
107 		&m_in.m_lc_pm_ptrace.data);
108 	if (r != OK) return(r);
109 
110 	mp->mp_reply.m_pm_lc_ptrace.data = m_in.m_lc_pm_ptrace.data;
111 	return(OK);
112 
113   case T_WRITEB_INS:	/* special hack for patching text segments */
114 	if (mp->mp_effuid != SUPER_USER) return(EPERM);
115 	if ((child = find_proc(m_in.m_lc_pm_ptrace.pid)) == NULL) return(ESRCH);
116 	if (child->mp_flags & EXITING) return(ESRCH);
117 
118 #if 0
119 	/* Should check for shared text */
120 
121 	/* Make sure the text segment is not used as a source for shared
122 	 * text.
123 	 */
124 	child->mp_ino = 0;
125 	child->mp_dev = 0;
126 	child->mp_ctime = 0;
127 #endif
128 
129 	r = sys_trace(req, child->mp_endpoint, m_in.m_lc_pm_ptrace.addr,
130 		&m_in.m_lc_pm_ptrace.data);
131 	if (r != OK) return(r);
132 
133 	mp->mp_reply.m_pm_lc_ptrace.data = m_in.m_lc_pm_ptrace.data;
134 	return(OK);
135   }
136 
137   /* All the other calls are made by the tracing process to control execution
138    * of the child. For all these calls, the child must be stopped.
139    */
140   if ((child = find_proc(m_in.m_lc_pm_ptrace.pid)) == NULL) return(ESRCH);
141   if (child->mp_flags & EXITING) return(ESRCH);
142   if (child->mp_tracer != who_p) return(ESRCH);
143   if (!(child->mp_flags & TRACE_STOPPED)) return(EBUSY);
144 
145   switch (req) {
146   case T_EXIT:		/* exit */
147 	child->mp_flags |= TRACE_EXIT;
148 
149 	/* Defer the exit if the traced process has a call pending. */
150 	if (child->mp_flags & (VFS_CALL | EVENT_CALL))
151 		child->mp_exitstatus = m_in.m_lc_pm_ptrace.data; /* save it */
152 	else
153 		exit_proc(child, m_in.m_lc_pm_ptrace.data,
154 			FALSE /*dump_core*/);
155 
156 	/* Do not reply to the caller until VFS has processed the exit
157 	 * request.
158 	 */
159 	return(SUSPEND);
160 
161   case T_SETOPT:	/* set trace options */
162 	child->mp_trace_flags = m_in.m_lc_pm_ptrace.data;
163 
164 	mp->mp_reply.m_pm_lc_ptrace.data = 0;
165 	return(OK);
166 
167   case T_GETRANGE:
168   case T_SETRANGE:	/* get/set range of values */
169 	r = sys_datacopy(who_e, m_in.m_lc_pm_ptrace.addr, SELF, (vir_bytes)&pr,
170 		(phys_bytes)sizeof(pr));
171 	if (r != OK) return(r);
172 
173 	if (pr.pr_space != TS_INS && pr.pr_space != TS_DATA) return(EINVAL);
174 	if (pr.pr_size == 0 || pr.pr_size > LONG_MAX) return(EINVAL);
175 
176 	if (req == T_GETRANGE)
177 		r = sys_vircopy(child->mp_endpoint, (vir_bytes) pr.pr_addr,
178 			who_e, (vir_bytes) pr.pr_ptr,
179 			(phys_bytes) pr.pr_size, 0);
180 	else
181 		r = sys_vircopy(who_e, (vir_bytes) pr.pr_ptr,
182 			child->mp_endpoint, (vir_bytes) pr.pr_addr,
183 			(phys_bytes) pr.pr_size, 0);
184 
185 	if (r != OK) return(r);
186 
187 	mp->mp_reply.m_pm_lc_ptrace.data = 0;
188 	return(OK);
189 
190   case T_DETACH:	/* detach from traced process */
191 	if (m_in.m_lc_pm_ptrace.data < 0 || m_in.m_lc_pm_ptrace.data >= _NSIG)
192 		return(EINVAL);
193 
194 	child->mp_tracer = NO_TRACER;
195 
196 	/* Let all tracer-pending signals through the filter. */
197 	for (i = 1; i < _NSIG; i++) {
198 		if (sigismember(&child->mp_sigtrace, i)) {
199 			sigdelset(&child->mp_sigtrace, i);
200 			check_sig(child->mp_pid, i, FALSE /* ksig */);
201 		}
202 	}
203 
204 	if (m_in.m_lc_pm_ptrace.data > 0) {		/* issue signal */
205 		sig_proc(child, m_in.m_lc_pm_ptrace.data, TRUE /*trace*/,
206 			FALSE /* ksig */);
207 	}
208 
209 	/* Resume the child as if nothing ever happened. */
210 	child->mp_flags &= ~TRACE_STOPPED;
211 	child->mp_trace_flags = 0;
212 
213 	check_pending(child);
214 
215 	break;
216 
217   case T_RESUME:
218   case T_STEP:
219   case T_SYSCALL:	/* resume execution */
220 	if (m_in.m_lc_pm_ptrace.data < 0 || m_in.m_lc_pm_ptrace.data >= _NSIG)
221 		return(EINVAL);
222 
223 	if (m_in.m_lc_pm_ptrace.data > 0) {		/* issue signal */
224 		sig_proc(child, m_in.m_lc_pm_ptrace.data, FALSE /*trace*/,
225 			FALSE /* ksig */);
226 	}
227 
228 	/* If there are any other signals waiting to be delivered,
229 	 * feign a successful resumption.
230 	 */
231 	for (i = 1; i < _NSIG; i++) {
232 		if (sigismember(&child->mp_sigtrace, i)) {
233 			mp->mp_reply.m_pm_lc_ptrace.data = 0;
234 			return(OK);
235 		}
236 	}
237 
238 	child->mp_flags &= ~TRACE_STOPPED;
239 
240 	check_pending(child);
241 
242 	break;
243   }
244   r = sys_trace(req, child->mp_endpoint, m_in.m_lc_pm_ptrace.addr,
245 	&m_in.m_lc_pm_ptrace.data);
246   if (r != OK) return(r);
247 
248   mp->mp_reply.m_pm_lc_ptrace.data = m_in.m_lc_pm_ptrace.data;
249   return(OK);
250 }
251 
252 /*===========================================================================*
253  *				trace_stop				     *
254  *===========================================================================*/
255 void
trace_stop(register struct mproc * rmp,int signo)256 trace_stop(register struct mproc *rmp, 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