/*
- * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2015 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <sys/kauth.h>
#include <sys/file_internal.h>
#include <sys/guarded.h>
+#include <sys/priv.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/stat.h>
#include <sys/resourcevar.h>
#include <sys/aio_kern.h>
#include <sys/ev.h>
-#include <kern/lock.h>
+#include <kern/locks.h>
#include <sys/uio_internal.h>
#include <sys/codesign.h>
+#include <sys/codedir_internal.h>
#include <security/audit/audit.h>
#include <sys/spawn.h>
#include <kern/kern_types.h>
#include <kern/kalloc.h>
+#include <kern/waitq.h>
#include <libkern/OSAtomic.h>
#include <sys/ubc_internal.h>
#include <vm/vm_protos.h>
#include <mach/mach_port.h>
+#include <stdbool.h>
-#if CONFIG_PROTECT
-#include <sys/cprotect.h>
-#endif
#include <hfs/hfs.h>
kern_return_t ipc_object_copyin(ipc_space_t, mach_port_name_t,
/* We don't want these exported */
__private_extern__
-int unlink1(vfs_context_t, struct nameidata *, int);
+int unlink1(vfs_context_t, vnode_t, user_addr_t, enum uio_seg, int);
static void _fdrelse(struct proc * p, int fd);
extern void file_lock_init(void);
-extern int kqueue_stat(struct fileproc *fp, void *ub, int isstat4, proc_t p);
extern kauth_scope_t kauth_scope_fileop;
/* Conflict wait queue for when selects collide (opaque type) */
-extern struct wait_queue select_conflict_queue;
+extern struct waitq select_conflict_queue;
#define f_flag f_fglob->fg_flag
#define f_type f_fglob->fg_ops->fo_type
uio_t auio = NULL;
char uio_buf[ UIO_SIZEOF(1) ];
struct vfs_context context = *(vfs_context_current());
+ bool wrote_some = false;
p = current_proc();
if ( !(io_flg & IO_APPEND))
flags = FOF_OFFSET;
- if (rw == UIO_WRITE)
+ if (rw == UIO_WRITE) {
+ user_ssize_t orig_resid = uio_resid(auio);
error = fo_write(fp, auio, flags, &context);
- else
+ wrote_some = uio_resid(auio) < orig_resid;
+ } else
error = fo_read(fp, auio, flags, &context);
if (aresid)
error = EIO;
}
out:
- if (rw == UIO_WRITE && error == 0)
+ if (wrote_some)
fp_drop_written(p, fd, fp);
else
fp_drop(p, fd, fp, 0);
fp_drop(p, old, fp, 1);
proc_fdunlock(p);
+ if (ENTR_SHOULDTRACE && fp->f_type == DTYPE_SOCKET) {
+ KERNEL_ENERGYTRACE(kEnTrActKernSocket, DBG_FUNC_START,
+ new, 0, (int64_t)VM_KERNEL_ADDRPERM(fp->f_data));
+ }
+
return (error);
}
struct fileproc *fp;
char *pop;
struct vnode *vp = NULLVP; /* for AUDIT_ARG() at end */
- int i, tmp, error, error2, flg = F_POSIX;
+ int i, tmp, error, error2, flg = 0;
struct flock fl;
struct flocktimeout fltimeout;
struct timespec *timeout = NULL;
}
goto out;
+ case F_SETCONFINED:
+ /*
+ * If this is the only reference to this fglob in the process
+ * and it's already marked as close-on-fork then mark it as
+ * (immutably) "confined" i.e. any fd that points to it will
+ * forever be close-on-fork, and attempts to use an IPC
+ * mechanism to move the descriptor elsewhere will fail.
+ */
+ if (CAST_DOWN_EXPLICIT(int, uap->arg)) {
+ struct fileglob *fg = fp->f_fglob;
+
+ lck_mtx_lock_spin(&fg->fg_lock);
+ if (fg->fg_lflags & FG_CONFINED)
+ error = 0;
+ else if (1 != fg->fg_count)
+ error = EAGAIN; /* go close the dup .. */
+ else if (UF_FORKCLOSE == (*pop & UF_FORKCLOSE)) {
+ fg->fg_lflags |= FG_CONFINED;
+ error = 0;
+ } else
+ error = EBADF; /* open without O_CLOFORK? */
+ lck_mtx_unlock(&fg->fg_lock);
+ } else {
+ /*
+ * Other subsystems may have built on the immutability
+ * of FG_CONFINED; clearing it may be tricky.
+ */
+ error = EPERM; /* immutable */
+ }
+ goto out;
+
+ case F_GETCONFINED:
+ *retval = (fp->f_fglob->fg_lflags & FG_CONFINED) ? 1 : 0;
+ error = 0;
+ goto out;
+
case F_SETLKWTIMEOUT:
case F_SETLKW:
+ case F_OFD_SETLKWTIMEOUT:
+ case F_OFD_SETLKW:
flg |= F_WAIT;
/* Fall into F_SETLK */
case F_SETLK:
+ case F_OFD_SETLK:
if (fp->f_type != DTYPE_VNODE) {
error = EBADF;
goto out;
proc_fdunlock(p);
/* Copy in the lock structure */
- if (uap->cmd == F_SETLKWTIMEOUT) {
+ if (F_SETLKWTIMEOUT == uap->cmd ||
+ F_OFD_SETLKWTIMEOUT == uap->cmd) {
error = copyin(argp, (caddr_t) &fltimeout, sizeof(fltimeout));
if (error) {
goto outdrop;
goto outdrop;
}
#endif
- switch (fl.l_type) {
-
- case F_RDLCK:
- if ((fflag & FREAD) == 0) {
- (void)vnode_put(vp);
- error = EBADF;
- goto outdrop;
+ switch (uap->cmd) {
+ case F_OFD_SETLK:
+ case F_OFD_SETLKW:
+ case F_OFD_SETLKWTIMEOUT:
+ flg |= F_OFD_LOCK;
+ switch (fl.l_type) {
+ case F_RDLCK:
+ if ((fflag & FREAD) == 0) {
+ error = EBADF;
+ break;
+ }
+ error = VNOP_ADVLOCK(vp, (caddr_t)fp->f_fglob,
+ F_SETLK, &fl, flg, &context, timeout);
+ break;
+ case F_WRLCK:
+ if ((fflag & FWRITE) == 0) {
+ error = EBADF;
+ break;
+ }
+ error = VNOP_ADVLOCK(vp, (caddr_t)fp->f_fglob,
+ F_SETLK, &fl, flg, &context, timeout);
+ break;
+ case F_UNLCK:
+ error = VNOP_ADVLOCK(vp, (caddr_t)fp->f_fglob,
+ F_UNLCK, &fl, F_OFD_LOCK, &context,
+ timeout);
+ break;
+ default:
+ error = EINVAL;
+ break;
}
- // XXX UInt32 unsafe for LP64 kernel
- OSBitOrAtomic(P_LADVLOCK, &p->p_ladvflag);
- error = VNOP_ADVLOCK(vp, (caddr_t)p, F_SETLK, &fl, flg, &context, timeout);
- (void)vnode_put(vp);
- goto outdrop;
+ if (0 == error &&
+ (F_RDLCK == fl.l_type || F_WRLCK == fl.l_type)) {
+ struct fileglob *fg = fp->f_fglob;
- case F_WRLCK:
- if ((fflag & FWRITE) == 0) {
- (void)vnode_put(vp);
- error = EBADF;
- goto outdrop;
+ /*
+ * arrange F_UNLCK on last close (once
+ * set, FG_HAS_OFDLOCK is immutable)
+ */
+ if ((fg->fg_lflags & FG_HAS_OFDLOCK) == 0) {
+ lck_mtx_lock_spin(&fg->fg_lock);
+ fg->fg_lflags |= FG_HAS_OFDLOCK;
+ lck_mtx_unlock(&fg->fg_lock);
+ }
}
- // XXX UInt32 unsafe for LP64 kernel
- OSBitOrAtomic(P_LADVLOCK, &p->p_ladvflag);
- error = VNOP_ADVLOCK(vp, (caddr_t)p, F_SETLK, &fl, flg, &context, timeout);
- (void)vnode_put(vp);
- goto outdrop;
-
- case F_UNLCK:
- error = VNOP_ADVLOCK(vp, (caddr_t)p, F_UNLCK, &fl,
- F_POSIX, &context, timeout);
- (void)vnode_put(vp);
- goto outdrop;
-
+ break;
default:
- (void)vnode_put(vp);
- error = EINVAL;
- goto outdrop;
+ flg |= F_POSIX;
+ switch (fl.l_type) {
+ case F_RDLCK:
+ if ((fflag & FREAD) == 0) {
+ error = EBADF;
+ break;
+ }
+ // XXX UInt32 unsafe for LP64 kernel
+ OSBitOrAtomic(P_LADVLOCK, &p->p_ladvflag);
+ error = VNOP_ADVLOCK(vp, (caddr_t)p,
+ F_SETLK, &fl, flg, &context, timeout);
+ break;
+ case F_WRLCK:
+ if ((fflag & FWRITE) == 0) {
+ error = EBADF;
+ break;
+ }
+ // XXX UInt32 unsafe for LP64 kernel
+ OSBitOrAtomic(P_LADVLOCK, &p->p_ladvflag);
+ error = VNOP_ADVLOCK(vp, (caddr_t)p,
+ F_SETLK, &fl, flg, &context, timeout);
+ break;
+ case F_UNLCK:
+ error = VNOP_ADVLOCK(vp, (caddr_t)p,
+ F_UNLCK, &fl, F_POSIX, &context, timeout);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
}
+ (void) vnode_put(vp);
+ goto outdrop;
case F_GETLK:
+ case F_OFD_GETLK:
if (fp->f_type != DTYPE_VNODE) {
error = EBADF;
goto out;
uap->cmd, &fl);
if (error == 0)
#endif
- error = VNOP_ADVLOCK(vp, (caddr_t)p, uap->cmd, &fl, F_POSIX, &context, NULL);
+ switch (uap->cmd) {
+ case F_OFD_GETLK:
+ error = VNOP_ADVLOCK(vp, (caddr_t)fp->f_fglob,
+ F_GETLK, &fl, F_OFD_LOCK, &context, NULL);
+ break;
+ case F_OFD_GETLKPID:
+ error = VNOP_ADVLOCK(vp, (caddr_t)fp->f_fglob,
+ F_GETLKPID, &fl, F_OFD_LOCK, &context, NULL);
+ break;
+ default:
+ error = VNOP_ADVLOCK(vp, (caddr_t)p,
+ uap->cmd, &fl, F_POSIX, &context, NULL);
+ break;
+ }
(void)vnode_put(vp);
}
devBlockSize = vfs_devblocksize(vnode_mount(vp));
if (uap->cmd == F_LOG2PHYS_EXT) {
-#if defined(__LP64__)
- a_size = l2p_struct.l2p_contigbytes;
-#else
- if ((l2p_struct.l2p_contigbytes > SIZE_MAX) || (l2p_struct.l2p_contigbytes < 0)) {
- /* size_t is 32-bit on a 32-bit kernel, therefore
- * assigning l2p_contigbytes to a_size may have
- * caused integer overflow. We, therefore, return
- * an error here instead of calculating incorrect
- * value.
- */
- printf ("fcntl: F_LOG2PHYS_EXT: l2p_contigbytes=%lld will overflow, returning error\n", l2p_struct.l2p_contigbytes);
- error = EFBIG;
+ if (l2p_struct.l2p_contigbytes < 0) {
+ vnode_put(vp);
+ error = EINVAL;
goto outdrop;
- } else {
- a_size = l2p_struct.l2p_contigbytes;
}
-#endif
+
+ a_size = MIN((uint64_t)l2p_struct.l2p_contigbytes, SIZE_MAX);
} else {
a_size = devBlockSize;
}
}
case F_CHKCLEAN: // used by regression tests to see if all dirty pages got cleaned by fsync()
- case F_FULLFSYNC: // fsync + flush the journal + DKIOCSYNCHRONIZECACHE
+ case F_FULLFSYNC: // fsync + flush the journal + DKIOCSYNCHRONIZE
+ case F_BARRIERFSYNC: // fsync + barrier
case F_FREEZE_FS: // freeze all other fs operations for the fs of this fd
case F_THAW_FS: { // thaw all frozen fs operations for the fs of this fd
if (fp->f_type != DTYPE_VNODE) {
* SPI (private) for unlinking a file starting from a dir fd
*/
case F_UNLINKFROM: {
- struct nameidata nd;
user_addr_t pathname;
/* Check if this isn't a valid file descriptor */
}
/* Start the lookup relative to the file descriptor's vnode. */
- NDINIT(&nd, DELETE, OP_UNLINK, USEDVP | AUDITVNPATH1, UIO_USERSPACE,
- pathname, &context);
- nd.ni_dvp = vp;
-
- error = unlink1(&context, &nd, 0);
+ error = unlink1(&context, vp, pathname, UIO_USERSPACE, 0);
vnode_put(vp);
break;
case F_ADDSIGS:
case F_ADDFILESIGS:
+ case F_ADDFILESIGS_FOR_DYLD_SIM:
+ case F_ADDFILESIGS_RETURN:
{
+ struct cs_blob *blob = NULL;
struct user_fsignatures fs;
kern_return_t kr;
- off_t kernel_blob_offset;
vm_offset_t kernel_blob_addr;
vm_size_t kernel_blob_size;
+ int blob_add_flags = 0;
if (fp->f_type != DTYPE_VNODE) {
error = EBADF;
}
vp = (struct vnode *)fp->f_data;
proc_fdunlock(p);
+
+ if (uap->cmd == F_ADDFILESIGS_FOR_DYLD_SIM) {
+ blob_add_flags |= MAC_VNODE_CHECK_DYLD_SIM;
+ if ((p->p_csflags & CS_KILL) == 0) {
+ proc_lock(p);
+ p->p_csflags |= CS_KILL;
+ proc_unlock(p);
+ }
+ }
+
error = vnode_getwithref(vp);
if (error)
goto outdrop;
goto outdrop;
}
- if(ubc_cs_blob_get(vp, CPU_TYPE_ANY, fs.fs_file_start))
+ /*
+ * First check if we have something loaded a this offset
+ */
+ blob = ubc_cs_blob_get(vp, CPU_TYPE_ANY, fs.fs_file_start);
+ if (blob != NULL)
{
- vnode_put(vp);
- goto outdrop;
- }
-#if defined(__LP64__)
-#define CS_MAX_BLOB_SIZE (2560ULL * 1024ULL) /* max shared cache file XXX ? */
-#else
-#define CS_MAX_BLOB_SIZE (1600ULL * 1024ULL) /* max shared cache file XXX ? */
-#endif
- if (fs.fs_blob_size > CS_MAX_BLOB_SIZE) {
- error = E2BIG;
- vnode_put(vp);
- goto outdrop;
- }
+ /* If this is for dyld_sim revalidate the blob */
+ if (uap->cmd == F_ADDFILESIGS_FOR_DYLD_SIM) {
+ error = ubc_cs_blob_revalidate(vp, blob, blob_add_flags);
+ }
- kernel_blob_size = CAST_DOWN(vm_size_t, fs.fs_blob_size);
- kr = ubc_cs_blob_allocate(&kernel_blob_addr, &kernel_blob_size);
- if (kr != KERN_SUCCESS) {
- error = ENOMEM;
- vnode_put(vp);
- goto outdrop;
- }
+ } else {
+ /*
+ * An arbitrary limit, to prevent someone from mapping in a 20GB blob. This should cover
+ * our use cases for the immediate future, but note that at the time of this commit, some
+ * platforms are nearing 2MB blob sizes (with a prior soft limit of 2.5MB).
+ *
+ * We should consider how we can manage this more effectively; the above means that some
+ * platforms are using megabytes of memory for signing data; it merely hasn't crossed the
+ * threshold considered ridiculous at the time of this change.
+ */
+#define CS_MAX_BLOB_SIZE (40ULL * 1024ULL * 1024ULL)
+ if (fs.fs_blob_size > CS_MAX_BLOB_SIZE) {
+ error = E2BIG;
+ vnode_put(vp);
+ goto outdrop;
+ }
- if(uap->cmd == F_ADDSIGS) {
- kernel_blob_offset = 0;
- error = copyin(fs.fs_blob_start,
- (void *) kernel_blob_addr,
- kernel_blob_size);
- } else /* F_ADDFILESIGS */ {
- kernel_blob_offset = fs.fs_blob_start;
- error = vn_rdwr(UIO_READ,
- vp,
- (caddr_t) kernel_blob_addr,
- kernel_blob_size,
- fs.fs_file_start + fs.fs_blob_start,
- UIO_SYSSPACE,
- 0,
- kauth_cred_get(),
- 0,
- p);
- }
-
- if (error) {
- ubc_cs_blob_deallocate(kernel_blob_addr,
- kernel_blob_size);
- vnode_put(vp);
- goto outdrop;
- }
+ kernel_blob_size = CAST_DOWN(vm_size_t, fs.fs_blob_size);
+ kr = ubc_cs_blob_allocate(&kernel_blob_addr, &kernel_blob_size);
+ if (kr != KERN_SUCCESS) {
+ error = ENOMEM;
+ vnode_put(vp);
+ goto outdrop;
+ }
- error = ubc_cs_blob_add(
- vp,
- CPU_TYPE_ANY, /* not for a specific architecture */
- fs.fs_file_start,
- kernel_blob_addr,
- kernel_blob_offset,
- kernel_blob_size);
- if (error) {
- ubc_cs_blob_deallocate(kernel_blob_addr,
+ if(uap->cmd == F_ADDSIGS) {
+ error = copyin(fs.fs_blob_start,
+ (void *) kernel_blob_addr,
kernel_blob_size);
- } else {
- /* ubc_blob_add() has consumed "kernel_blob_addr" */
+ } else /* F_ADDFILESIGS || F_ADDFILESIGS_RETURN || F_ADDFILESIGS_FOR_DYLD_SIM */ {
+ int resid;
+
+ error = vn_rdwr(UIO_READ,
+ vp,
+ (caddr_t) kernel_blob_addr,
+ kernel_blob_size,
+ fs.fs_file_start + fs.fs_blob_start,
+ UIO_SYSSPACE,
+ 0,
+ kauth_cred_get(),
+ &resid,
+ p);
+ if ((error == 0) && resid) {
+ /* kernel_blob_size rounded to a page size, but signature may be at end of file */
+ memset((void *)(kernel_blob_addr + (kernel_blob_size - resid)), 0x0, resid);
+ }
+ }
+
+ if (error) {
+ ubc_cs_blob_deallocate(kernel_blob_addr,
+ kernel_blob_size);
+ vnode_put(vp);
+ goto outdrop;
+ }
+
+ blob = NULL;
+ error = ubc_cs_blob_add(vp,
+ CPU_TYPE_ANY, /* not for a specific architecture */
+ fs.fs_file_start,
+ kernel_blob_addr,
+ kernel_blob_size,
+ blob_add_flags,
+ &blob);
+ if (error) {
+ ubc_cs_blob_deallocate(kernel_blob_addr,
+ kernel_blob_size);
+ } else {
+ /* ubc_blob_add() has consumed "kernel_blob_addr" */
#if CHECK_CS_VALIDATION_BITMAP
- ubc_cs_validation_bitmap_allocate( vp );
+ ubc_cs_validation_bitmap_allocate( vp );
#endif
+ }
+ }
+
+ if (uap->cmd == F_ADDFILESIGS_RETURN || uap->cmd == F_ADDFILESIGS_FOR_DYLD_SIM) {
+ /*
+ * The first element of the structure is a
+ * off_t that happen to have the same size for
+ * all archs. Lets overwrite that.
+ */
+ off_t end_offset = 0;
+ if (blob)
+ end_offset = blob->csb_end_offset;
+ error = copyout(&end_offset, argp, sizeof (end_offset));
}
(void) vnode_put(vp);
break;
}
case F_FINDSIGS: {
-#ifdef SECURE_KERNEL
error = ENOTSUP;
-#else /* !SECURE_KERNEL */
- off_t offsetMacho;
-
- if (fp->f_type != DTYPE_VNODE) {
- error = EBADF;
- goto out;
- }
- vp = (struct vnode *)fp->f_data;
- proc_fdunlock(p);
- error = vnode_getwithref(vp);
- if (error)
- goto outdrop;
-
- error = copyin(argp, &offsetMacho, sizeof(offsetMacho));
- if (error) {
- (void)vnode_put(vp);
- goto outdrop;
- }
-
-#if CONFIG_MACF
- error = mac_vnode_find_sigs(p, vp, offsetMacho);
-#else
- error = EPERM;
-#endif
- if (error) {
- (void)vnode_put(vp);
- goto outdrop;
- }
-#endif /* SECURE_KERNEL */
- break;
+ goto out;
}
#if CONFIG_PROTECT
case F_GETPROTECTIONCLASS: {
}
case F_TRANSCODEKEY: {
+
+ char *backup_keyp = NULL;
+ unsigned backup_key_len = CP_MAX_WRAPPEDKEYSIZE;
if (fp->f_type != DTYPE_VNODE) {
error = EBADF;
error = ENOENT;
goto outdrop;
}
-
- error = cp_vnode_transcode (vp);
+
+ MALLOC(backup_keyp, char *, backup_key_len, M_TEMP, M_WAITOK);
+ if (backup_keyp == NULL) {
+ error = ENOMEM;
+ goto outdrop;
+ }
+
+ error = cp_vnode_transcode (vp, backup_keyp, &backup_key_len);
vnode_put(vp);
+
+ if (error == 0) {
+ error = copyout((caddr_t)backup_keyp, argp, backup_key_len);
+ *retval = backup_key_len;
+ }
+
+ FREE(backup_keyp, M_TEMP);
+
break;
}
struct vnode *dst_vp = NULLVP;
/* We need to grab the 2nd FD out of the argments before moving on. */
int fd2 = CAST_DOWN_EXPLICIT(int32_t, uap->arg);
-
+
+ error = priv_check_cred(kauth_cred_get(), PRIV_VFS_MOVE_DATA_EXTENTS, 0);
+ if (error)
+ goto out;
+
if (fp->f_type != DTYPE_VNODE) {
error = EBADF;
goto out;
error = VNOP_IOCTL(vp, uap->cmd, ioctl_arg, 0, &context);
(void)vnode_put(vp);
+
+ break;
+ }
+
+ /*
+ * SPI (private) for indicating to the lower level storage driver that the
+ * subsequent writes should be of a particular IO type (burst, greedy, static),
+ * or other flavors that may be necessary.
+ */
+ case F_SETIOTYPE: {
+ caddr_t param_ptr;
+ uint32_t param;
+
+ if (uap->arg) {
+ /* extract 32 bits of flags from userland */
+ param_ptr = (caddr_t) uap->arg;
+ param = (uint32_t) param_ptr;
+ }
+ else {
+ /* If no argument is specified, error out */
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * Validate the different types of flags that can be specified:
+ * all of them are mutually exclusive for now.
+ */
+ switch (param) {
+ case F_IOTYPE_ISOCHRONOUS:
+ break;
+
+ default:
+ error = EINVAL;
+ goto out;
+ }
+
+
+ if (fp->f_type != DTYPE_VNODE) {
+ error = EBADF;
+ goto out;
+ }
+ vp = (struct vnode *)fp->f_data;
+ proc_fdunlock(p);
+
+ error = vnode_getwithref(vp);
+ if (error) {
+ error = ENOENT;
+ goto outdrop;
+ }
+
+ /* Only go forward if you have write access */
+ vfs_context_t ctx = vfs_context_current();
+ if(vnode_authorize(vp, NULLVP, (KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA), ctx) != 0) {
+ vnode_put(vp);
+ error = EBADF;
+ goto outdrop;
+ }
+
+ error = VNOP_IOCTL(vp, uap->cmd, param_ptr, 0, &context);
+ (void)vnode_put(vp);
+
+ break;
+ }
+
+
+ /*
+ * Extract the CodeDirectory of the vnode associated with
+ * the file descriptor and copy it back to user space
+ */
+ case F_GETCODEDIR: {
+ struct user_fcodeblobs args;
+
+ if (fp->f_type != DTYPE_VNODE) {
+ error = EBADF;
+ goto out;
+ }
+
+ vp = (struct vnode *)fp->f_data;
+ proc_fdunlock(p);
+
+ if ((fp->f_flag & FREAD) == 0) {
+ error = EBADF;
+ goto outdrop;
+ }
+
+ if (IS_64BIT_PROCESS(p)) {
+ struct user64_fcodeblobs args64;
+
+ error = copyin(argp, &args64, sizeof(args64));
+ if (error)
+ goto outdrop;
+
+ args.f_cd_hash = args64.f_cd_hash;
+ args.f_hash_size = args64.f_hash_size;
+ args.f_cd_buffer = args64.f_cd_buffer;
+ args.f_cd_size = args64.f_cd_size;
+ args.f_out_size = args64.f_out_size;
+ args.f_arch = args64.f_arch;
+ } else {
+ struct user32_fcodeblobs args32;
+
+ error = copyin(argp, &args32, sizeof(args32));
+ if (error)
+ goto outdrop;
+
+ args.f_cd_hash = CAST_USER_ADDR_T(args32.f_cd_hash);
+ args.f_hash_size = args32.f_hash_size;
+ args.f_cd_buffer = CAST_USER_ADDR_T(args32.f_cd_buffer);
+ args.f_cd_size = args32.f_cd_size;
+ args.f_out_size = CAST_USER_ADDR_T(args32.f_out_size);
+ args.f_arch = args32.f_arch;
+ }
+
+ if (vp->v_ubcinfo == NULL) {
+ error = EINVAL;
+ goto outdrop;
+ }
+
+ struct cs_blob *t_blob = vp->v_ubcinfo->cs_blobs;
+
+ /*
+ * This call fails if there is no cs_blob corresponding to the
+ * vnode, or if there are multiple cs_blobs present, and the caller
+ * did not specify which cpu_type they want the cs_blob for
+ */
+ if (t_blob == NULL) {
+ error = ENOENT; /* there is no codesigning blob for this process */
+ goto outdrop;
+ } else if (args.f_arch == 0 && t_blob->csb_next != NULL) {
+ error = ENOENT; /* too many architectures and none specified */
+ goto outdrop;
+ }
+
+ /* If the user specified an architecture, find the right blob */
+ if (args.f_arch != 0) {
+ while (t_blob) {
+ if (t_blob->csb_cpu_type == args.f_arch)
+ break;
+ t_blob = t_blob->csb_next;
+ }
+ /* The cpu_type the user requested could not be found */
+ if (t_blob == NULL) {
+ error = ENOENT;
+ goto outdrop;
+ }
+ }
+
+ const CS_SuperBlob *super_blob = (void *)t_blob->csb_mem_kaddr;
+ const CS_CodeDirectory *cd = findCodeDirectory(super_blob,
+ (const char *) super_blob,
+ (const char *) super_blob + t_blob->csb_mem_size);
+ if (cd == NULL) {
+ error = ENOENT;
+ goto outdrop;
+ }
+
+ uint64_t buffer_size = ntohl(cd->length);
+
+ if (buffer_size > UINT_MAX) {
+ error = ERANGE;
+ goto outdrop;
+ }
+
+ error = copyout(&buffer_size, args.f_out_size, sizeof(unsigned int));
+ if (error)
+ goto outdrop;
+
+ if (sizeof(t_blob->csb_cdhash) > args.f_hash_size ||
+ buffer_size > args.f_cd_size) {
+ error = ERANGE;
+ goto outdrop;
+ }
+
+ error = copyout(t_blob->csb_cdhash, args.f_cd_hash, sizeof(t_blob->csb_cdhash));
+ if (error)
+ goto outdrop;
+ error = copyout(cd, args.f_cd_buffer, buffer_size);
+ if (error)
+ goto outdrop;
+
break;
}
goto outdrop;
}
+#if DEBUG || DEVELOPMENT
+ case F_RECYCLE:
+ if (fp->f_type != DTYPE_VNODE) {
+ error = EBADF;
+ goto out;
+ }
+ vp = (struct vnode *)fp->f_data;
+ proc_fdunlock(p);
+
+ vnode_recycle(vp);
+ break;
+#endif
+
default:
/*
* This is an fcntl() that we d not recognize at this level;
procfdtbl_reservefd(p, fd);
}
+ if (ENTR_SHOULDTRACE && fp->f_type == DTYPE_SOCKET)
+ KERNEL_ENERGYTRACE(kEnTrActKernSocket, DBG_FUNC_END,
+ fd, 0, (int64_t)VM_KERNEL_ADDRPERM(fp->f_data));
+
error = closef_locked(fp, fp->f_fglob, p);
if ((fp->f_flags & FP_WAITCLOSE) == FP_WAITCLOSE)
wakeup(&fp->f_flags);
struct user32_stat64 user32_sb64;
} dest;
int error, my_size;
- int funnel_state;
file_type_t type;
caddr_t data;
kauth_filesec_t fsec;
break;
case DTYPE_KQUEUE:
- funnel_state = thread_funnel_set(kernel_flock, TRUE);
- error = kqueue_stat(fp, sbptr, isstat64, p);
- thread_funnel_set(kernel_flock, funnel_state);
+ error = kqueue_stat((void *)data, sbptr, isstat64, p);
break;
default:
(fp->f_flags & ~FP_TYPEMASK);
nfp->f_iocount = fp->f_iocount;
nfp->f_fglob = fp->f_fglob;
- nfp->f_waddr = fp->f_waddr;
+ nfp->f_wset = fp->f_wset;
p->p_fd->fd_ofiles[fd] = nfp;
(void) fp_drop(p, fd, nfp, 1);
{
OSAddAtomic(-1, &nfiles);
+ if (fg->fg_vn_data) {
+ fg_vn_data_free(fg->fg_vn_data);
+ fg->fg_vn_data = NULL;
+ }
+
if (IS_VALID_CRED(fg->fg_cred)) {
kauth_cred_unref(&fg->fg_cred);
}
*
* Locks: This function internally takes and drops proc_fdlock()
*
- * Notes: This function drops and retakes the kernel funnel; this is
- * inherently unsafe, since another thread may have the
- * proc_fdlock.
- *
- * XXX: We should likely reverse the lock and funnel drop/acquire
- * order to avoid the small race window; it's also possible that
- * if the program doing the exec has an outstanding listen socket
- * and a network connection is completed asynchronously that we
- * will end up with a "ghost" socket reference in the new process.
- *
- * This needs reworking to make it safe to remove the funnel from
- * the execve and posix_spawn system calls.
*/
void
fdexec(proc_t p, short flags)
* allowing the table to shrink.
*/
i = newfdp->fd_nfiles;
- while (i > 2 * NDEXTENT && i > newfdp->fd_lastfile * 2)
+ while (i > 1 + 2 * NDEXTENT && i > 1 + newfdp->fd_lastfile * 2)
i /= 2;
}
proc_fdunlock(p);
for (i = newfdp->fd_lastfile + 1; --i >= 0; fpp++, flags++)
if ((ofp = *fpp) != NULL &&
+ 0 == (ofp->f_fglob->fg_lflags & FG_CONFINED) &&
0 == (*flags & (UF_FORKCLOSE|UF_RESERVED))) {
#if DEBUG
if (FILEPROC_TYPE(ofp) != FTYPE_SIMPLE)
(*fp->f_fglob->fg_ops->fo_drain)(fp, &context);
}
if ((fp->f_flags & FP_INSELECT) == FP_INSELECT) {
- if (wait_queue_wakeup_all((wait_queue_t)fp->f_waddr, NULL, THREAD_INTERRUPTED) == KERN_INVALID_ARGUMENT)
- panic("bad wait queue for wait_queue_wakeup_all %p", fp->f_waddr);
- }
+ if (waitq_wakeup64_all((struct waitq *)fp->f_wset, NO_EVENT64,
+ THREAD_INTERRUPTED, WAITQ_ALL_PRIORITIES) == KERN_INVALID_ARGUMENT)
+ panic("bad wait queue for waitq_wakeup64_all %p (fp:%p)", fp->f_wset, fp);
+ }
if ((fp->f_flags & FP_SELCONFLICT) == FP_SELCONFLICT) {
- if (wait_queue_wakeup_all(&select_conflict_queue, NULL, THREAD_INTERRUPTED) == KERN_INVALID_ARGUMENT)
+ if (waitq_wakeup64_all(&select_conflict_queue, NO_EVENT64,
+ THREAD_INTERRUPTED, WAITQ_ALL_PRIORITIES) == KERN_INVALID_ARGUMENT)
panic("bad select_conflict_queue");
}
p->p_fpdrainwait = 1;
ipc_port_t fileport;
mach_port_name_t name = MACH_PORT_NULL;
- err = fp_lookup(p, fd, &fp, 0);
+ proc_fdlock(p);
+ err = fp_lookup(p, fd, &fp, 1);
if (err != 0) {
- goto out;
+ goto out_unlock;
}
- if (!filetype_issendable(fp->f_type)) {
+ if (!file_issendable(p, fp)) {
err = EINVAL;
- goto out;
+ goto out_unlock;
}
if (FP_ISGUARDED(fp, GUARD_FILEPORT)) {
- proc_fdlock(p);
err = fp_guard_exception(p, fd, fp, kGUARD_EXC_FILEPORT);
- proc_fdunlock(p);
- goto out;
+ goto out_unlock;
}
/* Dropped when port is deallocated */
fg = fp->f_fglob;
fg_ref(fp);
+ proc_fdunlock(p);
+
/* Allocate and initialize a port */
fileport = fileport_alloc(fg);
if (fileport == IPC_PORT_NULL) {
return 0;
+out_unlock:
+ proc_fdunlock(p);
out:
if (MACH_PORT_VALID(name)) {
/* Don't care if another thread races us to deallocate the entry */
switch (error) {
case ENODEV:
if (FP_ISGUARDED(wfp, GUARD_DUP)) {
- int err = fp_guard_exception(p,
- dfd, wfp, kGUARD_EXC_DUP);
proc_fdunlock(p);
- return (err);
+ return (EPERM);
}
/*
#if SOCKETS
/*
- * fg_insertuipc
+ * fg_insertuipc_mark
*
- * Description: Insert fileglob onto message queue
+ * Description: Mark fileglob for insertion onto message queue if needed
+ * Also takes fileglob reference
*
- * Parameters: fg Fileglob pointer to insert
+ * Parameters: fg Fileglob pointer to insert
*
- * Returns: void
+ * Returns: true, if the fileglob needs to be inserted onto msg queue
*
* Locks: Takes and drops fg_lock, potentially many times
*/
-void
-fg_insertuipc(struct fileglob * fg)
+boolean_t
+fg_insertuipc_mark(struct fileglob * fg)
{
- int insertque = 0;
+ boolean_t insert = FALSE;
lck_mtx_lock_spin(&fg->fg_lock);
-
while (fg->fg_lflags & FG_RMMSGQ) {
lck_mtx_convert_spin(&fg->fg_lock);
fg->fg_msgcount++;
if (fg->fg_msgcount == 1) {
fg->fg_lflags |= FG_INSMSGQ;
- insertque=1;
+ insert = TRUE;
}
lck_mtx_unlock(&fg->fg_lock);
+ return (insert);
+}
- if (insertque) {
+/*
+ * fg_insertuipc
+ *
+ * Description: Insert marked fileglob onto message queue
+ *
+ * Parameters: fg Fileglob pointer to insert
+ *
+ * Returns: void
+ *
+ * Locks: Takes and drops fg_lock & uipc_lock
+ * DO NOT call this function with proc_fdlock held as unp_gc()
+ * can potentially try to acquire proc_fdlock, which can result
+ * in a deadlock if this function is in unp_gc_wait().
+ */
+void
+fg_insertuipc(struct fileglob * fg)
+{
+ if (fg->fg_lflags & FG_INSMSGQ) {
lck_mtx_lock_spin(uipc_lock);
unp_gc_wait();
LIST_INSERT_HEAD(&fmsghead, fg, f_msglist);
}
lck_mtx_unlock(&fg->fg_lock);
}
-
}
-
/*
- * fg_removeuipc
+ * fg_removeuipc_mark
*
- * Description: Remove fileglob from message queue
+ * Description: Mark the fileglob for removal from message queue if needed
+ * Also releases fileglob message queue reference
*
- * Parameters: fg Fileglob pointer to remove
+ * Parameters: fg Fileglob pointer to remove
*
- * Returns: void
+ * Returns: true, if the fileglob needs to be removed from msg queue
*
* Locks: Takes and drops fg_lock, potentially many times
*/
-void
-fg_removeuipc(struct fileglob * fg)
+boolean_t
+fg_removeuipc_mark(struct fileglob * fg)
{
- int removeque = 0;
+ boolean_t remove = FALSE;
lck_mtx_lock_spin(&fg->fg_lock);
while (fg->fg_lflags & FG_INSMSGQ) {
fg->fg_msgcount--;
if (fg->fg_msgcount == 0) {
fg->fg_lflags |= FG_RMMSGQ;
- removeque=1;
+ remove = TRUE;
}
lck_mtx_unlock(&fg->fg_lock);
+ return (remove);
+}
- if (removeque) {
+/*
+ * fg_removeuipc
+ *
+ * Description: Remove marked fileglob from message queue
+ *
+ * Parameters: fg Fileglob pointer to remove
+ *
+ * Returns: void
+ *
+ * Locks: Takes and drops fg_lock & uipc_lock
+ * DO NOT call this function with proc_fdlock held as unp_gc()
+ * can potentially try to acquire proc_fdlock, which can result
+ * in a deadlock if this function is in unp_gc_wait().
+ */
+void
+fg_removeuipc(struct fileglob * fg)
+{
+ if (fg->fg_lflags & FG_RMMSGQ) {
lck_mtx_lock_spin(uipc_lock);
unp_gc_wait();
LIST_REMOVE(fg, f_msglist);
* process is opt-in by file type.
*/
boolean_t
-filetype_issendable(file_type_t fdtype)
+file_issendable(proc_t p, struct fileproc *fp)
{
- switch (fdtype) {
- case DTYPE_VNODE:
- case DTYPE_SOCKET:
- case DTYPE_PIPE:
- case DTYPE_PSXSHM:
- return TRUE;
- default:
- /* DTYPE_KQUEUE, DTYPE_FSEVENTS, DTYPE_PSXSEM */
- return FALSE;
+ proc_fdlock_assert(p, LCK_MTX_ASSERT_OWNED);
+
+ switch (fp->f_type) {
+ case DTYPE_VNODE:
+ case DTYPE_SOCKET:
+ case DTYPE_PIPE:
+ case DTYPE_PSXSHM:
+ return (0 == (fp->f_fglob->fg_lflags & FG_CONFINED));
+ default:
+ /* DTYPE_KQUEUE, DTYPE_FSEVENTS, DTYPE_PSXSEM */
+ return FALSE;
}
}