X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/fe8ab488e9161c46dd9885d58fc52996dc0249ff..4ba76501152d51ccb5647018f3192c6096367d48:/bsd/dev/i386/systemcalls.c?ds=inline diff --git a/bsd/dev/i386/systemcalls.c b/bsd/dev/i386/systemcalls.c index 8b2c8ab98..a5a7255bd 100644 --- a/bsd/dev/i386/systemcalls.c +++ b/bsd/dev/i386/systemcalls.c @@ -1,8 +1,8 @@ /* - * Copyright (c) 2000-2008 Apple Inc. All rights reserved. + * Copyright (c) 2000-2016 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -11,10 +11,10 @@ * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. - * + * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -22,7 +22,7 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #include @@ -34,7 +34,6 @@ #include #include #include -#include #include #include @@ -48,6 +47,7 @@ #include #include #include +#include #include @@ -57,6 +57,10 @@ #include +#if CONFIG_MACF +#include +#endif + #if CONFIG_DTRACE extern int32_t dtrace_systrace_syscall(struct proc *, void *, int *); extern void dtrace_systrace_syscall_return(unsigned short, int, int *); @@ -69,6 +73,10 @@ extern void *find_user_regs(thread_t); /* dynamically generated at build time based on syscalls.master */ extern const char *syscallnames[]; +#define code_is_kdebug_trace(code) (((code) == SYS_kdebug_trace) || \ + ((code) == SYS_kdebug_trace64) || \ + ((code) == SYS_kdebug_trace_string)) + /* * Function: unix_syscall * @@ -76,72 +84,71 @@ extern const char *syscallnames[]; * * Outputs: none */ +__attribute__((noreturn)) void unix_syscall(x86_saved_state_t *state) { - thread_t thread; - void *vt; - unsigned int code; - struct sysent *callp; - - int error; - vm_offset_t params; - struct proc *p; - struct uthread *uthread; - x86_saved_state32_t *regs; - boolean_t is_vfork; + thread_t thread; + void *vt; + unsigned int code, syscode; + struct sysent *callp; + + int error; + vm_offset_t params; + struct proc *p; + struct uthread *uthread; + x86_saved_state32_t *regs; + boolean_t is_vfork; + pid_t pid; assert(is_saved_state32(state)); regs = saved_state32(state); #if DEBUG - if (regs->eax == 0x800) + if (regs->eax == 0x800) { thread_exception_return(); + } #endif thread = current_thread(); uthread = get_bsdthread_info(thread); + uthread_reset_proc_refcount(uthread); + /* Get the approriate proc; may be different from task's for vfork() */ is_vfork = uthread->uu_flag & UT_VFORK; - if (__improbable(is_vfork != 0)) + if (__improbable(is_vfork != 0)) { p = current_proc(); - else + } else { p = (struct proc *)get_bsdtask_info(current_task()); - - /* Verify that we are not being called from a task without a proc */ - if (__improbable(p == NULL)) { - regs->eax = EPERM; - regs->efl |= EFL_CF; - task_terminate_internal(current_task()); - thread_exception_return(); - /* NOTREACHED */ } - code = regs->eax & I386_SYSCALL_NUMBER_MASK; + code = regs->eax & I386_SYSCALL_NUMBER_MASK; + syscode = (code < nsysent) ? code : SYS_invalid; DEBUG_KPRINT_SYSCALL_UNIX("unix_syscall: code=%d(%s) eip=%u\n", - code, syscallnames[code >= NUM_SYSENT ? 63 : code], (uint32_t)regs->eip); - params = (vm_offset_t) (regs->uesp + sizeof (int)); + code, syscallnames[syscode], (uint32_t)regs->eip); + params = (vm_offset_t) (regs->uesp + sizeof(int)); regs->efl &= ~(EFL_CF); - callp = (code >= NUM_SYSENT) ? &sysent[63] : &sysent[code]; + callp = &sysent[syscode]; if (__improbable(callp == sysent)) { code = fuword(params); params += sizeof(int); - callp = (code >= NUM_SYSENT) ? &sysent[63] : &sysent[code]; + syscode = (code < nsysent) ? code : SYS_invalid; + callp = &sysent[syscode]; } vt = (void *)uthread->uu_arg; if (callp->sy_arg_bytes != 0) { #if CONFIG_REQUIRES_U32_MUNGING - sy_munge_t *mungerp; + sy_munge_t *mungerp; #else #error U32 syscalls on x86_64 kernel requires munging #endif - uint32_t nargs; + uint32_t nargs; - assert((unsigned) callp->sy_arg_bytes <= sizeof (uthread->uu_arg)); + assert((unsigned) callp->sy_arg_bytes <= sizeof(uthread->uu_arg)); nargs = callp->sy_arg_bytes; error = copyin((user_addr_t) params, (char *) vt, nargs); if (error) { @@ -151,24 +158,22 @@ unix_syscall(x86_saved_state_t *state) /* NOTREACHED */ } - if (__probable(code != 180)) { - int *ip = (int *)vt; - - KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, - BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START, - *ip, *(ip+1), *(ip+2), *(ip+3), 0); + if (__probable(!code_is_kdebug_trace(code))) { + uint32_t *uip = vt; + KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START, + uip[0], uip[1], uip[2], uip[3]); } #if CONFIG_REQUIRES_U32_MUNGING mungerp = callp->sy_arg_munge32; - if (mungerp != NULL) + if (mungerp != NULL) { (*mungerp)(vt); + } #endif - } else - KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, - BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START, - 0, 0, 0, 0, 0); + } else { + KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START); + } /* * Delayed binding of thread credential to process credential, if we @@ -180,19 +185,34 @@ unix_syscall(x86_saved_state_t *state) uthread->uu_rval[1] = 0; uthread->uu_flag |= UT_NOTCANCELPT; uthread->syscall_code = code; + pid = proc_pid(p); #ifdef JOE_DEBUG - uthread->uu_iocount = 0; - uthread->uu_vpindex = 0; + uthread->uu_iocount = 0; + uthread->uu_vpindex = 0; #endif +#if CONFIG_MACF + if (__improbable(p->syscall_filter_mask != NULL && !bitstr_test(p->syscall_filter_mask, syscode))) { + error = mac_proc_check_syscall_unix(p, syscode); + if (error) { + goto skip_syscall; + } + } +#endif /* CONFIG_MACF */ + AUDIT_SYSCALL_ENTER(code, p, uthread); error = (*(callp->sy_call))((void *) p, (void *) vt, &(uthread->uu_rval[0])); AUDIT_SYSCALL_EXIT(code, p, uthread, error); +#if CONFIG_MACF +skip_syscall: +#endif /* CONFIG_MACF */ + #ifdef JOE_DEBUG - if (uthread->uu_iocount) - printf("system call returned with uu_iocount != 0\n"); + if (uthread->uu_iocount) { + printf("system call returned with uu_iocount != 0\n"); + } #endif #if CONFIG_DTRACE uthread->t_dtrace_errno = error; @@ -207,20 +227,19 @@ unix_syscall(x86_saved_state_t *state) */ pal_syscall_restart(thread, state); - } - else if (error != EJUSTRETURN) { + } else if (error != EJUSTRETURN) { if (__improbable(error)) { - regs->eax = error; - regs->efl |= EFL_CF; /* carry bit */ + regs->eax = error; + regs->efl |= EFL_CF; /* carry bit */ } else { /* (not error) */ - /* - * We split retval across two registers, in case the - * syscall had a 64-bit return value, in which case - * eax/edx matches the function call ABI. - */ - regs->eax = uthread->uu_rval[0]; - regs->edx = uthread->uu_rval[1]; - } + /* + * We split retval across two registers, in case the + * syscall had a 64-bit return value, in which case + * eax/edx matches the function call ABI. + */ + regs->eax = uthread->uu_rval[0]; + regs->edx = uthread->uu_rval[1]; + } } DEBUG_KPRINT_SYSCALL_UNIX( @@ -228,9 +247,16 @@ unix_syscall(x86_saved_state_t *state) error, regs->eax, regs->edx); uthread->uu_flag &= ~UT_NOTCANCELPT; + uthread->syscall_code = 0; + +#if DEBUG || DEVELOPMENT + kern_allocation_name_t + prior __assert_only = thread_set_allocation_name(NULL); + assertf(prior == NULL, "thread_set_allocation_name(\"%s\") not cleared", kern_allocation_get_name(prior)); +#endif /* DEBUG || DEVELOPMENT */ if (__improbable(uthread->uu_lowpri_window)) { - /* + /* * task is marked as a low priority I/O type * and the I/O we issued while in this system call * collided with normal I/O operations... we'll @@ -239,48 +265,59 @@ unix_syscall(x86_saved_state_t *state) */ throttle_lowpri_io(1); } - if (__probable(code != 180)) - KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, - BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_END, - error, uthread->uu_rval[0], uthread->uu_rval[1], p->p_pid, 0); + if (__probable(!code_is_kdebug_trace(code))) { + KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_END, + error, uthread->uu_rval[0], uthread->uu_rval[1], pid); + } if (__improbable(!is_vfork && callp->sy_call == (sy_call_t *)execve && !error)) { pal_execve_return(thread); } +#if PROC_REF_DEBUG + if (__improbable(uthread_get_proc_refcount(uthread) != 0)) { + panic("system call returned with uu_proc_refcount != 0"); + } +#endif + thread_exception_return(); /* NOTREACHED */ } - +__attribute__((noreturn)) void unix_syscall64(x86_saved_state_t *state) { - thread_t thread; - void *vt; - unsigned int code; - struct sysent *callp; - int args_in_regs; - boolean_t args_start_at_rdi; - int error; - struct proc *p; - struct uthread *uthread; + thread_t thread; + void *vt; + unsigned int code, syscode; + struct sysent *callp; + int args_in_regs; + boolean_t args_start_at_rdi; + int error; + struct proc *p; + struct uthread *uthread; x86_saved_state64_t *regs; + pid_t pid; assert(is_saved_state64(state)); regs = saved_state64(state); -#if DEBUG - if (regs->rax == 0x2000800) +#if DEBUG + if (regs->rax == 0x2000800) { thread_exception_return(); + } #endif thread = current_thread(); uthread = get_bsdthread_info(thread); + uthread_reset_proc_refcount(uthread); + /* Get the approriate proc; may be different from task's for vfork() */ - if (__probable(!(uthread->uu_flag & UT_VFORK))) + if (__probable(!(uthread->uu_flag & UT_VFORK))) { p = (struct proc *)get_bsdtask_info(current_task()); - else + } else { p = current_proc(); + } /* Verify that we are not being called from a task without a proc */ if (__improbable(p == NULL)) { @@ -291,21 +328,23 @@ unix_syscall64(x86_saved_state_t *state) /* NOTREACHED */ } - code = regs->rax & SYSCALL_NUMBER_MASK; + code = regs->rax & SYSCALL_NUMBER_MASK; + syscode = (code < nsysent) ? code : SYS_invalid; DEBUG_KPRINT_SYSCALL_UNIX( "unix_syscall64: code=%d(%s) rip=%llx\n", - code, syscallnames[code >= NUM_SYSENT ? 63 : code], regs->isf.rip); - callp = (code >= NUM_SYSENT) ? &sysent[63] : &sysent[code]; + code, syscallnames[syscode], regs->isf.rip); + callp = &sysent[syscode]; vt = (void *)uthread->uu_arg; if (__improbable(callp == sysent)) { - /* + /* * indirect system call... system call number * passed as 'arg0' */ - code = regs->rdi; - callp = (code >= NUM_SYSENT) ? &sysent[63] : &sysent[code]; + code = regs->rdi; + syscode = (code < nsysent) ? code : SYS_invalid; + callp = &sysent[syscode]; args_start_at_rdi = FALSE; args_in_regs = 5; } else { @@ -319,13 +358,11 @@ unix_syscall64(x86_saved_state_t *state) args_in_regs = MIN(args_in_regs, callp->sy_narg); memcpy(vt, args_start_at_rdi ? ®s->rdi : ®s->rsi, args_in_regs * sizeof(syscall_arg_t)); + if (!code_is_kdebug_trace(code)) { + uint64_t *uip = vt; - if (code != 180) { - uint64_t *ip = (uint64_t *)vt; - - KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, - BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START, - (int)(*ip), (int)(*(ip+1)), (int)(*(ip+2)), (int)(*(ip+3)), 0); + KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START, + uip[0], uip[1], uip[2], uip[3]); } if (__improbable(callp->sy_narg > args_in_regs)) { @@ -341,10 +378,9 @@ unix_syscall64(x86_saved_state_t *state) /* NOTREACHED */ } } - } else - KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, - BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START, - 0, 0, 0, 0, 0); + } else { + KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_START); + } /* * Delayed binding of thread credential to process credential, if we @@ -356,25 +392,40 @@ unix_syscall64(x86_saved_state_t *state) uthread->uu_rval[1] = 0; uthread->uu_flag |= UT_NOTCANCELPT; uthread->syscall_code = code; + pid = proc_pid(p); #ifdef JOE_DEBUG - uthread->uu_iocount = 0; - uthread->uu_vpindex = 0; + uthread->uu_iocount = 0; + uthread->uu_vpindex = 0; #endif +#if CONFIG_MACF + if (__improbable(p->syscall_filter_mask != NULL && !bitstr_test(p->syscall_filter_mask, syscode))) { + error = mac_proc_check_syscall_unix(p, syscode); + if (error) { + goto skip_syscall; + } + } +#endif /* CONFIG_MACF */ + AUDIT_SYSCALL_ENTER(code, p, uthread); error = (*(callp->sy_call))((void *) p, vt, &(uthread->uu_rval[0])); AUDIT_SYSCALL_EXIT(code, p, uthread, error); +#if CONFIG_MACF +skip_syscall: +#endif /* CONFIG_MACF */ + #ifdef JOE_DEBUG - if (uthread->uu_iocount) - printf("system call returned with uu_iocount != 0\n"); + if (uthread->uu_iocount) { + printf("system call returned with uu_iocount != 0\n"); + } #endif #if CONFIG_DTRACE uthread->t_dtrace_errno = error; #endif /* CONFIG_DTRACE */ - + if (__improbable(error == ERESTART)) { /* * all system calls come through via the syscall instruction @@ -382,13 +433,11 @@ unix_syscall64(x86_saved_state_t *state) * move the user's pc back to repeat the syscall: */ pal_syscall_restart( thread, state ); - } - else if (error != EJUSTRETURN) { + } else if (error != EJUSTRETURN) { if (__improbable(error)) { regs->rax = error; - regs->isf.rflags |= EFL_CF; /* carry bit */ + regs->isf.rflags |= EFL_CF; /* carry bit */ } else { /* (not error) */ - switch (callp->sy_return_type) { case _SYSCALL_RET_INT_T: regs->rax = uthread->uu_rval[0]; @@ -403,7 +452,7 @@ unix_syscall64(x86_saved_state_t *state) case _SYSCALL_RET_SIZE_T: case _SYSCALL_RET_SSIZE_T: case _SYSCALL_RET_UINT64_T: - regs->rax = *((uint64_t *)(&uthread->uu_rval[0])); + regs->rax = *((uint64_t *)(&uthread->uu_rval[0])); regs->rdx = 0; break; case _SYSCALL_RET_NONE: @@ -413,17 +462,24 @@ unix_syscall64(x86_saved_state_t *state) break; } regs->isf.rflags &= ~EFL_CF; - } + } } DEBUG_KPRINT_SYSCALL_UNIX( "unix_syscall64: error=%d retval=(%llu,%llu)\n", error, regs->rax, regs->rdx); - + uthread->uu_flag &= ~UT_NOTCANCELPT; + uthread->syscall_code = 0; + +#if DEBUG || DEVELOPMENT + kern_allocation_name_t + prior __assert_only = thread_set_allocation_name(NULL); + assertf(prior == NULL, "thread_set_allocation_name(\"%s\") not cleared", kern_allocation_get_name(prior)); +#endif /* DEBUG || DEVELOPMENT */ if (__improbable(uthread->uu_lowpri_window)) { - /* + /* * task is marked as a low priority I/O type * and the I/O we issued while in this system call * collided with normal I/O operations... we'll @@ -432,10 +488,16 @@ unix_syscall64(x86_saved_state_t *state) */ throttle_lowpri_io(1); } - if (__probable(code != 180)) - KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, - BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_END, - error, uthread->uu_rval[0], uthread->uu_rval[1], p->p_pid, 0); + if (__probable(!code_is_kdebug_trace(code))) { + KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_END, + error, uthread->uu_rval[0], uthread->uu_rval[1], pid); + } + +#if PROC_REF_DEBUG + if (__improbable(uthread_get_proc_refcount(uthread))) { + panic("system call returned with uu_proc_refcount != 0"); + } +#endif thread_exception_return(); /* NOTREACHED */ @@ -445,8 +507,8 @@ unix_syscall64(x86_saved_state_t *state) void unix_syscall_return(int error) { - thread_t thread; - struct uthread *uthread; + thread_t thread; + struct uthread *uthread; struct proc *p; unsigned int code; struct sysent *callp; @@ -464,11 +526,12 @@ unix_syscall_return(int error) regs = saved_state64(find_user_regs(thread)); code = uthread->syscall_code; - callp = (code >= NUM_SYSENT) ? &sysent[63] : &sysent[code]; + callp = (code >= nsysent) ? &sysent[SYS_invalid] : &sysent[code]; #if CONFIG_DTRACE - if (callp->sy_call == dtrace_systrace_syscall) + if (callp->sy_call == dtrace_systrace_syscall) { dtrace_systrace_syscall_return( code, error, uthread->uu_rval ); + } #endif /* CONFIG_DTRACE */ AUDIT_SYSCALL_EXIT(code, p, uthread, error); @@ -476,14 +539,12 @@ unix_syscall_return(int error) /* * repeat the syscall */ - pal_syscall_restart( thread, find_user_regs(thread) ); - } - else if (error != EJUSTRETURN) { + pal_syscall_restart( thread, find_user_regs(thread)); + } else if (error != EJUSTRETURN) { if (error) { regs->rax = error; - regs->isf.rflags |= EFL_CF; /* carry bit */ + regs->isf.rflags |= EFL_CF; /* carry bit */ } else { /* (not error) */ - switch (callp->sy_return_type) { case _SYSCALL_RET_INT_T: regs->rax = uthread->uu_rval[0]; @@ -508,38 +569,38 @@ unix_syscall_return(int error) break; } regs->isf.rflags &= ~EFL_CF; - } + } } DEBUG_KPRINT_SYSCALL_UNIX( "unix_syscall_return: error=%d retval=(%llu,%llu)\n", error, regs->rax, regs->rdx); } else { - x86_saved_state32_t *regs; + x86_saved_state32_t *regs; regs = saved_state32(find_user_regs(thread)); regs->efl &= ~(EFL_CF); code = uthread->syscall_code; - callp = (code >= NUM_SYSENT) ? &sysent[63] : &sysent[code]; + callp = (code >= nsysent) ? &sysent[SYS_invalid] : &sysent[code]; #if CONFIG_DTRACE - if (callp->sy_call == dtrace_systrace_syscall) + if (callp->sy_call == dtrace_systrace_syscall) { dtrace_systrace_syscall_return( code, error, uthread->uu_rval ); + } #endif /* CONFIG_DTRACE */ AUDIT_SYSCALL_EXIT(code, p, uthread, error); if (error == ERESTART) { - pal_syscall_restart( thread, find_user_regs(thread) ); - } - else if (error != EJUSTRETURN) { + pal_syscall_restart( thread, find_user_regs(thread)); + } else if (error != EJUSTRETURN) { if (error) { regs->eax = error; - regs->efl |= EFL_CF; /* carry bit */ + regs->efl |= EFL_CF; /* carry bit */ } else { /* (not error) */ regs->eax = uthread->uu_rval[0]; regs->edx = uthread->uu_rval[1]; - } + } } DEBUG_KPRINT_SYSCALL_UNIX( "unix_syscall_return: error=%d retval=(%u,%u)\n", @@ -549,8 +610,14 @@ unix_syscall_return(int error) uthread->uu_flag &= ~UT_NOTCANCELPT; +#if DEBUG || DEVELOPMENT + kern_allocation_name_t + prior __assert_only = thread_set_allocation_name(NULL); + assertf(prior == NULL, "thread_set_allocation_name(\"%s\") not cleared", kern_allocation_get_name(prior)); +#endif /* DEBUG || DEVELOPMENT */ + if (uthread->uu_lowpri_window) { - /* + /* * task is marked as a low priority I/O type * and the I/O we issued while in this system call * collided with normal I/O operations... we'll @@ -559,12 +626,11 @@ unix_syscall_return(int error) */ throttle_lowpri_io(1); } - if (code != 180) - KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, - BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_END, - error, uthread->uu_rval[0], uthread->uu_rval[1], p->p_pid, 0); + if (!code_is_kdebug_trace(code)) { + KDBG_RELEASE(BSDDBG_CODE(DBG_BSD_EXCP_SC, code) | DBG_FUNC_END, + error, uthread->uu_rval[0], uthread->uu_rval[1], p->p_pid); + } thread_exception_return(); /* NOTREACHED */ } -