xref: /minix/minix/servers/pm/trace.c (revision 0a6a1f1d)
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 an VFS call pending. */
149 	if (child->mp_flags & VFS_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