Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F162416438
D57163.id181408.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
30 KB
Referenced Files
None
Subscribers
None
D57163.id181408.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D57163: pddupfd(2)
Attached
Detach File
Event Timeline
Log In to Comment