1 | /* |
2 | * Copyright (c) 2019 Apple Inc. All rights reserved. |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
5 | * |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License |
8 | * Version 2.0 (the 'License'). You may not use this file except in |
9 | * compliance with the License. The rights granted to you under the License |
10 | * may not be used to create, or enable the creation or redistribution of, |
11 | * unlawful or unlicensed copies of an Apple operating system, or to |
12 | * circumvent, violate, or enable the circumvention or violation of, any |
13 | * terms of an Apple operating system software license agreement. |
14 | * |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. |
17 | * |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and |
24 | * limitations under the License. |
25 | * |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ |
28 | |
29 | #ifdef MACH_BSD |
30 | #include <mach_ldebug.h> |
31 | |
32 | #include <mach/kern_return.h> |
33 | #include <mach/mach_traps.h> |
34 | #include <mach/vm_param.h> |
35 | |
36 | #include <kern/bits.h> |
37 | #include <kern/cpu_data.h> |
38 | #include <arm/cpu_data_internal.h> |
39 | #include <kern/mach_param.h> |
40 | #include <kern/task.h> |
41 | #include <kern/thread.h> |
42 | #include <kern/sched_prim.h> |
43 | #include <kern/misc_protos.h> |
44 | #include <kern/assert.h> |
45 | #include <kern/spl.h> |
46 | #include <kern/syscall_sw.h> |
47 | #include <ipc/ipc_port.h> |
48 | #include <vm/vm_kern.h> |
49 | #include <mach/thread_status.h> |
50 | #include <vm/pmap.h> |
51 | |
52 | #include <sys/kdebug.h> |
53 | |
54 | #include <sys/syscall.h> |
55 | |
56 | #if CONFIG_MACF |
57 | #include <security/mac_mach_internal.h> |
58 | #endif |
59 | |
60 | extern void throttle_lowpri_io(int); |
61 | extern arm_debug_state64_t *find_or_allocate_debug_state64(thread_t thread); |
62 | void mach_syscall(struct arm_saved_state*); |
63 | typedef kern_return_t (*mach_call_t)(void *); |
64 | |
65 | struct mach_call_args { |
66 | syscall_arg_t arg1; |
67 | syscall_arg_t arg2; |
68 | syscall_arg_t arg3; |
69 | syscall_arg_t arg4; |
70 | syscall_arg_t arg5; |
71 | syscall_arg_t arg6; |
72 | syscall_arg_t arg7; |
73 | syscall_arg_t arg8; |
74 | syscall_arg_t arg9; |
75 | }; |
76 | |
77 | static void |
78 | arm_set_mach_syscall_ret(struct arm_saved_state *state, int retval) |
79 | { |
80 | if (is_saved_state32(iss: state)) { |
81 | saved_state32(iss: state)->r[0] = retval; |
82 | } else { |
83 | saved_state64(iss: state)->x[0] = retval; |
84 | } |
85 | } |
86 | |
87 | static kern_return_t |
88 | arm_get_mach_syscall_args(struct arm_saved_state *state, struct mach_call_args *dest, const mach_trap_t *trapp) |
89 | { |
90 | uint32_t reg_count; |
91 | |
92 | if (is_saved_state32(iss: state)) { |
93 | /* The trap table entry defines the number of 32-bit words to be copied in from userspace. */ |
94 | reg_count = trapp->mach_trap_u32_words; |
95 | |
96 | /* |
97 | * We get 7 contiguous words; r0-r6, hop over r7 |
98 | * (frame pointer), optionally r8 |
99 | */ |
100 | if (reg_count <= 7) { |
101 | bcopy(src: (char*)saved_state32(iss: state), dst: (char*)dest, n: sizeof(uint32_t) * reg_count); |
102 | } else if (reg_count <= 9) { |
103 | bcopy(src: (char*)saved_state32(iss: state), dst: (char*)dest, n: sizeof(uint32_t) * 7); |
104 | bcopy(src: (char*)&saved_state32(iss: state)->r[8], dst: ((char*)dest) + sizeof(uint32_t) * 7, |
105 | n: reg_count - 7); |
106 | } else { |
107 | panic("Trap with %d words of args? We only support 9." , reg_count); |
108 | } |
109 | |
110 | #if CONFIG_REQUIRES_U32_MUNGING |
111 | trapp->mach_trap_arg_munge32(dest); |
112 | #else |
113 | #error U32 mach traps on ARM64 kernel requires munging |
114 | #endif |
115 | } else { |
116 | assert(is_saved_state64(state)); |
117 | bcopy(src: (char*)saved_state64(iss: state), dst: (char*)dest, n: trapp->mach_trap_arg_count * sizeof(uint64_t)); |
118 | } |
119 | |
120 | return KERN_SUCCESS; |
121 | } |
122 | |
123 | /** |
124 | * Marks or unmarks the given thread to be single stepped such |
125 | * that it executes exactly one instruction and then takes an exception to |
126 | * prevent further execution. |
127 | * |
128 | * @param thread 64 bit thread to be single stepped |
129 | * @param on boolean value representing whether the thread should be |
130 | * single stepped (on is true) or not (on is false) |
131 | * |
132 | * @returns KERN_SUCCESS if the status is successfully set or KERN_FAILURE if |
133 | * it fails for any reason. |
134 | */ |
135 | kern_return_t |
136 | thread_setsinglestep(thread_t thread, int on) |
137 | { |
138 | arm_debug_state64_t *thread_state = find_or_allocate_debug_state64(thread); |
139 | |
140 | if (thread_state == NULL) { |
141 | return KERN_FAILURE; |
142 | } |
143 | |
144 | if (on) { |
145 | thread_state->mdscr_el1 |= MDSCR_SS; |
146 | } else { |
147 | thread_state->mdscr_el1 &= ~MDSCR_SS; |
148 | } |
149 | |
150 | if (thread == current_thread()) { |
151 | arm_debug_set64(debug_state: thread->machine.DebugData); |
152 | } |
153 | return KERN_SUCCESS; |
154 | } |
155 | |
156 | #if CONFIG_DTRACE |
157 | |
158 | vm_offset_t dtrace_get_cpu_int_stack_top(void); |
159 | |
160 | vm_offset_t |
161 | dtrace_get_cpu_int_stack_top(void) |
162 | { |
163 | return getCpuDatap()->intstack_top; |
164 | } |
165 | #endif /* CONFIG_DTRACE */ |
166 | |
167 | /* ARM64_TODO: remove this. still TODO?*/ |
168 | extern struct proc* current_proc(void); |
169 | extern int proc_pid(struct proc*); |
170 | |
171 | #if CONFIG_DEBUG_SYSCALL_REJECTION |
172 | extern int debug_syscall_rejection_mode; |
173 | extern bool debug_syscall_rejection_handle(int syscall_mach_trap_number); |
174 | #endif /* CONFIG_DEBUG_SYSCALL_REJECTION */ |
175 | |
176 | void |
177 | mach_syscall(struct arm_saved_state *state) |
178 | { |
179 | kern_return_t retval; |
180 | mach_call_t mach_call; |
181 | struct mach_call_args args = { |
182 | .arg1 = 0, |
183 | .arg2 = 0, |
184 | .arg3 = 0, |
185 | .arg4 = 0, |
186 | .arg5 = 0, |
187 | .arg6 = 0, |
188 | .arg7 = 0, |
189 | .arg8 = 0, |
190 | .arg9 = 0 |
191 | }; |
192 | int call_number = get_saved_state_svc_number(iss: state); |
193 | int64_t exc_code; |
194 | int argc; |
195 | |
196 | struct uthread *ut = get_bsdthread_info(current_thread()); |
197 | uthread_reset_proc_refcount(ut); |
198 | |
199 | assert(call_number < 0); /* Otherwise it would be a Unix syscall */ |
200 | call_number = -call_number; |
201 | |
202 | if (call_number >= MACH_TRAP_TABLE_COUNT) { |
203 | goto bad; |
204 | } |
205 | |
206 | DEBUG_KPRINT_SYSCALL_MACH( |
207 | "mach_syscall: code=%d(%s) (pid %d, tid %lld)\n" , |
208 | call_number, mach_syscall_name_table[call_number], |
209 | proc_pid(current_proc()), thread_tid(current_thread())); |
210 | |
211 | #if DEBUG_TRACE |
212 | kprintf("mach_syscall(0x%08x) code=%d\n" , state, call_number); |
213 | #endif |
214 | |
215 | mach_call = (mach_call_t)mach_trap_table[call_number].mach_trap_function; |
216 | |
217 | if (mach_call == (mach_call_t)kern_invalid) { |
218 | DEBUG_KPRINT_SYSCALL_MACH( |
219 | "mach_syscall: kern_invalid 0x%x\n" , call_number); |
220 | goto bad; |
221 | } |
222 | |
223 | argc = mach_trap_table[call_number].mach_trap_arg_count; |
224 | if (argc) { |
225 | retval = arm_get_mach_syscall_args(state, dest: &args, trapp: &mach_trap_table[call_number]); |
226 | if (retval != KERN_SUCCESS) { |
227 | arm_set_mach_syscall_ret(state, retval); |
228 | |
229 | DEBUG_KPRINT_SYSCALL_MACH( |
230 | "mach_syscall: retval=0x%x\n" , retval); |
231 | return; |
232 | } |
233 | } |
234 | |
235 | KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, |
236 | MACHDBG_CODE(DBG_MACH_EXCP_SC, (call_number)) | DBG_FUNC_START, |
237 | args.arg1, args.arg2, args.arg3, args.arg4, 0); |
238 | |
239 | #if CONFIG_MACF |
240 | /* |
241 | * Check syscall filter mask, if exists. |
242 | * |
243 | * Not all mach traps are filtered. e.g., mach_absolute_time() and |
244 | * mach_continuous_time(). See handle_svc(). |
245 | */ |
246 | thread_ro_t tro = current_thread_ro(); |
247 | task_t task = tro->tro_task; |
248 | struct proc *proc = tro->tro_proc; |
249 | uint8_t *filter_mask = task_get_mach_trap_filter_mask(task); |
250 | |
251 | if (__improbable(filter_mask != NULL && |
252 | !bitstr_test(filter_mask, call_number) && |
253 | mac_task_mach_trap_evaluate != NULL)) { |
254 | retval = mac_task_mach_trap_evaluate(proc, call_number); |
255 | if (retval != KERN_SUCCESS) { |
256 | if (mach_trap_table[call_number].mach_trap_returns_port) { |
257 | retval = MACH_PORT_NULL; |
258 | } |
259 | goto skip_machcall; |
260 | } |
261 | } |
262 | #endif /* CONFIG_MACF */ |
263 | |
264 | #if CONFIG_DEBUG_SYSCALL_REJECTION |
265 | bitmap_t const *rejection_mask = uthread_get_syscall_rejection_mask(ut); |
266 | if (__improbable(rejection_mask != NULL && |
267 | uthread_syscall_rejection_is_enabled(ut)) && |
268 | !bitmap_test(rejection_mask, call_number)) { |
269 | if (debug_syscall_rejection_handle(-call_number)) { |
270 | if (mach_trap_table[call_number].mach_trap_returns_port) { |
271 | retval = MACH_PORT_NULL; |
272 | } else { |
273 | retval = KERN_DENIED; |
274 | } |
275 | goto skip_machcall; |
276 | } |
277 | } |
278 | #endif /* CONFIG_DEBUG_SYSCALL_REJECTION */ |
279 | |
280 | |
281 | retval = mach_call(&args); |
282 | |
283 | skip_machcall: |
284 | |
285 | DEBUG_KPRINT_SYSCALL_MACH("mach_syscall: retval=0x%x (pid %d, tid %lld)\n" , retval, |
286 | proc_pid(current_proc()), thread_tid(current_thread())); |
287 | |
288 | KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, |
289 | MACHDBG_CODE(DBG_MACH_EXCP_SC, (call_number)) | DBG_FUNC_END, |
290 | retval, 0, 0, 0, 0); |
291 | |
292 | arm_set_mach_syscall_ret(state, retval); |
293 | |
294 | throttle_lowpri_io(1); |
295 | |
296 | #if DEBUG || DEVELOPMENT |
297 | kern_allocation_name_t |
298 | prior __assert_only = thread_get_kernel_state(current_thread())->allocation_name; |
299 | assertf(prior == NULL, "thread_set_allocation_name(\"%s\") not cleared" , kern_allocation_get_name(prior)); |
300 | #endif /* DEBUG || DEVELOPMENT */ |
301 | |
302 | uthread_assert_zero_proc_refcount(ut); |
303 | return; |
304 | |
305 | bad: |
306 | exc_code = call_number; |
307 | exception_triage(EXC_SYSCALL, code: &exc_code, codeCnt: 1); |
308 | /* NOTREACHED */ |
309 | panic("Returned from exception_triage()?" ); |
310 | } |
311 | #endif /* MACH_BSD */ |
312 | |