Page MenuHomeFreeBSD

D57163.id181408.diff
No OneTemporary

D57163.id181408.diff

diff --git a/lib/libsys/Makefile.sys b/lib/libsys/Makefile.sys
--- a/lib/libsys/Makefile.sys
+++ b/lib/libsys/Makefile.sys
@@ -500,7 +500,8 @@
pdfork.2 pdopenpid.2 \
pdfork.2 pdkill.2 \
pdfork.2 pdrfork.2 \
- pdfork.2 pdwait.2
+ pdfork.2 pdwait.2 \
+ pdfork.2 pddupfd.2
MLINKS+=pipe.2 pipe2.2
MLINKS+=poll.2 ppoll.2
MLINKS+=rctl_add_rule.2 rctl_get_limits.2 \
diff --git a/lib/libsys/Symbol.sys.map b/lib/libsys/Symbol.sys.map
--- a/lib/libsys/Symbol.sys.map
+++ b/lib/libsys/Symbol.sys.map
@@ -391,6 +391,7 @@
};
FBSD_1.9 {
+ pddupfd;
pdopenpid;
pdrfork;
pdrfork_thread;
diff --git a/lib/libsys/_libsys.h b/lib/libsys/_libsys.h
--- a/lib/libsys/_libsys.h
+++ b/lib/libsys/_libsys.h
@@ -476,6 +476,7 @@
typedef int (__sys_pdwait_t)(int, int *, int, struct __wrusage *, struct __siginfo *);
typedef int (__sys_renameat2_t)(int, const char *, int, const char *, int);
typedef int (__sys_pdopenpid_t)(pid_t, int);
+typedef int (__sys_pddupfd_t)(int, int, int);
_Noreturn void __sys__exit(int rval);
int __sys_fork(void);
@@ -887,6 +888,7 @@
int __sys_pdwait(int fd, int * status, int options, struct __wrusage * wrusage, struct __siginfo * info);
int __sys_renameat2(int oldfd, const char * old, int newfd, const char * new, int flags);
int __sys_pdopenpid(pid_t pid, int flags);
+int __sys_pddupfd(int pd, int fd, int flags);
__END_DECLS
#endif /* __LIBSYS_H_ */
diff --git a/lib/libsys/pdfork.2 b/lib/libsys/pdfork.2
--- a/lib/libsys/pdfork.2
+++ b/lib/libsys/pdfork.2
@@ -39,7 +39,8 @@
.Nm pdopenpid ,
.Nm pdgetpid ,
.Nm pdkill ,
-.Nm pdwait
+.Nm pdwait ,
+.Nm pddupfd
.Nd System calls to manage process descriptors
.Sh LIBRARY
.Lb libc
@@ -63,6 +64,8 @@
.Fa "struct __wrusage *wrusage"
.Fa "struct __siginfo *info"
.Fc
+.Ft int
+.Fn pddupfd "int fd" "int remotefd" "int flags"
.Sh DESCRIPTION
Process descriptors are special file descriptors that represent processes,
and are created using
@@ -174,6 +177,23 @@
.Xr wait6
system call for the behavior specification.
.Pp
+The
+.Fn pddupfd
+allows the caller to duplicate a file descriptor across the process
+boundaries.
+The function returns the new file descriptor that points to the same file
+as the file descriptor
+.Fa remotefd
+in the process specified by the
+.Fa fd
+process descriptor.
+The returned file descriptor has the
+.Va O_CLOEXEC
+flag set.
+The
+.Fa flags
+argument is reserved and must be zero.
+.Pp
The following system calls also have effects specific to process descriptors:
.Pp
.Xr fstat 2
@@ -226,8 +246,9 @@
.Fn pdopenpid ,
.Fn pdgetpid ,
.Fn pdkill ,
+.Fn pdwait ,
and
-.Fn pdwait
+.Fn pddupfd
return 0 on success and -1 on failure.
.Sh ERRORS
These functions may return the same error numbers as their PID-based equivalents
@@ -304,6 +325,30 @@
.Xr close 2
on it.
.El
+.Pp
+The
+.Fn pddupfd
+returns the following errors:
+.Bl -tag -width Er
+.It Bq Er EINVAL
+The
+.Fa flags
+argument is not zero.
+.It Bq Er ESRCH
+The process specified by the file descriptor
+.Fa fd
+exited.
+.It Bq Er EBADF
+The file descriptor
+.Fa fd
+is not a process file descriptor.
+.It Bq Er EBADF
+The file descriptor
+.Fa remotefd
+is not a valid file descriptor in the specified process.
+.It Bq Er ENOENT
+The specified process does not have a file descriptor table.
+.El
.Sh SEE ALSO
.Xr close 2 ,
.Xr fork 2 ,
@@ -330,7 +375,9 @@
.Fx 15.1 .
The
.Fn pdopenpid
-system call first appeared in
+and
+.Fn pddupfd
+system calls first appeared in
.Fx 16.0 .
.Pp
Support for process descriptors mode was developed as part of the
@@ -354,5 +401,7 @@
.An Alan Somers Aq Mt asomers@FreeBSD.org .
The
.Fn pdopenpid
-function was developed by
+and
+.Fn pddupfd
+functions were developed by
.An Konstantin Belousov Aq Mt kib@FreeBSD.org .
diff --git a/lib/libsys/syscalls.map b/lib/libsys/syscalls.map
--- a/lib/libsys/syscalls.map
+++ b/lib/libsys/syscalls.map
@@ -827,4 +827,6 @@
__sys_renameat2;
_pdopenpid;
__sys_pdopenpid;
+ _pddupfd;
+ __sys_pddupfd;
};
diff --git a/sys/compat/freebsd32/freebsd32_syscall.h b/sys/compat/freebsd32/freebsd32_syscall.h
--- a/sys/compat/freebsd32/freebsd32_syscall.h
+++ b/sys/compat/freebsd32/freebsd32_syscall.h
@@ -521,4 +521,5 @@
#define FREEBSD32_SYS_freebsd32_pdwait 601
#define FREEBSD32_SYS_renameat2 602
#define FREEBSD32_SYS_pdopenpid 603
-#define FREEBSD32_SYS_MAXSYSCALL 604
+#define FREEBSD32_SYS_pddupfd 604
+#define FREEBSD32_SYS_MAXSYSCALL 605
diff --git a/sys/compat/freebsd32/freebsd32_syscalls.c b/sys/compat/freebsd32/freebsd32_syscalls.c
--- a/sys/compat/freebsd32/freebsd32_syscalls.c
+++ b/sys/compat/freebsd32/freebsd32_syscalls.c
@@ -609,4 +609,5 @@
"freebsd32_pdwait", /* 601 = freebsd32_pdwait */
"renameat2", /* 602 = renameat2 */
"pdopenpid", /* 603 = pdopenpid */
+ "pddupfd", /* 604 = pddupfd */
};
diff --git a/sys/compat/freebsd32/freebsd32_sysent.c b/sys/compat/freebsd32/freebsd32_sysent.c
--- a/sys/compat/freebsd32/freebsd32_sysent.c
+++ b/sys/compat/freebsd32/freebsd32_sysent.c
@@ -671,4 +671,5 @@
{ .sy_narg = AS(freebsd32_pdwait_args), .sy_call = (sy_call_t *)freebsd32_pdwait, .sy_auevent = AUE_PDWAIT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 601 = freebsd32_pdwait */
{ .sy_narg = AS(renameat2_args), .sy_call = (sy_call_t *)sys_renameat2, .sy_auevent = AUE_RENAMEAT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 602 = renameat2 */
{ .sy_narg = AS(pdopenpid_args), .sy_call = (sy_call_t *)sys_pdopenpid, .sy_auevent = AUE_PDOPENPID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 603 = pdopenpid */
+ { .sy_narg = AS(pddupfd_args), .sy_call = (sy_call_t *)sys_pddupfd, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 604 = pddupfd */
};
diff --git a/sys/compat/freebsd32/freebsd32_systrace_args.c b/sys/compat/freebsd32/freebsd32_systrace_args.c
--- a/sys/compat/freebsd32/freebsd32_systrace_args.c
+++ b/sys/compat/freebsd32/freebsd32_systrace_args.c
@@ -3466,6 +3466,15 @@
*n_args = 2;
break;
}
+ /* pddupfd */
+ case 604: {
+ struct pddupfd_args *p = params;
+ iarg[a++] = p->pd; /* int */
+ iarg[a++] = p->fd; /* int */
+ iarg[a++] = p->flags; /* int */
+ *n_args = 3;
+ break;
+ }
default:
*n_args = 0;
break;
@@ -9368,6 +9377,22 @@
break;
};
break;
+ /* pddupfd */
+ case 604:
+ switch (ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
default:
break;
};
@@ -11306,6 +11331,11 @@
if (ndx == 0 || ndx == 1)
p = "int";
break;
+ /* pddupfd */
+ case 604:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
default:
break;
};
diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c
--- a/sys/kern/init_sysent.c
+++ b/sys/kern/init_sysent.c
@@ -670,4 +670,5 @@
{ .sy_narg = AS(pdwait_args), .sy_call = (sy_call_t *)sys_pdwait, .sy_auevent = AUE_PDWAIT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 601 = pdwait */
{ .sy_narg = AS(renameat2_args), .sy_call = (sy_call_t *)sys_renameat2, .sy_auevent = AUE_RENAMEAT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 602 = renameat2 */
{ .sy_narg = AS(pdopenpid_args), .sy_call = (sy_call_t *)sys_pdopenpid, .sy_auevent = AUE_PDOPENPID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 603 = pdopenpid */
+ { .sy_narg = AS(pddupfd_args), .sy_call = (sy_call_t *)sys_pddupfd, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 604 = pddupfd */
};
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -3156,13 +3156,21 @@
#endif
int
-fget_remote(struct thread *td, struct proc *p, int fd, struct file **fpp)
+fget_remote(struct thread *td, struct proc *p, int fd, struct filecaps *fcaps,
+ uint8_t *fd_flags, struct file **fpp)
{
struct filedesc *fdp;
struct file *fp;
int error;
+ bool copied __diagused;
- if (p == td->td_proc) /* curproc */
+ /*
+ * Both fcaps and fd_flags must be either requested together,
+ * or not at all.
+ */
+ MPASS((!(fcaps == NULL) ^ (fd_flags == NULL)));
+
+ if (p == td->td_proc && fcaps == NULL) /* curproc */
return (fget_unlocked(td, fd, &cap_no_rights, fpp));
PROC_LOCK(p);
@@ -3175,6 +3183,15 @@
fp = fget_noref(fdp, fd);
if (fp != NULL && fhold(fp)) {
*fpp = fp;
+ if (fd_flags != NULL) {
+ *fd_flags = fde_to_fd_flags(fdp->fd_ofiles[fd].
+ fde_flags);
+ }
+ if (fcaps != NULL) {
+ copied = filecaps_copy(
+ &fdp->fd_ofiles[fd].fde_caps, fcaps, true);
+ MPASS(copied);
+ }
error = 0;
} else {
error = EBADF;
@@ -3215,7 +3232,7 @@
}
for (fd = 0; fd <= highfd; fd++) {
- error1 = fget_remote(td, p, fd, &fp);
+ error1 = fget_remote(td, p, fd, NULL, NULL, &fp);
if (error1 != 0)
continue;
error = fn(p, fd, fp, arg);
diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
--- a/sys/kern/kern_event.c
+++ b/sys/kern/kern_event.c
@@ -3355,7 +3355,7 @@
struct kqueue *kq;
int error;
- error = fget_remote(td, p, kq_fd, &fp);
+ error = fget_remote(td, p, kq_fd, NULL, NULL, &fp);
if (error == 0) {
if (fp->f_type != DTYPE_KQUEUE) {
error = EINVAL;
diff --git a/sys/kern/subr_capability.c b/sys/kern/subr_capability.c
--- a/sys/kern/subr_capability.c
+++ b/sys/kern/subr_capability.c
@@ -88,6 +88,7 @@
const cap_rights_t cap_mkdirat_rights = CAP_RIGHTS_INITIALIZER(CAP_MKDIRAT);
const cap_rights_t cap_mkfifoat_rights = CAP_RIGHTS_INITIALIZER(CAP_MKFIFOAT);
const cap_rights_t cap_mknodat_rights = CAP_RIGHTS_INITIALIZER(CAP_MKNODAT);
+const cap_rights_t cap_pddupfd_rights = CAP_RIGHTS_INITIALIZER(CAP_PDDUPFD);
const cap_rights_t cap_pdgetpid_rights = CAP_RIGHTS_INITIALIZER(CAP_PDGETPID);
const cap_rights_t cap_pdkill_rights = CAP_RIGHTS_INITIALIZER(CAP_PDKILL);
const cap_rights_t cap_pdwait_rights = CAP_RIGHTS_INITIALIZER(CAP_PDWAIT);
diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c
--- a/sys/kern/sys_generic.c
+++ b/sys/kern/sys_generic.c
@@ -2185,9 +2185,9 @@
switch (type) {
case KCMP_FILE:
case KCMP_FILEOBJ:
- error = fget_remote(td, p1, idx1, &fp1);
+ error = fget_remote(td, p1, idx1, NULL, NULL, &fp1);
if (error == 0) {
- error = fget_remote(td, p2, idx2, &fp2);
+ error = fget_remote(td, p2, idx2, NULL, NULL, &fp2);
if (error == 0) {
if (type == KCMP_FILEOBJ)
res = fo_cmp(fp1, fp2, td);
diff --git a/sys/kern/sys_procdesc.c b/sys/kern/sys_procdesc.c
--- a/sys/kern/sys_procdesc.c
+++ b/sys/kern/sys_procdesc.c
@@ -67,6 +67,7 @@
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
+#include <sys/imgact.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/mutex.h>
@@ -702,3 +703,84 @@
return (EINVAL);
return (kern_pdopenpid(td, args->pid, args->flags));
}
+
+static int
+kern_pddupfd(struct thread *td, int pdfd, int fd, int flags)
+{
+ struct proc *p;
+ struct file *fp, *pfp;
+ struct procdesc *pd;
+ struct filecaps fcaps;
+ uint8_t fd_flags;
+ int error, fdr;
+
+ error = fget(td, pdfd, &cap_pddupfd_rights, &pfp);
+ if (error != 0)
+ return (error);
+ if (pfp->f_type != DTYPE_PROCDESC) {
+ error = EBADF;
+ goto out;
+ }
+ pd = pfp->f_data;
+again:
+ sx_slock(&proctree_lock);
+ p = pd->pd_proc;
+ if (p != NULL) {
+ AUDIT_ARG_PROCESS(p);
+ PROC_LOCK(p);
+ sx_sunlock(&proctree_lock);
+ if ((p->p_flag & P_WEXIT) != 0) {
+ error = ESRCH;
+ } else {
+ /*
+ * Block the target process from entering
+ * execve(). We need to ensure that the
+ * p_candebug() predicate is stable until the
+ * fget_remote() call ends even after the
+ * process lock is dropped. For that, the
+ * process must not change uid/suid .
+ */
+ if (!execve_block(td, p))
+ goto again;
+ error = p_candebug(td, p);
+ if (error == 0)
+ _PHOLD(p);
+ else
+ execve_unblock(td, p);
+ }
+ PROC_UNLOCK(p);
+ if (error != 0)
+ goto out;
+
+ error = fget_remote(td, p, fd, &fcaps, &fd_flags, &fp);
+ PROC_LOCK(p);
+ execve_unblock(td, p);
+ _PRELE(p);
+ PROC_UNLOCK(p);
+ if (error == 0) {
+ error = finstall_refed(td, fp, &fdr, O_CLOEXEC |
+ ((fd_flags & FD_RESOLVE_BENEATH) != 0 ?
+ O_RESOLVE_BENEATH : 0), &fcaps);
+ if (error != 0) {
+ fdrop(fp, td);
+ filecaps_free(&fcaps);
+ } else {
+ td->td_retval[0] = fdr;
+ }
+ }
+ } else {
+ sx_sunlock(&proctree_lock);
+ error = ESRCH;
+ }
+out:
+ fdrop(pfp, td);
+ return (error);
+}
+
+int
+sys_pddupfd(struct thread *td, struct pddupfd_args *args)
+{
+ if (args->flags != 0)
+ return (EINVAL);
+ return (kern_pddupfd(td, args->pd, args->fd, args->flags));
+}
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -31,29 +31,28 @@
* SUCH DAMAGE.
*/
-#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/caprights.h>
+#include <sys/filedesc.h>
#include <sys/imgact.h>
#include <sys/ktr.h>
#include <sys/limits.h>
#include <sys/lock.h>
+#include <sys/malloc.h>
#include <sys/mman.h>
#include <sys/mutex.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/ptrace.h>
#include <sys/reg.h>
+#include <sys/rwlock.h>
+#include <sys/signalvar.h>
#include <sys/sleepqueue.h>
+#include <sys/sx.h>
#include <sys/syscallsubr.h>
#include <sys/sysent.h>
#include <sys/sysproto.h>
-#include <sys/priv.h>
-#include <sys/proc.h>
#include <sys/vnode.h>
-#include <sys/ptrace.h>
-#include <sys/rwlock.h>
-#include <sys/sx.h>
-#include <sys/malloc.h>
-#include <sys/signalvar.h>
-#include <sys/caprights.h>
-#include <sys/filedesc.h>
#include <security/audit/audit.h>
diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c
--- a/sys/kern/syscalls.c
+++ b/sys/kern/syscalls.c
@@ -609,4 +609,5 @@
"pdwait", /* 601 = pdwait */
"renameat2", /* 602 = renameat2 */
"pdopenpid", /* 603 = pdopenpid */
+ "pddupfd", /* 604 = pddupfd */
};
diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master
--- a/sys/kern/syscalls.master
+++ b/sys/kern/syscalls.master
@@ -3435,5 +3435,12 @@
int flags
);
}
+604 AUE_NULL STD {
+ int pddupfd(
+ int pd,
+ int fd,
+ int flags
+ );
+ }
; vim: syntax=off
diff --git a/sys/kern/systrace_args.c b/sys/kern/systrace_args.c
--- a/sys/kern/systrace_args.c
+++ b/sys/kern/systrace_args.c
@@ -3563,6 +3563,15 @@
*n_args = 2;
break;
}
+ /* pddupfd */
+ case 604: {
+ struct pddupfd_args *p = params;
+ iarg[a++] = p->pd; /* int */
+ iarg[a++] = p->fd; /* int */
+ iarg[a++] = p->flags; /* int */
+ *n_args = 3;
+ break;
+ }
default:
*n_args = 0;
break;
@@ -9542,6 +9551,22 @@
break;
};
break;
+ /* pddupfd */
+ case 604:
+ switch (ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
default:
break;
};
@@ -11575,6 +11600,11 @@
if (ndx == 0 || ndx == 1)
p = "int";
break;
+ /* pddupfd */
+ case 604:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
default:
break;
};
diff --git a/sys/sys/caprights.h b/sys/sys/caprights.h
--- a/sys/sys/caprights.h
+++ b/sys/sys/caprights.h
@@ -90,6 +90,7 @@
extern const cap_rights_t cap_mknodat_rights;
extern const cap_rights_t cap_mmap_rights;
extern const cap_rights_t cap_no_rights;
+extern const cap_rights_t cap_pddupfd_rights;
extern const cap_rights_t cap_pdgetpid_rights;
extern const cap_rights_t cap_pdkill_rights;
extern const cap_rights_t cap_pdwait_rights;
diff --git a/sys/sys/capsicum.h b/sys/sys/capsicum.h
--- a/sys/sys/capsicum.h
+++ b/sys/sys/capsicum.h
@@ -262,12 +262,7 @@
/* Process management via process descriptors. */
/* Allows for pdgetpid(2). */
#define CAP_PDGETPID CAPRIGHT(1, 0x0000000000000200ULL)
-/*
- * Allows for pdwait4(2).
- *
- * XXX: this constant was imported unused, but is targeted to be implemented
- * in the future (bug 235871).
- */
+/* Allows for pdwait(2). */
#define CAP_PDWAIT CAPRIGHT(1, 0x0000000000000400ULL)
/* Allows for pdkill(2). */
#define CAP_PDKILL CAPRIGHT(1, 0x0000000000000800ULL)
@@ -301,7 +296,9 @@
#define CAP_INOTIFY_ADD CAPRIGHT(1, 0x0000000000200000ULL)
#define CAP_INOTIFY_RM CAPRIGHT(1, 0x0000000000400000ULL)
-#define CAP_UNUSED1_24 CAPRIGHT(1, 0x0000000000800000ULL)
+/* Allows pdgetfd(2). */
+#define CAP_PDDUPFD CAPRIGHT(1, 0x0000000000800000ULL)
+
#define CAP_UNUSED1_25 CAPRIGHT(1, 0x0000000001000000ULL)
#define CAP_UNUSED1_26 CAPRIGHT(1, 0x0000000002000000ULL)
#define CAP_UNUSED1_27 CAPRIGHT(1, 0x0000000004000000ULL)
@@ -337,7 +334,7 @@
#define CAP_UNUSED1_57 CAPRIGHT(1, 0x0100000000000000ULL)
/* All used bits for index 1. */
-#define CAP_ALL1 CAPRIGHT(1, 0x00000000007FFFFFULL)
+#define CAP_ALL1 CAPRIGHT(1, 0x0000000000FFFFFFULL)
/* Backward compatibility. */
#define CAP_POLL_EVENT CAP_EVENT
diff --git a/sys/sys/file.h b/sys/sys/file.h
--- a/sys/sys/file.h
+++ b/sys/sys/file.h
@@ -283,7 +283,8 @@
int fget_fcntl(struct thread *td, int fd, const cap_rights_t *rightsp,
int needfcntl, struct file **fpp);
int _fdrop(struct file *fp, struct thread *td);
-int fget_remote(struct thread *td, struct proc *p, int fd, struct file **fpp);
+int fget_remote(struct thread *td, struct proc *p, int fd,
+ struct filecaps *fcaps, uint8_t *fd_flags, struct file **fpp);
int fget_remote_foreach(struct thread *td, struct proc *p,
int (*fn)(struct proc *, int, struct file *, void *), void *arg);
diff --git a/sys/sys/procdesc.h b/sys/sys/procdesc.h
--- a/sys/sys/procdesc.h
+++ b/sys/sys/procdesc.h
@@ -139,6 +139,7 @@
int pdgetpid(int, pid_t *);
int pdopenpid(pid_t, int);
int pdwait(int, int *, int, struct __wrusage *, struct __siginfo *);
+int pddupfd(int, int, int);
pid_t pdrfork_thread(int *, int, int, void *, int (*)(void *), void *);
__END_DECLS
diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h
--- a/sys/sys/syscall.h
+++ b/sys/sys/syscall.h
@@ -542,4 +542,5 @@
#define SYS_pdwait 601
#define SYS_renameat2 602
#define SYS_pdopenpid 603
-#define SYS_MAXSYSCALL 604
+#define SYS_pddupfd 604
+#define SYS_MAXSYSCALL 605
diff --git a/sys/sys/syscall.mk b/sys/sys/syscall.mk
--- a/sys/sys/syscall.mk
+++ b/sys/sys/syscall.mk
@@ -445,4 +445,5 @@
pdrfork.o \
pdwait.o \
renameat2.o \
- pdopenpid.o
+ pdopenpid.o \
+ pddupfd.o
diff --git a/sys/sys/sysproto.h b/sys/sys/sysproto.h
--- a/sys/sys/sysproto.h
+++ b/sys/sys/sysproto.h
@@ -1936,6 +1936,11 @@
char pid_l_[PADL_(pid_t)]; pid_t pid; char pid_r_[PADR_(pid_t)];
char flags_l_[PADL_(int)]; int flags; char flags_r_[PADR_(int)];
};
+struct pddupfd_args {
+ char pd_l_[PADL_(int)]; int pd; char pd_r_[PADR_(int)];
+ char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)];
+ char flags_l_[PADL_(int)]; int flags; char flags_r_[PADR_(int)];
+};
int sys__exit(struct thread *, struct _exit_args *);
int sys_fork(struct thread *, struct fork_args *);
int sys_read(struct thread *, struct read_args *);
@@ -2347,6 +2352,7 @@
int sys_pdwait(struct thread *, struct pdwait_args *);
int sys_renameat2(struct thread *, struct renameat2_args *);
int sys_pdopenpid(struct thread *, struct pdopenpid_args *);
+int sys_pddupfd(struct thread *, struct pddupfd_args *);
#ifdef COMPAT_43
@@ -3350,6 +3356,7 @@
#define SYS_AUE_pdwait AUE_PDWAIT
#define SYS_AUE_renameat2 AUE_RENAMEAT
#define SYS_AUE_pdopenpid AUE_PDOPENPID
+#define SYS_AUE_pddupfd AUE_NULL
#undef PAD_
#undef PADL_
diff --git a/tests/sys/kern/procdesc.c b/tests/sys/kern/procdesc.c
--- a/tests/sys/kern/procdesc.c
+++ b/tests/sys/kern/procdesc.c
@@ -27,16 +27,21 @@
*/
#include <sys/types.h>
+#include <sys/capsicum.h>
+#include <sys/resource.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/procdesc.h>
#include <sys/sysctl.h>
#include <sys/wait.h>
+#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <pthread.h>
+#include <signal.h>
#include <stdio.h>
+#include <string.h>
#include <unistd.h>
#include <atf-c.h>
@@ -205,11 +210,433 @@
ATF_REQUIRE_MSG(close(pd) == 0, "close: %s", strerror(errno));
}
+/* Tests for pdopenpid(2) */
+
+/*
+ * Basic: open a process descriptor for a child, verify pdgetpid returns the
+ * right pid.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_basic);
+ATF_TC_BODY(pdopenpid_basic, tc)
+{
+ pid_t child, queried;
+ int fd;
+
+ child = fork();
+ ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
+ if (child == 0) {
+ pause();
+ _exit(0);
+ }
+
+ fd = pdopenpid(child, 0);
+ ATF_REQUIRE_MSG(fd >= 0, "pdopenpid: %s", strerror(errno));
+
+ ATF_REQUIRE_MSG(pdgetpid(fd, &queried) == 0,
+ "pdgetpid: %s", strerror(errno));
+ ATF_REQUIRE_EQ(child, queried);
+
+ ATF_REQUIRE_MSG(pdkill(fd, SIGKILL) == 0,
+ "pdkill: %s", strerror(errno));
+ ATF_REQUIRE_EQ(child, waitpid(child, NULL, 0));
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * pdopenpid with PD_CLOEXEC should set the close-on-exec flag.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_cloexec);
+ATF_TC_BODY(pdopenpid_cloexec, tc)
+{
+ pid_t child;
+ int fd, flags;
+
+ child = fork();
+ ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
+ if (child == 0) {
+ for (;;)
+ pause();
+ _exit(0);
+ }
+
+ fd = pdopenpid(child, PD_CLOEXEC);
+ ATF_REQUIRE_MSG(fd >= 0, "pdopenpid: %s", strerror(errno));
+
+ flags = fcntl(fd, F_GETFD);
+ ATF_REQUIRE(flags >= 0);
+ ATF_REQUIRE(flags & FD_CLOEXEC);
+
+ ATF_REQUIRE_MSG(pdkill(fd, SIGKILL) == 0,
+ "pdkill: %s", strerror(errno));
+ ATF_REQUIRE_EQ(child, waitpid(child, NULL, 0));
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Invalid flags should return EINVAL.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_einval);
+ATF_TC_BODY(pdopenpid_einval, tc)
+{
+ ATF_REQUIRE_ERRNO(EINVAL, pdopenpid(getpid(), 0xdeadbeef) < 0);
+ ATF_REQUIRE_ERRNO(EINVAL, pdopenpid(getpid(), ~PD_ALLOWED_AT_FORK) < 0);
+}
+
+/*
+ * Validate handling of EMFILE.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_emfile);
+ATF_TC_BODY(pdopenpid_emfile, tc)
+{
+ pid_t child;
+ struct rlimit rl;
+ int fd;
+
+ child = fork();
+ ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
+ if (child == 0) {
+ pause();
+ _exit(0);
+ }
+
+ /*
+ * Determine the lowest unused fd, then set the fd limit to that
+ * value so that no new fds can be allocated.
+ */
+ fd = dup(STDIN_FILENO);
+ ATF_REQUIRE(fd >= 0);
+ ATF_REQUIRE(close(fd) == 0);
+
+ ATF_REQUIRE_EQ(getrlimit(RLIMIT_NOFILE, &rl), 0);
+ rl.rlim_cur = fd;
+ ATF_REQUIRE_EQ(setrlimit(RLIMIT_NOFILE, &rl), 0);
+
+ ATF_REQUIRE_ERRNO(EMFILE, pdopenpid(child, 0) < 0);
+
+ /*
+ * The child should not have been killed or reparented as a side
+ * effect of the failed syscall.
+ */
+ ATF_REQUIRE_MSG(kill(child, 0) == 0,
+ "child was killed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(waitpid(child, NULL, WNOHANG) == 0,
+ "child was reparented");
+
+ ATF_REQUIRE_MSG(kill(child, SIGKILL) == 0,
+ "kill: %s", strerror(errno));
+ ATF_REQUIRE_EQ(child, waitpid(child, NULL, 0));
+}
+
+/*
+ * Opening a nonexistent pid should return ESRCH.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_esrch);
+ATF_TC_BODY(pdopenpid_esrch, tc)
+{
+ ATF_REQUIRE_ERRNO(ESRCH, pdopenpid(123456789, 0) < 0);
+}
+
+/*
+ * pdopenpid should fail in capability mode.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_capmode);
+ATF_TC_BODY(pdopenpid_capmode, tc)
+{
+ pid_t child, parent;
+
+ parent = getpid();
+ child = fork();
+ ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
+ if (child == 0) {
+ while (getppid() == parent)
+ sleep(1);
+ _exit(0);
+ }
+
+ ATF_REQUIRE_MSG(cap_enter() == 0, "cap_enter: %s", strerror(errno));
+ ATF_REQUIRE_ERRNO(ECAPMODE, pdopenpid(child, 0) < 0);
+}
+
+/*
+ * Open a process descriptor for a child that already has one from pdfork.
+ * Both fds should refer to the same process.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_pdfork_then_open);
+ATF_TC_BODY(pdopenpid_pdfork_then_open, tc)
+{
+ pid_t child, pid1, pid2;
+ int fd1, fd2;
+
+ child = pdfork(&fd1, PD_DAEMON);
+ ATF_REQUIRE_MSG(child >= 0, "pdfork: %s", strerror(errno));
+ if (child == 0) {
+ for (;;)
+ pause();
+ _exit(0);
+ }
+
+ fd2 = pdopenpid(child, 0);
+ ATF_REQUIRE_MSG(fd2 >= 0, "pdopenpid: %s", strerror(errno));
+
+ ATF_REQUIRE(pdgetpid(fd1, &pid1) == 0);
+ ATF_REQUIRE(pdgetpid(fd2, &pid2) == 0);
+ ATF_REQUIRE_EQ(pid1, pid2);
+ ATF_REQUIRE_EQ(child, pid1);
+
+ /* Kill via the second fd, wait via the first. */
+ ATF_REQUIRE_MSG(pdkill(fd2, SIGKILL) == 0,
+ "pdkill: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pdwait(fd1, NULL, WEXITED, NULL, NULL) == 0,
+ "pdwait: %s", strerror(errno));
+
+ ATF_REQUIRE(close(fd1) == 0);
+ ATF_REQUIRE(close(fd2) == 0);
+}
+
+/*
+ * Open a process descriptor for a child created with fork(), which has no
+ * pre-existing procdesc. Then use pdkill() and pdwait() on it.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_fork_then_open);
+ATF_TC_BODY(pdopenpid_fork_then_open, tc)
+{
+ pid_t child;
+ int fd, status;
+
+ child = fork();
+ ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
+ if (child == 0) {
+ for (;;)
+ pause();
+ _exit(0);
+ }
+
+ fd = pdopenpid(child, 0);
+ ATF_REQUIRE_MSG(fd >= 0, "pdopenpid: %s", strerror(errno));
+
+ ATF_REQUIRE_MSG(pdkill(fd, SIGKILL) == 0,
+ "pdkill: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pdwait(fd, &status, WEXITED, NULL, NULL) == 0,
+ "pdwait: %s", strerror(errno));
+ ATF_REQUIRE(WIFSIGNALED(status));
+ ATF_REQUIRE_EQ(WTERMSIG(status), SIGKILL);
+
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Closing one fd should not kill the process when another fd still references
+ * the same procdesc.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_close_one_of_two);
+ATF_TC_BODY(pdopenpid_close_one_of_two, tc)
+{
+ pid_t child, queried;
+ int fd1, fd2, status;
+
+ child = pdfork(&fd1, PD_DAEMON);
+ ATF_REQUIRE_MSG(child >= 0, "pdfork: %s", strerror(errno));
+ if (child == 0) {
+ for (;;)
+ pause();
+ _exit(0);
+ }
+
+ fd2 = pdopenpid(child, 0);
+ ATF_REQUIRE_MSG(fd2 >= 0, "pdopenpid: %s", strerror(errno));
+
+ /* Close the first fd; the process should remain alive. */
+ ATF_REQUIRE(close(fd1) == 0);
+
+ /*
+ * Verify the process is still reachable via the second fd and still
+ * alive.
+ */
+ ATF_REQUIRE_EQ(0, pdgetpid(fd2, &queried));
+ ATF_REQUIRE_EQ(child, queried);
+ ATF_REQUIRE_MSG(pdkill(fd2, 0) == 0,
+ "pdkill(0) after closing first fd: %s", strerror(errno));
+
+ /* Now kill and reap via the second fd. */
+ ATF_REQUIRE_MSG(pdkill(fd2, SIGKILL) == 0,
+ "pdkill: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pdwait(fd2, &status, WEXITED, NULL, NULL) == 0,
+ "pdwait: %s", strerror(errno));
+ ATF_REQUIRE(WIFSIGNALED(status));
+ ATF_REQUIRE_EQ(WTERMSIG(status), SIGKILL);
+
+ ATF_REQUIRE(close(fd2) == 0);
+}
+
+/*
+ * Two calls to pdopenpid for the same process (no pdfork) should both work.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_open_twice);
+ATF_TC_BODY(pdopenpid_open_twice, tc)
+{
+ pid_t child, pid1, pid2;
+ int fd1, fd2;
+
+ child = fork();
+ ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
+ if (child == 0) {
+ for (;;)
+ pause();
+ _exit(0);
+ }
+
+ fd1 = pdopenpid(child, 0);
+ ATF_REQUIRE_MSG(fd1 >= 0, "pdopenpid(1): %s", strerror(errno));
+
+ fd2 = pdopenpid(child, 0);
+ ATF_REQUIRE_MSG(fd2 >= 0, "pdopenpid(2): %s", strerror(errno));
+
+ ATF_REQUIRE_EQ(0, pdgetpid(fd1, &pid1));
+ ATF_REQUIRE_EQ(0, pdgetpid(fd2, &pid2));
+ ATF_REQUIRE_EQ(pid1, pid2);
+
+ /* Close the first, process should survive. */
+ ATF_REQUIRE(close(fd1) == 0);
+ ATF_REQUIRE_MSG(pdkill(fd2, 0) == 0,
+ "pdkill(0) after closing first fd: %s", strerror(errno));
+
+ ATF_REQUIRE_MSG(pdkill(fd2, SIGKILL) == 0,
+ "pdkill: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pdwait(fd2, NULL, WEXITED, NULL, NULL) == 0,
+ "pdwait: %s", strerror(errno));
+ ATF_REQUIRE(close(fd2) == 0);
+}
+
+/*
+ * When a process with two procdesc fds exits, only one pdwait should succeed
+ * in collecting the exit status. The other should get ESRCH.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_pdwait_only_one);
+ATF_TC_BODY(pdopenpid_pdwait_only_one, tc)
+{
+ pid_t child;
+ int fd1, fd2, status;
+
+ child = pdfork(&fd1, PD_DAEMON);
+ ATF_REQUIRE_MSG(child >= 0, "pdfork: %s", strerror(errno));
+ if (child == 0)
+ _exit(42);
+
+ fd2 = pdopenpid(child, 0);
+ ATF_REQUIRE_MSG(fd2 >= 0, "pdopenpid: %s", strerror(errno));
+
+ /* Collect via the first fd. */
+ ATF_REQUIRE_MSG(pdwait(fd1, &status, WEXITED, NULL, NULL) == 0,
+ "pdwait(fd1): %s", strerror(errno));
+ ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 42);
+
+ /* The second fd should no longer be able to collect. */
+ ATF_REQUIRE_ERRNO(ESRCH, pdwait(fd2, &status, WEXITED, NULL, NULL) < 0);
+
+ ATF_REQUIRE(close(fd1) == 0);
+ ATF_REQUIRE(close(fd2) == 0);
+}
+
+/*
+ * Opening a process descriptor for ourselves should work.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_self);
+ATF_TC_BODY(pdopenpid_self, tc)
+{
+ pid_t child, queried;
+ int childfd, fd, status;
+
+ /*
+ * Closing self-referencing procdesc would steal the exit
+ * status from the parent. Keep one more procdesc for the
+ * child in the parent so that we can query the state.
+ *
+ * For the same reason we must use fork() and test in the
+ * child, otherwise kyua does not get the SIGCHILD nor the
+ * exit status.
+ */
+ child = pdfork(&childfd, 0);
+ if (child == 0) {
+ fd = pdopenpid(getpid(), PD_DAEMON);
+ if (fd < 0)
+ _exit(21);
+ if (pdgetpid(fd, &queried) == -1)
+ _exit(22);
+ if (getpid() != queried)
+ _exit(23);
+ if (close(fd) != 0)
+ _exit(24);
+ _exit(0);
+ }
+
+ ATF_REQUIRE(pdgetpid(childfd, &queried) == 0);
+ ATF_REQUIRE_EQ(queried, child);
+ ATF_REQUIRE(pdwait(childfd, &status, WEXITED, NULL, NULL) == 0);
+
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
+ ATF_REQUIRE(close(childfd) == 0);
+}
+
+/*
+ * pdopenpid for a process that is exiting (P_WEXIT set) should fail with EBUSY.
+ */
+ATF_TC_WITHOUT_HEAD(pdopenpid_exiting);
+ATF_TC_BODY(pdopenpid_exiting, tc)
+{
+ pid_t child;
+ int fd, pip[2], status;
+
+ ATF_REQUIRE_EQ(pipe(pip), 0);
+
+ child = fork();
+ ATF_REQUIRE_MSG(child >= 0, "fork: %s", strerror(errno));
+ if (child == 0) {
+ char c;
+
+ close(pip[1]);
+ (void)read(pip[0], &c, 1);
+ _exit(0);
+ }
+ ATF_REQUIRE(close(pip[0]) == 0);
+
+ /* Tell the child to exit. */
+ ATF_REQUIRE(close(pip[1]) == 0);
+
+ /* Might still race. */
+ usleep(1000);
+ fd = pdopenpid(child, 0);
+ if (fd >= 0) {
+ ATF_REQUIRE(close(fd) == 0);
+ } else {
+ ATF_REQUIRE_MSG(errno == EBUSY,
+ "unexpected errno %d (%s)", errno, strerror(errno));
+ }
+
+ ATF_REQUIRE(waitpid(child, &status, 0) == child);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, pid_recycle);
ATF_TP_ADD_TC(tp, poll_close_race);
ATF_TP_ADD_TC(tp, poll_exit_wakeup);
+ ATF_TP_ADD_TC(tp, pdopenpid_basic);
+ ATF_TP_ADD_TC(tp, pdopenpid_cloexec);
+ ATF_TP_ADD_TC(tp, pdopenpid_einval);
+ ATF_TP_ADD_TC(tp, pdopenpid_emfile);
+ ATF_TP_ADD_TC(tp, pdopenpid_esrch);
+ ATF_TP_ADD_TC(tp, pdopenpid_capmode);
+ ATF_TP_ADD_TC(tp, pdopenpid_pdfork_then_open);
+ ATF_TP_ADD_TC(tp, pdopenpid_fork_then_open);
+ ATF_TP_ADD_TC(tp, pdopenpid_close_one_of_two);
+ ATF_TP_ADD_TC(tp, pdopenpid_open_twice);
+ ATF_TP_ADD_TC(tp, pdopenpid_pdwait_only_one);
+ ATF_TP_ADD_TC(tp, pdopenpid_self);
+ ATF_TP_ADD_TC(tp, pdopenpid_exiting);
+
return (atf_no_error());
}

File Metadata

Mime Type
text/plain
Expires
Tue, Jul 14, 1:34 AM (18 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35051454
Default Alt Text
D57163.id181408.diff (30 KB)

Event Timeline