/*
- * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <sys/poll.h>
#include <sys/event.h>
#include <sys/eventvar.h>
+#include <sys/proc.h>
+#include <sys/kauth.h>
#include <mach/mach_types.h>
#include <kern/kern_types.h>
#include <kern/kalloc.h>
#include <kern/thread.h>
#include <kern/clock.h>
+#include <kern/ledger.h>
+#include <kern/task.h>
+#if CONFIG_TELEMETRY
+#include <kern/telemetry.h>
+#endif
#include <sys/mbuf.h>
+#include <sys/domain.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/errno.h>
#include <kern/kalloc.h>
#include <sys/vnode_internal.h>
+#include <pexpert/pexpert.h>
+
/* XXX should be in a header file somewhere */
void evsofree(struct socket *);
void evpipefree(struct pipe *);
/* Conflict wait queue for when selects collide (opaque type) */
struct wait_queue select_conflict_queue;
+#if 13841988
+int temp_debug_13841988 = 0;
+#endif
+
/*
* Init routine called from bsd_init.c
*/
select_wait_queue_init(void)
{
wait_queue_init(&select_conflict_queue, SYNC_POLICY_FIFO);
+#if 13841988
+ if (PE_parse_boot_argn("temp_debug_13841988", &temp_debug_13841988, sizeof(temp_debug_13841988))) {
+ kprintf("Temporary debugging for 13841988 enabled\n");
+ }
+#endif
}
-
-#if NETAT
-extern int appletalk_inited;
-#endif /* NETAT */
-
#define f_flag f_fglob->fg_flag
-#define f_type f_fglob->fg_type
+#define f_type f_fglob->fg_ops->fo_type
#define f_msgcount f_fglob->fg_msgcount
#define f_cred f_fglob->fg_cred
#define f_ops f_fglob->fg_ops
donefileread(struct proc *p, struct fileproc *fp, int fd)
{
proc_fdlock_spin(p);
-
- fp->f_flags &= ~FP_INCHRREAD;
-
fp_drop(p, fd, fp, 1);
proc_fdunlock(p);
}
error = ENXIO;
goto out;
}
- if (vp->v_type == VCHR)
- fp->f_flags |= FP_INCHRREAD;
}
*fp_ret = fp;
/* finalize uio_t for use and do the IO
*/
- uio_calculateresid(auio);
+ error = uio_calculateresid(auio);
+ if (error) {
+ goto ExitThisRoutine;
+ }
error = rd_uio(p, uap->fd, auio, retval);
ExitThisRoutine:
/* finalize uio_t for use and do the IO
*/
- uio_calculateresid(auio);
+ error = uio_calculateresid(auio);
+ if (error) {
+ goto ExitThisRoutine;
+ }
error = wr_uio(p, uap->fd, auio, retval);
ExitThisRoutine:
int
ioctl(struct proc *p, struct ioctl_args *uap, __unused int32_t *retval)
{
- struct fileproc *fp;
- u_long com;
+ struct fileproc *fp = NULL;
int error = 0;
- u_int size;
- caddr_t datap, memp;
- boolean_t is64bit;
- int tmp;
+ u_int size = 0;
+ caddr_t datap = NULL, memp = NULL;
+ boolean_t is64bit = FALSE;
+ int tmp = 0;
#define STK_PARAMS 128
char stkbuf[STK_PARAMS];
int fd = uap->fd;
+ u_long com = uap->com;
struct vfs_context context = *vfs_context_current();
AUDIT_ARG(fd, uap->fd);
is64bit = proc_is64bit(p);
#if CONFIG_AUDIT
if (is64bit)
- AUDIT_ARG(value64, uap->com);
+ AUDIT_ARG(value64, com);
else
- AUDIT_ARG(cmd, CAST_DOWN_EXPLICIT(int, uap->com));
+ AUDIT_ARG(cmd, CAST_DOWN_EXPLICIT(int, com));
#endif /* CONFIG_AUDIT */
- proc_fdlock(p);
- error = fp_lookup(p,fd,&fp,1);
- if (error) {
- proc_fdunlock(p);
- return(error);
- }
-
- AUDIT_ARG(file, p, fp);
-
- if ((fp->f_flag & (FREAD | FWRITE)) == 0) {
- error = EBADF;
- goto out;
- }
-
- context.vc_ucred = fp->f_fglob->fg_cred;
-
-#if CONFIG_MACF
- error = mac_file_check_ioctl(context.vc_ucred, fp->f_fglob, uap->com);
- if (error)
- goto out;
-#endif
-
-#if NETAT
- /*
- * ### LD 6/11/97 Hack Alert: this is to get AppleTalk to work
- * while implementing an ATioctl system call
- */
- {
- if (appletalk_inited && ((uap->com & 0x0000FFFF) == 0xff99)) {
- u_long fixed_command;
-
-#ifdef APPLETALK_DEBUG
- kprintf("ioctl: special AppleTalk \n");
-#endif
- datap = &stkbuf[0];
- *(user_addr_t *)datap = uap->data;
- fixed_command = _IOW(0, 0xff99, uap->data);
- error = fo_ioctl(fp, fixed_command, datap, &context);
- goto out;
- }
- }
-
-#endif /* NETAT */
-
-
- switch (com = uap->com) {
- case FIONCLEX:
- *fdflags(p, uap->fd) &= ~UF_EXCLOSE;
- error =0;
- goto out;
- case FIOCLEX:
- *fdflags(p, uap->fd) |= UF_EXCLOSE;
- error =0;
- goto out;
- }
-
/*
* Interpret high order word to find amount of data to be
* copied to/from the user's address space.
*/
size = IOCPARM_LEN(com);
- if (size > IOCPARM_MAX) {
- error = ENOTTY;
- goto out;
- }
- memp = NULL;
+ if (size > IOCPARM_MAX)
+ return ENOTTY;
if (size > sizeof (stkbuf)) {
- proc_fdunlock(p);
- if ((memp = (caddr_t)kalloc(size)) == 0) {
- proc_fdlock(p);
- error = ENOMEM;
- goto out;
- }
- proc_fdlock(p);
+ if ((memp = (caddr_t)kalloc(size)) == 0)
+ return ENOMEM;
datap = memp;
} else
datap = &stkbuf[0];
- if (com&IOC_IN) {
+ if (com & IOC_IN) {
if (size) {
- proc_fdunlock(p);
error = copyin(uap->data, datap, size);
- if (error) {
- if (memp)
- kfree(memp, size);
- proc_fdlock(p);
- goto out;
- }
- proc_fdlock(p);
+ if (error)
+ goto out_nofp;
} else {
/* XXX - IOC_IN and no size? we should proably return an error here!! */
if (is64bit) {
*(uint32_t *)datap = (uint32_t)uap->data;
}
}
- } else if ((com&IOC_OUT) && size)
+ } else if ((com & IOC_OUT) && size)
/*
* Zero the buffer so the user always
* gets back something deterministic.
*/
bzero(datap, size);
- else if (com&IOC_VOID) {
+ else if (com & IOC_VOID) {
/* XXX - this is odd since IOC_VOID means no parameters */
if (is64bit) {
*(user_addr_t *)datap = uap->data;
}
}
+ proc_fdlock(p);
+ error = fp_lookup(p,fd,&fp,1);
+ if (error) {
+ proc_fdunlock(p);
+ goto out_nofp;
+ }
+
+ AUDIT_ARG(file, p, fp);
+
+ if ((fp->f_flag & (FREAD | FWRITE)) == 0) {
+ error = EBADF;
+ goto out;
+ }
+
+ context.vc_ucred = fp->f_fglob->fg_cred;
+
+#if CONFIG_MACF
+ error = mac_file_check_ioctl(context.vc_ucred, fp->f_fglob, com);
+ if (error)
+ goto out;
+#endif
+
switch (com) {
+ case FIONCLEX:
+ *fdflags(p, fd) &= ~UF_EXCLOSE;
+ break;
+
+ case FIOCLEX:
+ *fdflags(p, fd) |= UF_EXCLOSE;
+ break;
case FIONBIO:
if ( (tmp = *(int *)datap) )
tmp = *(int *)datap;
if (fp->f_type == DTYPE_SOCKET) {
((struct socket *)fp->f_data)->so_pgid = tmp;
- error = 0;
break;
}
if (fp->f_type == DTYPE_PIPE) {
case FIOGETOWN:
if (fp->f_type == DTYPE_SOCKET) {
- error = 0;
*(int *)datap = ((struct socket *)fp->f_data)->so_pgid;
break;
}
* Copy any data to user, size was
* already set and checked above.
*/
- if (error == 0 && (com&IOC_OUT) && size)
+ if (error == 0 && (com & IOC_OUT) && size)
error = copyout(datap, uap->data, (u_int)size);
break;
}
- proc_fdunlock(p);
- if (memp)
- kfree(memp, size);
- proc_fdlock(p);
out:
fp_drop(p, fd, fp, 1);
proc_fdunlock(p);
+
+out_nofp:
+ if (memp)
+ kfree(memp, size);
return(error);
}
th_act = current_thread();
uth = get_bsdthread_info(th_act);
sel = &uth->uu_select;
+ sel->data = &uth->uu_kevent.ss_select_data;
retval = (int *)get_bsduthreadrval(th_act);
*retval = 0;
}
clock_absolutetime_interval_to_deadline(
- tvtoabstime(&atv), &sel->abstime);
+ tvtoabstime(&atv), &sel->data->abstime);
}
else
- sel->abstime = 0;
+ sel->data->abstime = 0;
if ( (error = selcount(p, sel->ibits, uap->nd, &count)) ) {
goto continuation;
}
- sel->count = count;
+ sel->data->count = count;
size = SIZEOF_WAITQUEUE_SET + (count * SIZEOF_WAITQUEUE_LINK);
if (uth->uu_allocsize) {
if (uth->uu_wqset == 0)
panic("failed to allocate memory for waitqueue\n");
}
bzero(uth->uu_wqset, size);
- sel->wql = (char *)uth->uu_wqset + SIZEOF_WAITQUEUE_SET;
+ sel->data->wql = (char *)uth->uu_wqset + SIZEOF_WAITQUEUE_SET;
wait_queue_set_init(uth->uu_wqset, (SYNC_POLICY_FIFO | SYNC_POLICY_PREPOST));
continuation:
if ((error != 0) && (sel_pass == SEL_FIRSTPASS))
unwind = 0;
- if (sel->count == 0)
+ if (sel->data->count == 0)
unwind = 0;
retry:
if (error != 0) {
ncoll = nselcoll;
OSBitOrAtomic(P_SELECT, &p->p_flag);
/* skip scans if the select is just for timeouts */
- if (sel->count) {
+ if (sel->data->count) {
/*
* Clear out any dangling refs from prior calls; technically
* there should not be any.
uint64_t now;
clock_get_uptime(&now);
- if (now >= sel->abstime)
+ if (now >= sel->data->abstime)
goto done;
}
* To effect a poll, the timeout argument should be
* non-nil, pointing to a zero-valued timeval structure.
*/
- if (uap->tv && sel->abstime == 0) {
+ if (uap->tv && sel->data->abstime == 0) {
goto done;
}
OSBitAndAtomic(~((uint32_t)P_SELECT), &p->p_flag);
/* if the select is just for timeout skip check */
- if (sel->count &&(sel_pass == SEL_SECONDPASS))
+ if (sel->data->count &&(sel_pass == SEL_SECONDPASS))
panic("selprocess: 2nd pass assertwaiting");
/* Wait Queue Subordinate has waitqueue as first element */
- wait_result = wait_queue_assert_wait((wait_queue_t)uth->uu_wqset,
- NULL, THREAD_ABORTSAFE, sel->abstime);
+ wait_result = wait_queue_assert_wait_with_leeway((wait_queue_t)uth->uu_wqset,
+ NULL, THREAD_ABORTSAFE,
+ TIMEOUT_URGENCY_USER_NORMAL, sel->data->abstime, 0);
if (wait_result != THREAD_AWAKENED) {
/* there are no preposted events */
error = tsleep1(NULL, PSOCK | PCATCH,
}
ibits = sel->ibits;
obits = sel->obits;
- wql = sel->wql;
+ wql = sel->data->wql;
nw = howmany(nfd, NFDBITS);
- count = sel->count;
+ count = sel->data->count;
nc = 0;
if (count) {
context.vc_ucred = fp->f_cred;
/* The select; set the bit, if true */
- if (fp->f_ops
+ if (fp->f_ops && fp->f_type
&& fo_select(fp, flag[msk], wql_ptr, &context)) {
optr[fd/NFDBITS] |= (1 << (fd % NFDBITS));
n++;
{
struct poll_continue_args *cont = (struct poll_continue_args *)data;
struct pollfd *fds = CAST_DOWN(struct pollfd *, kevp->udata);
+ short prev_revents = fds->revents;
short mask;
/* convert the results back into revents */
break;
}
- if (fds->revents)
+ if (fds->revents != 0 && prev_revents == 0)
cont->pca_rfds++;
return 0;
evq->ee_req.er_rcnt = pipep->pipe_buffer.cnt;
}
if ((evq->ee_eventmask & EV_WR) &&
- (pipep->pipe_buffer.size - pipep->pipe_buffer.cnt) >= PIPE_BUF) {
+ (MAX(pipep->pipe_buffer.size,PIPE_SIZE) - pipep->pipe_buffer.cnt) >= PIPE_BUF) {
if (pipep->pipe_state & PIPE_EOF) {
mask |= EV_WR|EV_RESET;
break;
}
mask |= EV_WR;
- evq->ee_req.er_wcnt = pipep->pipe_buffer.size - pipep->pipe_buffer.cnt;
+ evq->ee_req.er_wcnt = MAX(pipep->pipe_buffer.size, PIPE_SIZE) - pipep->pipe_buffer.cnt;
}
break;
*/
case EV_RWBYTES:
if ((evq->ee_eventmask & EV_RE) && soreadable(sp)) {
- if (sp->so_error) {
- if ((sp->so_type == SOCK_STREAM) && ((sp->so_error == ECONNREFUSED) || (sp->so_error == ECONNRESET))) {
- if ((sp->so_pcb == 0) || (((struct inpcb *)sp->so_pcb)->inp_state == INPCB_STATE_DEAD) || !(tp = sototcpcb(sp)) ||
- (tp->t_state == TCPS_CLOSED)) {
- mask |= EV_RE|EV_RESET;
- break;
- }
+ /* for AFP/OT purposes; may go away in future */
+ if ((SOCK_DOM(sp) == PF_INET ||
+ SOCK_DOM(sp) == PF_INET6) &&
+ SOCK_PROTO(sp) == IPPROTO_TCP &&
+ (sp->so_error == ECONNREFUSED ||
+ sp->so_error == ECONNRESET)) {
+ if (sp->so_pcb == NULL ||
+ sotoinpcb(sp)->inp_state ==
+ INPCB_STATE_DEAD ||
+ (tp = sototcpcb(sp)) == NULL ||
+ tp->t_state == TCPS_CLOSED) {
+ mask |= EV_RE|EV_RESET;
+ break;
}
}
mask |= EV_RE;
}
}
if ((evq->ee_eventmask & EV_WR) && sowriteable(sp)) {
- if (sp->so_error) {
- if ((sp->so_type == SOCK_STREAM) && ((sp->so_error == ECONNREFUSED) || (sp->so_error == ECONNRESET))) {
- if ((sp->so_pcb == 0) || (((struct inpcb *)sp->so_pcb)->inp_state == INPCB_STATE_DEAD) || !(tp = sototcpcb(sp)) ||
- (tp->t_state == TCPS_CLOSED)) {
- mask |= EV_WR|EV_RESET;
- break;
- }
+ /* for AFP/OT purposes; may go away in future */
+ if ((SOCK_DOM(sp) == PF_INET ||
+ SOCK_DOM(sp) == PF_INET6) &&
+ SOCK_PROTO(sp) == IPPROTO_TCP &&
+ (sp->so_error == ECONNREFUSED ||
+ sp->so_error == ECONNRESET)) {
+ if (sp->so_pcb == NULL ||
+ sotoinpcb(sp)->inp_state ==
+ INPCB_STATE_DEAD ||
+ (tp = sototcpcb(sp)) == NULL ||
+ tp->t_state == TCPS_CLOSED) {
+ mask |= EV_WR|EV_RESET;
+ break;
}
}
mask |= EV_WR;
*
* Parameters: uuid_buf Pointer to buffer to receive UUID
* timeout Timespec for timout
+ * spi SPI, skip sandbox check (temporary)
*
* Returns: 0 Success
* EWOULDBLOCK Timeout is too short
mach_timespec_t mach_ts; /* for IOKit call */
__darwin_uuid_t uuid_kern; /* for IOKit call */
+ if (!uap->spi) {
+#if 13841988
+ uint32_t flags;
+ if (temp_debug_13841988 && (0 == proc_get_darwinbgstate(p->task, &flags)) && (flags & PROC_FLAG_IOS_APPLICATION)) {
+ printf("Unauthorized access to gethostuuid() by %s(%d)\n", p->p_comm, proc_pid(p));
+ return (EPERM);
+ }
+#else
+ /* Perform sandbox check */
+#endif
+ }
+
/* Convert the 32/64 bit timespec into a mach_timespec_t */
if ( proc_is64bit(p) ) {
struct user64_timespec ts;
return (error);
}
+
+/*
+ * ledger
+ *
+ * Description: Omnibus system call for ledger operations
+ */
+int
+ledger(struct proc *p, struct ledger_args *args, __unused int32_t *retval)
+{
+#if !CONFIG_MACF
+#pragma unused(p)
+#endif
+ int rval, pid, len, error;
+#ifdef LEDGER_DEBUG
+ struct ledger_limit_args lla;
+#endif
+ task_t task;
+ proc_t proc;
+
+ /* Finish copying in the necessary args before taking the proc lock */
+ error = 0;
+ len = 0;
+ if (args->cmd == LEDGER_ENTRY_INFO)
+ 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)
+ error = copyin(args->arg2, (char *)&lla, sizeof (lla));
+#endif
+ if (error)
+ return (error);
+ if (len < 0)
+ return (EINVAL);
+
+ rval = 0;
+ if (args->cmd != LEDGER_TEMPLATE_INFO) {
+ pid = args->arg1;
+ proc = proc_find(pid);
+ if (proc == NULL)
+ return (ESRCH);
+
+#if CONFIG_MACF
+ error = mac_proc_check_ledger(p, proc, args->cmd);
+ if (error) {
+ proc_rele(proc);
+ return (error);
+ }
+#endif
+
+ task = proc->task;
+ }
+
+ switch (args->cmd) {
+#ifdef LEDGER_DEBUG
+ case LEDGER_LIMIT: {
+ if (!kauth_cred_issuser(kauth_cred_get()))
+ rval = EPERM;
+ rval = ledger_limit(task, &lla);
+ proc_rele(proc);
+ break;
+ }
+#endif
+ case LEDGER_INFO: {
+ struct ledger_info info;
+
+ rval = ledger_info(task, &info);
+ proc_rele(proc);
+ if (rval == 0)
+ rval = copyout(&info, args->arg2,
+ sizeof (info));
+ break;
+ }
+
+ case LEDGER_ENTRY_INFO: {
+ void *buf;
+ int sz;
+
+ rval = ledger_get_task_entry_info_multiple(task, &buf, &len);
+ proc_rele(proc);
+ if ((rval == 0) && (len > 0)) {
+ sz = len * sizeof (struct ledger_entry_info);
+ rval = copyout(buf, args->arg2, sz);
+ kfree(buf, sz);
+ }
+ if (rval == 0)
+ rval = copyout(&len, args->arg3, sizeof (len));
+ break;
+ }
+
+ case LEDGER_TEMPLATE_INFO: {
+ void *buf;
+ int sz;
+
+ rval = ledger_template_info(&buf, &len);
+ if ((rval == 0) && (len > 0)) {
+ sz = len * sizeof (struct ledger_template_info);
+ rval = copyout(buf, args->arg1, sz);
+ kfree(buf, sz);
+ }
+ if (rval == 0)
+ rval = copyout(&len, args->arg2, sizeof (len));
+ break;
+ }
+
+ default:
+ rval = EINVAL;
+ }
+
+ return (rval);
+}
+
+#if CONFIG_TELEMETRY
+int
+telemetry(__unused struct proc *p, struct telemetry_args *args, __unused int32_t *retval)
+{
+ int error = 0;
+
+ switch (args->cmd) {
+ case TELEMETRY_CMD_TIMER_EVENT:
+ error = telemetry_timer_event(args->deadline, args->interval, args->leeway);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return (error);
+}
+#endif /* CONFIG_TELEMETRY */