X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/3e170ce000f1506b7b5d2c5c7faec85ceabb573d..a39ff7e25e19b3a8c3020042a3872ca9ec9659f1:/bsd/kern/sys_generic.c diff --git a/bsd/kern/sys_generic.c b/bsd/kern/sys_generic.c index d6c46f58d..9cfbe7e91 100644 --- a/bsd/kern/sys_generic.c +++ b/bsd/kern/sys_generic.c @@ -97,6 +97,7 @@ #include #include +#include #include #include #include @@ -140,13 +141,16 @@ #include #include +#if CONFIG_MACF +#include +#endif + /* XXX should be in a header file somewhere */ void evsofree(struct socket *); void evpipefree(struct pipe *); void postpipeevent(struct pipe *, int); void postevent(struct socket *, struct sockbuf *, int); extern kern_return_t IOBSDGetPlatformUUID(__darwin_uuid_t uuid, mach_timespec_t timeoutp); -extern void delay(int); int rd_uio(struct proc *p, int fdes, uio_t uio, user_ssize_t *retval); int wr_uio(struct proc *p, struct fileproc *fp, uio_t uio, user_ssize_t *retval); @@ -160,7 +164,6 @@ __private_extern__ int dofilewrite(vfs_context_t ctx, struct fileproc *fp, __private_extern__ int preparefileread(struct proc *p, struct fileproc **fp_ret, int fd, int check_for_vnode); __private_extern__ void donefileread(struct proc *p, struct fileproc *fp_ret, int fd); - /* Conflict wait queue for when selects collide (opaque type) */ struct waitq select_conflict_queue; @@ -171,7 +174,7 @@ void select_waitq_init(void); void select_waitq_init(void) { - waitq_init(&select_conflict_queue, SYNC_POLICY_FIFO | SYNC_POLICY_DISABLE_IRQ); + waitq_init(&select_conflict_queue, SYNC_POLICY_FIFO); } #define f_flag f_fglob->fg_flag @@ -771,7 +774,7 @@ ioctl(struct proc *p, struct ioctl_args *uap, __unused int32_t *retval) boolean_t is64bit = FALSE; int tmp = 0; #define STK_PARAMS 128 - char stkbuf[STK_PARAMS]; + char stkbuf[STK_PARAMS] = {}; int fd = uap->fd; u_long com = uap->com; struct vfs_context context = *vfs_context_current(); @@ -940,6 +943,7 @@ static int selscan(struct proc *p, struct _select * sel, struct _select_data * s static int selcount(struct proc *p, u_int32_t *ibits, int nfd, int *count); static int seldrop_locked(struct proc *p, u_int32_t *ibits, int nfd, int lim, int *need_wakeup, int fromselcount); static int seldrop(struct proc *p, u_int32_t *ibits, int nfd); +static int select_internal(struct proc *p, struct select_nocancel_args *uap, uint64_t timeout, int32_t *retval); /* * Select system call. @@ -947,17 +951,126 @@ static int seldrop(struct proc *p, u_int32_t *ibits, int nfd); * Returns: 0 Success * EINVAL Invalid argument * EAGAIN Nonconformant error if allocation fails - * selprocess:??? */ int select(struct proc *p, struct select_args *uap, int32_t *retval) { __pthread_testcancel(1); - return(select_nocancel(p, (struct select_nocancel_args *)uap, retval)); + return select_nocancel(p, (struct select_nocancel_args *)uap, retval); } int select_nocancel(struct proc *p, struct select_nocancel_args *uap, int32_t *retval) +{ + uint64_t timeout = 0; + + if (uap->tv) { + int err; + struct timeval atv; + if (IS_64BIT_PROCESS(p)) { + struct user64_timeval atv64; + err = copyin(uap->tv, (caddr_t)&atv64, sizeof(atv64)); + /* Loses resolution - assume timeout < 68 years */ + atv.tv_sec = atv64.tv_sec; + atv.tv_usec = atv64.tv_usec; + } else { + struct user32_timeval atv32; + err = copyin(uap->tv, (caddr_t)&atv32, sizeof(atv32)); + atv.tv_sec = atv32.tv_sec; + atv.tv_usec = atv32.tv_usec; + } + if (err) + return err; + + if (itimerfix(&atv)) { + err = EINVAL; + return err; + } + + clock_absolutetime_interval_to_deadline(tvtoabstime(&atv), &timeout); + } + + return select_internal(p, uap, timeout, retval); +} + +int +pselect(struct proc *p, struct pselect_args *uap, int32_t *retval) +{ + __pthread_testcancel(1); + return pselect_nocancel(p, (struct pselect_nocancel_args *)uap, retval); +} + +int +pselect_nocancel(struct proc *p, struct pselect_nocancel_args *uap, int32_t *retval) +{ + int err; + struct uthread *ut; + uint64_t timeout = 0; + + if (uap->ts) { + struct timespec ts; + + if (IS_64BIT_PROCESS(p)) { + struct user64_timespec ts64; + err = copyin(uap->ts, (caddr_t)&ts64, sizeof(ts64)); + ts.tv_sec = ts64.tv_sec; + ts.tv_nsec = ts64.tv_nsec; + } else { + struct user32_timespec ts32; + err = copyin(uap->ts, (caddr_t)&ts32, sizeof(ts32)); + ts.tv_sec = ts32.tv_sec; + ts.tv_nsec = ts32.tv_nsec; + } + if (err) { + return err; + } + + if (!timespec_is_valid(&ts)) { + return EINVAL; + } + clock_absolutetime_interval_to_deadline(tstoabstime(&ts), &timeout); + } + + ut = get_bsdthread_info(current_thread()); + + if (uap->mask != USER_ADDR_NULL) { + /* save current mask, then copyin and set new mask */ + sigset_t newset; + err = copyin(uap->mask, &newset, sizeof(sigset_t)); + if (err) { + return err; + } + ut->uu_oldmask = ut->uu_sigmask; + ut->uu_flag |= UT_SAS_OLDMASK; + ut->uu_sigmask = (newset & ~sigcantmask); + } + + err = select_internal(p, (struct select_nocancel_args *)uap, timeout, retval); + + if (err != EINTR && ut->uu_flag & UT_SAS_OLDMASK) { + /* + * Restore old mask (direct return case). NOTE: EINTR can also be returned + * if the thread is cancelled. In that case, we don't reset the signal + * mask to its original value (which usually happens in the signal + * delivery path). This behavior is permitted by POSIX. + */ + ut->uu_sigmask = ut->uu_oldmask; + ut->uu_oldmask = 0; + ut->uu_flag &= ~UT_SAS_OLDMASK; + } + + return err; +} + +/* + * Generic implementation of {,p}select. Care: we type-pun uap across the two + * syscalls, which differ slightly. The first 4 arguments (nfds and the fd sets) + * are identical. The 5th (timeout) argument points to different types, so we + * unpack in the syscall-specific code, but the generic code still does a null + * check on this argument to determine if a timeout was specified. + */ +static int +select_internal(struct proc *p, struct select_nocancel_args *uap, uint64_t timeout, int32_t *retval) { int error = 0; u_int ni, nw; @@ -1049,32 +1162,7 @@ select_nocancel(struct proc *p, struct select_nocancel_args *uap, int32_t *retva getbits(ex, 2); #undef getbits - if (uap->tv) { - struct timeval atv; - if (IS_64BIT_PROCESS(p)) { - struct user64_timeval atv64; - error = copyin(uap->tv, (caddr_t)&atv64, sizeof(atv64)); - /* Loses resolution - assume timeout < 68 years */ - atv.tv_sec = atv64.tv_sec; - atv.tv_usec = atv64.tv_usec; - } else { - struct user32_timeval atv32; - error = copyin(uap->tv, (caddr_t)&atv32, sizeof(atv32)); - atv.tv_sec = atv32.tv_sec; - atv.tv_usec = atv32.tv_usec; - } - if (error) - goto continuation; - if (itimerfix(&atv)) { - error = EINVAL; - goto continuation; - } - - clock_absolutetime_interval_to_deadline( - tvtoabstime(&atv), &seldata->abstime); - } - else - seldata->abstime = 0; + seldata->abstime = timeout; if ( (error = selcount(p, sel->ibits, uap->nd, &count)) ) { goto continuation; @@ -1119,12 +1207,12 @@ select_nocancel(struct proc *p, struct select_nocancel_args *uap, int32_t *retva panic("can't allocate %ld bytes for wqstate buffer", uth->uu_wqstate_sz); waitq_set_init(uth->uu_wqset, - SYNC_POLICY_FIFO|SYNC_POLICY_PREPOST|SYNC_POLICY_DISABLE_IRQ, NULL); + SYNC_POLICY_FIFO|SYNC_POLICY_PREPOST, NULL, NULL); } if (!waitq_set_is_valid(uth->uu_wqset)) waitq_set_init(uth->uu_wqset, - SYNC_POLICY_FIFO|SYNC_POLICY_PREPOST|SYNC_POLICY_DISABLE_IRQ, NULL); + SYNC_POLICY_FIFO|SYNC_POLICY_PREPOST, NULL, NULL); /* the last chunk of our buffer is an array of waitq pointers */ seldata->wqp = (uint64_t *)((char *)(uth->uu_wqset) + ALIGN(sizeof(struct waitq_set))); @@ -1306,6 +1394,14 @@ done: putbits(ex, 2); #undef putbits } + + if (error != EINTR && sel_pass == SEL_SECONDPASS && uth->uu_flag & UT_SAS_OLDMASK) { + /* restore signal mask - continuation case */ + uth->uu_sigmask = uth->uu_oldmask; + uth->uu_oldmask = 0; + uth->uu_flag &= ~UT_SAS_OLDMASK; + } + return(error); } @@ -1598,7 +1694,7 @@ poll_nocancel(struct proc *p, struct poll_nocancel_args *uap, int32_t *retval) (nfds > p->p_rlimit[RLIMIT_NOFILE].rlim_cur && (proc_suser(p) || nfds > FD_SETSIZE))) return (EINVAL); - kq = kqueue_alloc(p); + kq = kqueue_alloc(p, 0); if (kq == NULL) return (EAGAIN); @@ -1635,7 +1731,6 @@ poll_nocancel(struct proc *p, struct poll_nocancel_args *uap, int32_t *retval) OSBitOrAtomic(P_SELECT, &p->p_flag); for (i = 0; i < nfds; i++) { short events = fds[i].events; - int kerror = 0; /* per spec, ignore fd values below zero */ if (fds[i].fd < 0) { @@ -1654,19 +1749,19 @@ poll_nocancel(struct proc *p, struct poll_nocancel_args *uap, int32_t *retval) kev.filter = EVFILT_READ; if (events & ( POLLPRI | POLLRDBAND )) kev.flags |= EV_OOBAND; - kerror = kevent_register(kq, &kev, p); + kevent_register(kq, &kev, p); } /* Handle output events */ - if (kerror == 0 && - events & ( POLLOUT | POLLWRNORM | POLLWRBAND )) { + if ((kev.flags & EV_ERROR) == 0 && + (events & ( POLLOUT | POLLWRNORM | POLLWRBAND ))) { kev.filter = EVFILT_WRITE; - kerror = kevent_register(kq, &kev, p); + kevent_register(kq, &kev, p); } /* Handle BSD extension vnode events */ - if (kerror == 0 && - events & ( POLLEXTEND | POLLATTRIB | POLLNLINK | POLLWRITE )) { + if ((kev.flags & EV_ERROR) == 0 && + (events & ( POLLEXTEND | POLLATTRIB | POLLNLINK | POLLWRITE ))) { kev.filter = EVFILT_VNODE; kev.fflags = 0; if (events & POLLEXTEND) @@ -1677,25 +1772,40 @@ poll_nocancel(struct proc *p, struct poll_nocancel_args *uap, int32_t *retval) kev.fflags |= NOTE_LINK; if (events & POLLWRITE) kev.fflags |= NOTE_WRITE; - kerror = kevent_register(kq, &kev, p); + kevent_register(kq, &kev, p); } - if (kerror != 0) { + if (kev.flags & EV_ERROR) { fds[i].revents = POLLNVAL; rfds++; } else fds[i].revents = 0; } - /* Did we have any trouble registering? */ - if (rfds > 0) + /* + * Did we have any trouble registering? + * If user space passed 0 FDs, then respect any timeout value passed. + * This is an extremely inefficient sleep. If user space passed one or + * more FDs, and we had trouble registering _all_ of them, then bail + * out. If a subset of the provided FDs failed to register, then we + * will still call the kqueue_scan function. + */ + if (nfds && (rfds == nfds)) goto done; + /* + * If any events have trouble registering, an event has fired and we + * shouldn't wait for events in kqueue_scan -- use the current time as + * the deadline. + */ + if (rfds) + getmicrouptime(&atv); + /* scan for, and possibly wait for, the kevents to trigger */ cont->pca_fds = uap->fds; cont->pca_nfds = nfds; cont->pca_rfds = rfds; - error = kqueue_scan(kq, poll_callback, NULL, cont, &atv, p); + error = kqueue_scan(kq, poll_callback, NULL, cont, NULL, &atv, p); rfds = cont->pca_rfds; done: @@ -2011,7 +2121,7 @@ selrecord(__unused struct proc *selector, struct selinfo *sip, void *s_data) return; if ((sip->si_flags & SI_INITED) == 0) { - waitq_init(&sip->si_waitq, SYNC_POLICY_FIFO | SYNC_POLICY_DISABLE_IRQ); + waitq_init(&sip->si_waitq, SYNC_POLICY_FIFO); sip->si_flags |= SI_INITED; sip->si_flags &= ~SI_CLEAR; } @@ -3005,6 +3115,14 @@ gethostuuid(struct proc *p, struct gethostuuid_args *uap, __unused int32_t *retv __darwin_uuid_t uuid_kern; /* for IOKit call */ if (!uap->spi) { +#if CONFIG_EMBEDDED +#if CONFIG_MACF + if ((error = mac_system_check_info(kauth_cred_get(), "hw.uuid")) != 0) { + /* EPERM invokes userspace upcall if present */ + return (error); + } +#endif +#endif } /* Convert the 32/64 bit timespec into a mach_timespec_t */ @@ -3066,10 +3184,15 @@ ledger(struct proc *p, struct ledger_args *args, __unused int32_t *retval) error = copyin(args->arg3, (char *)&len, sizeof (len)); else if (args->cmd == LEDGER_TEMPLATE_INFO) error = copyin(args->arg2, (char *)&len, sizeof (len)); -#ifdef LEDGER_DEBUG else if (args->cmd == LEDGER_LIMIT) +#ifdef LEDGER_DEBUG error = copyin(args->arg2, (char *)&lla, sizeof (lla)); +#else + return (EINVAL); #endif + else if ((args->cmd < 0) || (args->cmd > LEDGER_MAX_CMD)) + return (EINVAL); + if (error) return (error); if (len < 0) @@ -3120,7 +3243,7 @@ ledger(struct proc *p, struct ledger_args *args, __unused int32_t *retval) rval = ledger_get_task_entry_info_multiple(task, &buf, &len); proc_rele(proc); - if ((rval == 0) && (len > 0)) { + if ((rval == 0) && (len >= 0)) { sz = len * sizeof (struct ledger_entry_info); rval = copyout(buf, args->arg2, sz); kfree(buf, sz); @@ -3135,7 +3258,7 @@ ledger(struct proc *p, struct ledger_args *args, __unused int32_t *retval) int sz; rval = ledger_template_info(&buf, &len); - if ((rval == 0) && (len > 0)) { + if ((rval == 0) && (len >= 0)) { sz = len * sizeof (struct ledger_template_info); rval = copyout(buf, args->arg1, sz); kfree(buf, sz); @@ -3146,6 +3269,8 @@ ledger(struct proc *p, struct ledger_args *args, __unused int32_t *retval) } default: + panic("ledger syscall logic error -- command type %d", args->cmd); + proc_rele(proc); rval = EINVAL; } @@ -3176,7 +3301,7 @@ telemetry(__unused struct proc *p, struct telemetry_args *args, __unused int32_t return (error); } -#if defined(DEVELOPMENT) || defined(DEBUG) +#if DEVELOPMENT || DEBUG #if CONFIG_WAITQ_DEBUG static uint64_t g_wqset_num = 0; struct g_wqset { @@ -3205,7 +3330,7 @@ static inline struct waitq_set *sysctl_get_wqset(int idx) gwqs = (struct g_wqset *)kalloc(sizeof(*gwqs)); assert(gwqs != NULL); - gwqs->wqset = waitq_set_alloc(SYNC_POLICY_FIFO|SYNC_POLICY_PREPOST|SYNC_POLICY_DISABLE_IRQ); + gwqs->wqset = waitq_set_alloc(SYNC_POLICY_FIFO|SYNC_POLICY_PREPOST, NULL); enqueue_tail(&g_wqset_list, &gwqs->link); printf("[WQ]: created new waitq set 0x%llx\n", wqset_id(gwqs->wqset)); @@ -3224,7 +3349,7 @@ static inline struct waitq *global_test_waitq(int idx) if (!g_wq_init) { g_wq_init = 1; for (int i = 0; i < MAX_GLOBAL_TEST_QUEUES; i++) - waitq_init(&g_wq[i], SYNC_POLICY_FIFO|SYNC_POLICY_DISABLE_IRQ); + waitq_init(&g_wq[i], SYNC_POLICY_FIFO); } return &g_wq[idx % MAX_GLOBAL_TEST_QUEUES]; @@ -3556,4 +3681,6 @@ SYSCTL_PROC(_kern, OID_AUTO, wqset_clear_preposts, CTLTYPE_QUAD | CTLFLAG_RW | C 0, 0, sysctl_wqset_clear_preposts, "Q", "clear preposts on given waitq set"); #endif /* CONFIG_WAITQ_DEBUG */ -#endif /* defined(DEVELOPMENT) || defined(DEBUG) */ +#endif /* DEVELOPMENT || DEBUG */ + +