1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <sys/dtrace.h>
28 #include <sys/dtrace_glue.h>
29 #include <sys/dtrace_impl.h>
30 #include <sys/fasttrap.h>
31 #include <sys/vm.h>
32 #include <sys/user.h>
33 #include <sys/kauth.h>
34 #include <kern/debug.h>
35
36 int (*dtrace_pid_probe_ptr)(x86_saved_state_t *);
37 int (*dtrace_return_probe_ptr)(x86_saved_state_t *);
38
39 /*
40 * HACK! There doesn't seem to be an easy way to include trap.h from
41 * here. FIXME!
42 */
43 #define T_INT3 3 /* int 3 instruction */
44 #define T_DTRACE_RET 0x7f /* DTrace pid return */
45
46 kern_return_t
47 dtrace_user_probe(x86_saved_state_t *);
48
49 kern_return_t
dtrace_user_probe(x86_saved_state_t * regs)50 dtrace_user_probe(x86_saved_state_t *regs)
51 {
52 x86_saved_state64_t *regs64;
53 x86_saved_state32_t *regs32;
54 int trapno;
55
56 /*
57 * FIXME!
58 *
59 * The only call path into this method is always a user trap.
60 * We don't need to test for user trap, but should assert it.
61 */
62 boolean_t user_mode = TRUE;
63
64 if (is_saved_state64(regs) == TRUE) {
65 regs64 = saved_state64(regs);
66 regs32 = NULL;
67 trapno = regs64->isf.trapno;
68 user_mode = TRUE; // By default, because xnu is 32 bit only
69 } else {
70 regs64 = NULL;
71 regs32 = saved_state32(regs);
72 if (regs32->cs & 0x03) user_mode = TRUE;
73 trapno = regs32->trapno;
74 }
75
76 lck_rw_t *rwp;
77 struct proc *p = current_proc();
78
79 thread_t thread = current_thread();
80 uthread_t uthread = current_uthread();
81 if (user_mode /*|| (rp->r_ps & PS_VM)*/) {
82 /*
83 * DTrace accesses t_cred in probe context. t_cred
84 * must always be either NULL, or point to a valid,
85 * allocated cred structure.
86 */
87 kauth_cred_thread_update(thread, p);
88 }
89
90 if (trapno == T_DTRACE_RET) {
91 uint8_t step = uthread->t_dtrace_step;
92 uint8_t ret = uthread->t_dtrace_ret;
93 user_addr_t npc = uthread->t_dtrace_npc;
94
95 if (uthread->t_dtrace_ast) {
96 printf("dtrace_user_probe() should be calling aston()\n");
97 // aston(uthread);
98 // uthread->t_sig_check = 1;
99 }
100
101 /*
102 * Clear all user tracing flags.
103 */
104 uthread->t_dtrace_ft = 0;
105
106 /*
107 * If we weren't expecting to take a return probe trap, kill
108 * the process as though it had just executed an unassigned
109 * trap instruction.
110 */
111 if (step == 0) {
112 /*
113 * APPLE NOTE: We're returning KERN_FAILURE, which causes
114 * the generic signal handling code to take over, which will effectively
115 * deliver a EXC_BAD_INSTRUCTION to the user process.
116 */
117 return KERN_FAILURE;
118 }
119
120 /*
121 * If we hit this trap unrelated to a return probe, we're
122 * just here to reset the AST flag since we deferred a signal
123 * until after we logically single-stepped the instruction we
124 * copied out.
125 */
126 if (ret == 0) {
127 if (regs64) {
128 regs64->isf.rip = npc;
129 } else {
130 regs32->eip = (uint32_t)npc;
131 }
132 return KERN_SUCCESS;
133 }
134
135 /*
136 * We need to wait until after we've called the
137 * dtrace_return_probe_ptr function pointer to set %pc.
138 */
139 rwp = &CPU->cpu_ft_lock;
140 lck_rw_lock_shared(rwp);
141
142 if (dtrace_return_probe_ptr != NULL)
143 (void) (*dtrace_return_probe_ptr)(regs);
144 lck_rw_unlock_shared(rwp);
145
146 if (regs64) {
147 regs64->isf.rip = npc;
148 } else {
149 regs32->eip = (uint32_t)npc;
150 }
151
152 return KERN_SUCCESS;
153 } else if (trapno == T_INT3) {
154 uint8_t instr, instr2;
155 rwp = &CPU->cpu_ft_lock;
156
157 /*
158 * The DTrace fasttrap provider uses the breakpoint trap
159 * (int 3). We let DTrace take the first crack at handling
160 * this trap; if it's not a probe that DTrace knowns about,
161 * we call into the trap() routine to handle it like a
162 * breakpoint placed by a conventional debugger.
163 */
164
165 /*
166 * APPLE NOTE: I believe the purpose of the reader/writers lock
167 * is thus: There are times which dtrace needs to prevent calling
168 * dtrace_pid_probe_ptr(). Sun's original impl grabbed a plain
169 * mutex here. However, that serialized all probe calls, and
170 * destroyed MP behavior. So now they use a RW lock, with probes
171 * as readers, and the top level synchronization as a writer.
172 */
173 lck_rw_lock_shared(rwp);
174 if (dtrace_pid_probe_ptr != NULL &&
175 (*dtrace_pid_probe_ptr)(regs) == 0) {
176 lck_rw_unlock_shared(rwp);
177 return KERN_SUCCESS;
178 }
179 lck_rw_unlock_shared(rwp);
180
181
182 /*
183 * If the instruction that caused the breakpoint trap doesn't
184 * look like an int 3 anymore, it may be that this tracepoint
185 * was removed just after the user thread executed it. In
186 * that case, return to user land to retry the instuction.
187 */
188 user_addr_t pc = (regs64) ? regs64->isf.rip : (user_addr_t)regs32->eip;
189 if (fuword8(pc - 1, &instr) == 0 && instr != FASTTRAP_INSTR && // neither single-byte INT3 (0xCC)
190 !(instr == 3 && fuword8(pc - 2, &instr2) == 0 && instr2 == 0xCD)) { // nor two-byte INT 3 (0xCD03)
191 if (regs64) {
192 regs64->isf.rip--;
193 } else {
194 regs32->eip--;
195 }
196 return KERN_SUCCESS;
197 }
198
199 }
200
201 return KERN_FAILURE;
202 }
203
204 void
dtrace_flush_caches(void)205 dtrace_flush_caches(void)
206 {
207
208 }
209