1/*
2 * Copyright (c) 2012 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#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/filedesc.h>
32#include <sys/kernel.h>
33#include <sys/file_internal.h>
34#include <sys/guarded.h>
35#include <kern/kalloc.h>
36#include <sys/sysproto.h>
37#include <sys/vnode.h>
38#include <vfs/vfs_support.h>
39#include <security/audit/audit.h>
40
41/*
42 * Experimental guarded file descriptor support.
43 */
44
45kern_return_t task_exception_notify(exception_type_t exception,
46        mach_exception_data_type_t code, mach_exception_data_type_t subcode);
47
48/*
49 * Most fd's have an underlying fileproc struct; but some may be
50 * guarded_fileproc structs which implement guarded fds.  The latter
51 * struct (below) embeds the former.
52 *
53 * The two types should be distinguished by the "type" portion of f_flags.
54 * There's also a magic number to help catch misuse and bugs.
55 *
56 * This is a bit unpleasant, but results from the desire to allow
57 * alternate file behaviours for a few file descriptors without
58 * growing the fileproc data structure.
59 */
60
61struct guarded_fileproc {
62	struct fileproc gf_fileproc;
63	u_int		gf_magic;
64	u_int		gf_attrs;
65	thread_t	gf_thread;
66	guardid_t	gf_guard;
67	int		gf_exc_fd;
68	u_int		gf_exc_code;
69};
70
71const size_t sizeof_guarded_fileproc = sizeof (struct guarded_fileproc);
72
73#define FP_TO_GFP(fp)	((struct guarded_fileproc *)(fp))
74#define	GFP_TO_FP(gfp)	(&(gfp)->gf_fileproc)
75
76#define GUARDED_FILEPROC_MAGIC	0x29083
77
78struct gfp_crarg {
79	guardid_t gca_guard;
80	u_int gca_attrs;
81};
82
83static struct fileproc *
84guarded_fileproc_alloc_init(void *crarg)
85{
86	struct gfp_crarg *aarg = crarg;
87	struct guarded_fileproc *gfp;
88
89	if ((gfp = kalloc(sizeof (*gfp))) == NULL)
90		return (NULL);
91
92	bzero(gfp, sizeof (*gfp));
93	gfp->gf_fileproc.f_flags = FTYPE_GUARDED;
94	gfp->gf_magic = GUARDED_FILEPROC_MAGIC;
95	gfp->gf_guard = aarg->gca_guard;
96	gfp->gf_attrs = aarg->gca_attrs;
97
98	return (GFP_TO_FP(gfp));
99}
100
101void
102guarded_fileproc_free(struct fileproc *fp)
103{
104	struct guarded_fileproc *gfp = FP_TO_GFP(fp);
105
106	if (FILEPROC_TYPE(fp) != FTYPE_GUARDED ||
107	    GUARDED_FILEPROC_MAGIC != gfp->gf_magic)
108		panic("%s: corrupt fp %p flags %x", __func__, fp, fp->f_flags);
109
110	kfree(gfp, sizeof (*gfp));
111}
112
113static int
114fp_lookup_guarded(proc_t p, int fd, guardid_t guard,
115    struct guarded_fileproc **gfpp)
116{
117	struct fileproc *fp;
118	int error;
119
120	if ((error = fp_lookup(p, fd, &fp, 1)) != 0)
121		return (error);
122	if (FILEPROC_TYPE(fp) != FTYPE_GUARDED) {
123		(void) fp_drop(p, fd, fp, 1);
124		return (EINVAL);
125	}
126	struct guarded_fileproc *gfp = FP_TO_GFP(fp);
127
128	if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic)
129		panic("%s: corrupt fp %p", __func__, fp);
130
131	if (guard != gfp->gf_guard) {
132		(void) fp_drop(p, fd, fp, 1);
133		return (EPERM);	/* *not* a mismatch exception */
134	}
135	if (gfpp)
136		*gfpp = gfp;
137	return (0);
138}
139
140/*
141 * Expected use pattern:
142 *
143 * if (FP_ISGUARDED(fp, GUARD_CLOSE)) {
144 * 	error = fp_guard_exception(p, fd, fp, kGUARD_EXC_CLOSE);
145 *      proc_fdunlock(p);
146 *      return (error);
147 * }
148 */
149
150int
151fp_isguarded(struct fileproc *fp, u_int attrs)
152{
153	if (FILEPROC_TYPE(fp) == FTYPE_GUARDED) {
154		struct guarded_fileproc *gfp = FP_TO_GFP(fp);
155
156		if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic)
157			panic("%s: corrupt gfp %p flags %x",
158			    __func__, gfp, fp->f_flags);
159		return ((attrs & gfp->gf_attrs) ? 1 : 0);
160	}
161	return (0);
162}
163
164extern char *proc_name_address(void *p);
165
166int
167fp_guard_exception(proc_t p, int fd, struct fileproc *fp, u_int code)
168{
169	if (FILEPROC_TYPE(fp) != FTYPE_GUARDED)
170		panic("%s corrupt fp %p flags %x", __func__, fp, fp->f_flags);
171
172	struct guarded_fileproc *gfp = FP_TO_GFP(fp);
173
174	/* all gfd fields protected via proc_fdlock() */
175	proc_fdlock_assert(p, LCK_MTX_ASSERT_OWNED);
176
177	if (NULL == gfp->gf_thread) {
178		thread_t t = current_thread();
179		gfp->gf_thread = t;
180		gfp->gf_exc_fd = fd;
181		gfp->gf_exc_code = code;
182
183		/*
184		 * This thread was the first to attempt the
185		 * operation that violated the guard on this fd;
186		 * generate an exception.
187		 */
188		printf("%s: guarded fd exception: "
189		    "fd %d code 0x%x guard 0x%llx\n",
190		    proc_name_address(p), gfp->gf_exc_fd,
191		    gfp->gf_exc_code, gfp->gf_guard);
192
193		thread_guard_violation(t, GUARD_TYPE_FD);
194	} else {
195		/*
196		 * We already recorded a violation on this fd for a
197		 * different thread, so posting an exception is
198		 * already in progress.  We could pause for a bit
199		 * and check again, or we could panic (though that seems
200		 * heavy handed), or we could just press on with the
201		 * error return alone.  For now, resort to printf.
202		 */
203		printf("%s: guarded fd exception+: "
204		    "fd %d code 0x%x guard 0x%llx\n",
205		    proc_name_address(p), gfp->gf_exc_fd,
206		    gfp->gf_exc_code, gfp->gf_guard);
207	}
208
209	return (EPERM);
210}
211
212/*
213 * (Invoked before returning to userland from the syscall handler.)
214 */
215void
216fd_guard_ast(thread_t t)
217{
218	proc_t p = current_proc();
219	struct filedesc *fdp = p->p_fd;
220	int i;
221
222	proc_fdlock(p);
223	for (i = fdp->fd_lastfile; i >= 0; i--) {
224		struct fileproc *fp = fdp->fd_ofiles[i];
225
226		if (fp == NULL ||
227		    FILEPROC_TYPE(fp) != FTYPE_GUARDED)
228			continue;
229
230		struct guarded_fileproc *gfp = FP_TO_GFP(fp);
231
232		if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic)
233			panic("%s: corrupt gfp %p flags %x",
234			    __func__, gfp, fp->f_flags);
235
236		if (gfp->gf_thread == t) {
237			mach_exception_data_type_t code, subcode;
238
239			gfp->gf_thread = NULL;
240
241			/*
242			 * EXC_GUARD exception code namespace.
243			 *
244			 * code:
245			 * +-------------------------------------------------+
246			 * | [63:61] guard type | [60:0] guard-specific data |
247			 * +-------------------------------------------------+
248			 *
249			 * subcode:
250			 * +-------------------------------------------------+
251			 * |       [63:0] guard-specific data                |
252			 * +-------------------------------------------------+
253			 *
254			 * At the moment, we have just one guard type: file
255			 * descriptor guards.
256			 *
257			 * File descriptor guards use the exception codes like
258			 * so:
259			 *
260			 * code:
261			 * +--------------------------------------------------+
262			 * |[63:61] GUARD_TYPE_FD | [60:32] flavor | [31:0] fd|
263			 * +--------------------------------------------------+
264			 *
265			 * subcode:
266			 * +--------------------------------------------------+
267			 * |       [63:0] guard value                         |
268			 * +--------------------------------------------------+
269			 */
270			code = (((uint64_t)GUARD_TYPE_FD) << 61) |
271			       (((uint64_t)gfp->gf_exc_code) << 32) |
272			       ((uint64_t)gfp->gf_exc_fd);
273			subcode = gfp->gf_guard;
274			proc_fdunlock(p);
275
276			(void) task_exception_notify(EXC_GUARD, code, subcode);
277			psignal(p, SIGKILL);
278
279			return;
280		}
281	}
282	proc_fdunlock(p);
283}
284
285/*
286 * Experimental guarded file descriptor SPIs
287 */
288
289/*
290 * int guarded_open_np(const char *pathname, int flags,
291 *     const guardid_t *guard, u_int guardflags, ...);
292 *
293 * In this initial implementation, GUARD_DUP must be specified.
294 * GUARD_CLOSE, GUARD_SOCKET_IPC and GUARD_FILEPORT are optional.
295 *
296 * If GUARD_DUP wasn't specified, then we'd have to do the (extra) work
297 * to allow dup-ing a descriptor to inherit the guard onto the new
298 * descriptor.  (Perhaps GUARD_DUP behaviours should just always be true
299 * for a guarded fd?  Or, more sanely, all the dup operations should
300 * just always propagate the guard?)
301 *
302 * Guarded descriptors are always close-on-exec, and GUARD_CLOSE
303 * requires close-on-fork; O_CLOEXEC must be set in flags.
304 * This setting is immutable; attempts to clear the flag will
305 * cause a guard exception.
306 */
307int
308guarded_open_np(proc_t p, struct guarded_open_np_args *uap, int32_t *retval)
309{
310	if ((uap->flags & O_CLOEXEC) == 0)
311		return (EINVAL);
312
313#define GUARD_REQUIRED (GUARD_DUP)
314#define GUARD_ALL      (GUARD_REQUIRED |	\
315			(GUARD_CLOSE | GUARD_SOCKET_IPC | GUARD_FILEPORT))
316
317	if (((uap->guardflags & GUARD_REQUIRED) != GUARD_REQUIRED) ||
318	    ((uap->guardflags & ~GUARD_ALL) != 0))
319		return (EINVAL);
320
321	int error;
322	struct gfp_crarg crarg = {
323		.gca_attrs = uap->guardflags
324	};
325
326	if ((error = copyin(uap->guard,
327	    &(crarg.gca_guard), sizeof (crarg.gca_guard))) != 0)
328		return (error);
329
330	/*
331	 * Disallow certain guard values -- is zero enough?
332	 */
333	if (crarg.gca_guard == 0)
334		return (EINVAL);
335
336	struct filedesc *fdp = p->p_fd;
337	struct vnode_attr va;
338	struct nameidata nd;
339	vfs_context_t ctx = vfs_context_current();
340	int cmode;
341
342	VATTR_INIT(&va);
343	cmode = ((uap->mode & ~fdp->fd_cmask) & ALLPERMS) & ~S_ISTXT;
344	VATTR_SET(&va, va_mode, cmode & ACCESSPERMS);
345
346	NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW | AUDITVNPATH1, UIO_USERSPACE,
347	       uap->path, ctx);
348
349	return (open1(ctx, &nd, uap->flags | O_CLOFORK, &va,
350	    guarded_fileproc_alloc_init, &crarg, retval));
351}
352
353/*
354 * int guarded_kqueue_np(const guardid_t *guard, u_int guardflags);
355 *
356 * Create a guarded kqueue descriptor with guardid and guardflags.
357 *
358 * Same restrictions on guardflags as for guarded_open_np().
359 * All kqueues are -always- close-on-exec and close-on-fork by themselves.
360 *
361 * XXX	Is it ever sensible to allow a kqueue fd (guarded or not) to
362 *	be sent to another process via a fileport or socket?
363 */
364int
365guarded_kqueue_np(proc_t p, struct guarded_kqueue_np_args *uap, int32_t *retval)
366{
367	if (((uap->guardflags & GUARD_REQUIRED) != GUARD_REQUIRED) ||
368	    ((uap->guardflags & ~GUARD_ALL) != 0))
369		return (EINVAL);
370
371	int error;
372	struct gfp_crarg crarg = {
373		.gca_attrs = uap->guardflags
374	};
375
376	if ((error = copyin(uap->guard,
377	    &(crarg.gca_guard), sizeof (crarg.gca_guard))) != 0)
378		return (error);
379
380	if (crarg.gca_guard == 0)
381		return (EINVAL);
382
383	return (kqueue_body(p, guarded_fileproc_alloc_init, &crarg, retval));
384}
385
386/*
387 * int guarded_close_np(int fd, const guardid_t *guard);
388 */
389int
390guarded_close_np(proc_t p, struct guarded_close_np_args *uap,
391    __unused int32_t *retval)
392{
393	struct guarded_fileproc *gfp;
394	int fd = uap->fd;
395	int error;
396	guardid_t uguard;
397
398	AUDIT_SYSCLOSE(p, fd);
399
400	if ((error = copyin(uap->guard, &uguard, sizeof (uguard))) != 0)
401		return (error);
402
403	proc_fdlock(p);
404	if ((error = fp_lookup_guarded(p, fd, uguard, &gfp)) != 0) {
405		proc_fdunlock(p);
406		return (error);
407	}
408	error = close_internal_locked(p, fd, GFP_TO_FP(gfp), 0);
409	proc_fdunlock(p);
410	return (error);
411}
412
413/*
414 * int
415 * change_fdguard_np(int fd, const guardid_t *guard, u_int guardflags,
416 *    const guardid_t *nguard, u_int nguardflags, int *fdflagsp);
417 *
418 * Given a file descriptor, atomically exchange <guard, guardflags> for
419 * a new guard <nguard, nguardflags>, returning the previous fd
420 * flags (see fcntl:F_SETFD) in *fdflagsp.
421 *
422 * This syscall can be used to either (a) add a new guard to an existing
423 * unguarded file descriptor (b) remove the old guard from an existing
424 * guarded file descriptor or (c) change the guard (guardid and/or
425 * guardflags) on a guarded file descriptor.
426 *
427 * If 'guard' is NULL, fd must be unguarded at entry. If the call completes
428 * successfully the fd will be guarded with <nguard, nguardflags>.
429 *
430 * Guarding a file descriptor has some side-effects on the "fdflags"
431 * associated with the descriptor - in particular FD_CLOEXEC is
432 * forced ON unconditionally, and FD_CLOFORK is forced ON by GUARD_CLOSE.
433 * Callers who wish to subsequently restore the state of the fd should save
434 * the value of *fdflagsp after a successful invocation.
435 *
436 * If 'nguard' is NULL, fd must be guarded at entry, <guard, guardflags>
437 * must match with what's already guarding the descriptor, and the
438 * result will be to completely remove the guard.  Note also that the
439 * fdflags are copied to the descriptor from the incoming *fdflagsp argument.
440 *
441 * If the descriptor is guarded, and neither 'guard' nor 'nguard' is NULL
442 * and <guard, guardflags> matches what's already guarding the descriptor,
443 * then <nguard, nguardflags> becomes the new guard.  In this case, even if
444 * the GUARD_CLOSE flag is being cleared, it is still possible to continue
445 * to keep FD_CLOFORK on the descriptor by passing FD_CLOFORK via fdflagsp.
446 *
447 * Example 1: Guard an unguarded descriptor during a set of operations,
448 * then restore the original state of the descriptor.
449 *
450 * int sav_flags = 0;
451 * change_fdguard_np(fd, NULL, 0, &myguard, GUARD_CLOSE, &sav_flags);
452 * // do things with now guarded 'fd'
453 * change_fdguard_np(fd, &myguard, GUARD_CLOSE, NULL, 0, &sav_flags);
454 * // fd now unguarded.
455 *
456 * Example 2: Change the guard of a guarded descriptor during a set of
457 * operations, then restore the original state of the descriptor.
458 *
459 * int sav_flags = (gdflags & GUARD_CLOSE) ? FD_CLOFORK : 0;
460 * change_fdguard_np(fd, &gd, gdflags, &myguard, GUARD_CLOSE, &sav_flags);
461 * // do things with 'fd' with a different guard
462 * change_fdguard_np(fd, &myg, GUARD_CLOSE, &gd, gdflags, &sav_flags);
463 * // back to original guarded state
464 */
465
466#define FDFLAGS_GET(p, fd) (*fdflags(p, fd) & (UF_EXCLOSE|UF_FORKCLOSE))
467#define FDFLAGS_SET(p, fd, bits) \
468	   (*fdflags(p, fd) |= ((bits) & (UF_EXCLOSE|UF_FORKCLOSE)))
469#define FDFLAGS_CLR(p, fd, bits) \
470	   (*fdflags(p, fd) &= ~((bits) & (UF_EXCLOSE|UF_FORKCLOSE)))
471
472int
473change_fdguard_np(proc_t p, struct change_fdguard_np_args *uap,
474    __unused int32_t *retval)
475{
476	struct fileproc *fp;
477	int fd = uap->fd;
478	int error;
479	guardid_t oldg = 0, newg = 0;
480	int nfdflags = 0;
481
482	if (0 != uap->guard &&
483	    0 != (error = copyin(uap->guard, &oldg, sizeof (oldg))))
484		return (error); /* can't copyin current guard */
485
486	if (0 != uap->nguard &&
487	    0 != (error = copyin(uap->nguard, &newg, sizeof (newg))))
488		return (error); /* can't copyin new guard */
489
490	if (0 != uap->fdflagsp &&
491	    0 != (error = copyin(uap->fdflagsp, &nfdflags, sizeof (nfdflags))))
492		return (error); /* can't copyin new fdflags */
493
494	proc_fdlock(p);
495restart:
496	if ((error = fp_lookup(p, fd, &fp, 1)) != 0) {
497		proc_fdunlock(p);
498		return (error);
499	}
500
501	if (0 != uap->fdflagsp) {
502		int ofdflags = FDFLAGS_GET(p, fd);
503		int ofl = ((ofdflags & UF_EXCLOSE) ? FD_CLOEXEC : 0) |
504			((ofdflags & UF_FORKCLOSE) ? FD_CLOFORK : 0);
505		proc_fdunlock(p);
506		if (0 != (error = copyout(&ofl, uap->fdflagsp, sizeof (ofl)))) {
507			proc_fdlock(p);
508			goto dropout; /* can't copyout old fdflags */
509		}
510		proc_fdlock(p);
511	}
512
513	if (FILEPROC_TYPE(fp) == FTYPE_GUARDED) {
514		if (0 == uap->guard || 0 == uap->guardflags)
515			error = EINVAL; /* missing guard! */
516		else if (0 == oldg)
517			error = EPERM; /* guardids cannot be zero */
518	} else {
519		if (0 != uap->guard || 0 != uap->guardflags)
520			error = EINVAL; /* guard provided, but none needed! */
521	}
522
523	if (0 != error)
524		goto dropout;
525
526	if (0 != uap->nguard) {
527		/*
528		 * There's a new guard in town.
529		 */
530		if (0 == newg)
531			error = EINVAL; /* guards cannot contain zero */
532		else if (0 == uap->nguardflags)
533			error = EINVAL; /* attributes cannot be zero */
534		else if (((uap->nguardflags & GUARD_REQUIRED) != GUARD_REQUIRED) ||
535		    ((uap->guardflags & ~GUARD_ALL) != 0))
536			error = EINVAL; /* must have valid attributes too */
537
538		if (0 != error)
539			goto dropout;
540
541		if (FILEPROC_TYPE(fp) == FTYPE_GUARDED) {
542			/*
543			 * Replace old guard with new guard
544			 */
545			struct guarded_fileproc *gfp = FP_TO_GFP(fp);
546
547			if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic)
548				panic("%s: corrupt gfp %p flags %x",
549				      __func__, gfp, fp->f_flags);
550
551			if (oldg == gfp->gf_guard &&
552			    uap->guardflags == gfp->gf_attrs) {
553				/*
554				 * Must match existing guard + attributes
555				 * before we'll swap them to new ones, managing
556				 * fdflags "side-effects" as we go.   Note that
557				 * userland can request FD_CLOFORK semantics.
558				 */
559				if (gfp->gf_attrs & GUARD_CLOSE)
560					FDFLAGS_CLR(p, fd, UF_FORKCLOSE);
561				gfp->gf_guard = newg;
562				gfp->gf_attrs = uap->nguardflags;
563				if (gfp->gf_attrs & GUARD_CLOSE)
564					FDFLAGS_SET(p, fd, UF_FORKCLOSE);
565				FDFLAGS_SET(p, fd,
566				    (nfdflags & FD_CLOFORK) ? UF_FORKCLOSE : 0);
567			} else {
568				error = EPERM;
569			}
570			goto dropout;
571		} else {
572			/*
573			 * Add a guard to a previously unguarded descriptor
574			 */
575			switch (FILEGLOB_DTYPE(fp->f_fglob)) {
576			case DTYPE_VNODE:
577			case DTYPE_PIPE:
578			case DTYPE_SOCKET:
579			case DTYPE_KQUEUE:
580				break;
581			default:
582				error = ENOTSUP;
583				goto dropout;
584			}
585
586			proc_fdunlock(p);
587
588			struct gfp_crarg crarg = {
589				.gca_guard = newg,
590				.gca_attrs = uap->nguardflags
591			};
592			struct fileproc *nfp =
593				guarded_fileproc_alloc_init(&crarg);
594
595			proc_fdlock(p);
596
597			switch (error = fp_tryswap(p, fd, nfp)) {
598				struct guarded_fileproc *gfp;
599
600			case 0: /* guarded-ness comes with side-effects */
601				gfp = FP_TO_GFP(nfp);
602				if (gfp->gf_attrs & GUARD_CLOSE)
603					FDFLAGS_SET(p, fd, UF_FORKCLOSE);
604				FDFLAGS_SET(p, fd, UF_EXCLOSE);
605				(void) fp_drop(p, fd, nfp, 1);
606				fileproc_free(fp);
607				break;
608			case EKEEPLOOKING: /* f_iocount indicates a collision */
609				(void) fp_drop(p, fd, fp, 1);
610				fileproc_free(nfp);
611				goto restart;
612			default:
613				(void) fp_drop(p, fd, fp, 1);
614				fileproc_free(nfp);
615				break;
616			}
617			proc_fdunlock(p);
618			return (error);
619		}
620	} else {
621		/*
622		 * No new guard.
623		 */
624		if (FILEPROC_TYPE(fp) == FTYPE_GUARDED) {
625			/*
626			 * Remove the guard altogether.
627			 */
628			struct guarded_fileproc *gfp = FP_TO_GFP(fp);
629
630			if (0 != uap->nguardflags) {
631				error = EINVAL;
632				goto dropout;
633			}
634
635			if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic)
636				panic("%s: corrupt gfp %p flags %x",
637				      __func__, gfp, fp->f_flags);
638
639			if (oldg != gfp->gf_guard ||
640			    uap->guardflags != gfp->gf_attrs) {
641				error = EPERM;
642				goto dropout;
643			}
644
645			proc_fdunlock(p);
646			struct fileproc *nfp = fileproc_alloc_init(NULL);
647			proc_fdlock(p);
648
649			switch (error = fp_tryswap(p, fd, nfp)) {
650			case 0: /* undo side-effects of guarded-ness */
651				FDFLAGS_CLR(p, fd, UF_FORKCLOSE | UF_EXCLOSE);
652				FDFLAGS_SET(p, fd,
653				    (nfdflags & FD_CLOFORK) ? UF_FORKCLOSE : 0);
654				FDFLAGS_SET(p, fd,
655				    (nfdflags & FD_CLOEXEC) ? UF_EXCLOSE : 0);
656				(void) fp_drop(p, fd, nfp, 1);
657				fileproc_free(fp);
658				break;
659			case EKEEPLOOKING: /* f_iocount indicates collision */
660				(void) fp_drop(p, fd, fp, 1);
661				fileproc_free(nfp);
662				goto restart;
663			default:
664				(void) fp_drop(p, fd, fp, 1);
665				fileproc_free(nfp);
666				break;
667			}
668			proc_fdunlock(p);
669			return (error);
670		} else {
671			/*
672			 * Not already guarded, and no new guard?
673			 */
674			error = EINVAL;
675		}
676	}
677
678dropout:
679	(void) fp_drop(p, fd, fp, 1);
680	proc_fdunlock(p);
681	return (error);
682}
683
684