Page MenuHomeFreeBSD

D58694.id183539.diff
No OneTemporary

D58694.id183539.diff

diff --git a/lib/libsys/procctl.2 b/lib/libsys/procctl.2
--- a/lib/libsys/procctl.2
+++ b/lib/libsys/procctl.2
@@ -27,7 +27,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd April 21, 2025
+.Dd August 6, 2026
.Dt PROCCTL 2
.Os
.Sh NAME
@@ -65,8 +65,22 @@
.It Dv P_PGID
Control processes belonging to the process group with the ID
.Fa id .
+.It Dv P_PROCDESC
+Control the process referenced by the process descriptor
+.Fa id .
+The descriptor is required to have the
+.Dv CAP_PROCCTL
+right.
.El
.Pp
+Unlike the other identifier types, which name processes by numbers in a
+global namespace,
+.Dv P_PROCDESC
+names a process by a descriptor the caller already holds.
+It is therefore the only identifier type accepted in capability mode;
+see
+.Xr cap_enter 2 .
+.Pp
The control request to perform is specified by the
.Fa cmd
argument.
@@ -857,11 +871,39 @@
.It Bq Er EPERM
The calling process does not have permission to perform the requested
operation on any of the selected processes.
+.It Bq Er ECAPMODE
+The calling process is in capability mode and the
+.Fa idtype
+argument is not
+.Dv P_PROCDESC .
+.It Bq Er EBADF
+The
+.Fa idtype
+argument is
+.Dv P_PROCDESC
+and
+.Fa id
+is not a valid descriptor, or is a descriptor of a type other than a
+process descriptor.
+.It Bq Er ENOTCAPABLE
+The
+.Fa idtype
+argument is
+.Dv P_PROCDESC
+and
+.Fa id
+lacks the
+.Dv CAP_PROCCTL
+right.
.It Bq Er ESRCH
No processes matched the requested
.Fa idtype
and
.Fa id .
+A
+.Dv P_PROCDESC
+descriptor also stops matching once the process it referenced has exited and
+been reaped.
.It Bq Er ESRCH
No descendant processes can be found matching criteria specified in the
.Dv PROC_REAP_KILL
@@ -944,6 +986,7 @@
.Xr ktrace 2 ,
.Xr mmap 2 ,
.Xr mprotect 2 ,
+.Xr pdfork 2 ,
.Xr ptrace 2 ,
.Xr wait 2 ,
.Xr capsicum 4 ,
diff --git a/share/man/man4/rights.4 b/share/man/man4/rights.4
--- a/share/man/man4/rights.4
+++ b/share/man/man4/rights.4
@@ -30,7 +30,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd May 22, 2025
+.Dd August 6, 2026
.Dt RIGHTS 4
.Os
.Sh NAME
@@ -490,6 +490,13 @@
.It Dv CAP_PEELOFF
Permit
.Xr sctp_peeloff 2 .
+.It Dv CAP_PROCCTL
+Permit
+.Xr procctl 2
+with an
+.Fa idtype
+of
+.Dv P_PROCDESC .
.It Dv CAP_PREAD
An alias to
.Dv CAP_READ
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
@@ -611,7 +611,7 @@
{ .sy_narg = AS(accept4_args), .sy_call = (sy_call_t *)sys_accept4, .sy_auevent = AUE_ACCEPT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 541 = accept4 */
{ .sy_narg = AS(pipe2_args), .sy_call = (sy_call_t *)sys_pipe2, .sy_auevent = AUE_PIPE, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 542 = pipe2 */
{ .sy_narg = AS(freebsd32_aio_mlock_args), .sy_call = (sy_call_t *)freebsd32_aio_mlock, .sy_auevent = AUE_AIO_MLOCK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 543 = freebsd32_aio_mlock */
- { .sy_narg = AS(freebsd32_procctl_args), .sy_call = (sy_call_t *)freebsd32_procctl, .sy_auevent = AUE_PROCCTL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 544 = freebsd32_procctl */
+ { .sy_narg = AS(freebsd32_procctl_args), .sy_call = (sy_call_t *)freebsd32_procctl, .sy_auevent = AUE_PROCCTL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 544 = freebsd32_procctl */
{ .sy_narg = AS(freebsd32_ppoll_args), .sy_call = (sy_call_t *)freebsd32_ppoll, .sy_auevent = AUE_POLL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 545 = freebsd32_ppoll */
{ .sy_narg = AS(freebsd32_futimens_args), .sy_call = (sy_call_t *)freebsd32_futimens, .sy_auevent = AUE_FUTIMES, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 546 = freebsd32_futimens */
{ .sy_narg = AS(freebsd32_utimensat_args), .sy_call = (sy_call_t *)freebsd32_utimensat, .sy_auevent = AUE_FUTIMESAT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 547 = freebsd32_utimensat */
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
@@ -610,7 +610,7 @@
{ .sy_narg = AS(accept4_args), .sy_call = (sy_call_t *)sys_accept4, .sy_auevent = AUE_ACCEPT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 541 = accept4 */
{ .sy_narg = AS(pipe2_args), .sy_call = (sy_call_t *)sys_pipe2, .sy_auevent = AUE_PIPE, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 542 = pipe2 */
{ .sy_narg = AS(aio_mlock_args), .sy_call = (sy_call_t *)sys_aio_mlock, .sy_auevent = AUE_AIO_MLOCK, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 543 = aio_mlock */
- { .sy_narg = AS(procctl_args), .sy_call = (sy_call_t *)sys_procctl, .sy_auevent = AUE_PROCCTL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 544 = procctl */
+ { .sy_narg = AS(procctl_args), .sy_call = (sy_call_t *)sys_procctl, .sy_auevent = AUE_PROCCTL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 544 = procctl */
{ .sy_narg = AS(ppoll_args), .sy_call = (sy_call_t *)sys_ppoll, .sy_auevent = AUE_POLL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 545 = ppoll */
{ .sy_narg = AS(futimens_args), .sy_call = (sy_call_t *)sys_futimens, .sy_auevent = AUE_FUTIMES, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 546 = futimens */
{ .sy_narg = AS(utimensat_args), .sy_call = (sy_call_t *)sys_utimensat, .sy_auevent = AUE_FUTIMESAT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 547 = utimensat */
diff --git a/sys/kern/kern_procctl.c b/sys/kern/kern_procctl.c
--- a/sys/kern/kern_procctl.c
+++ b/sys/kern/kern_procctl.c
@@ -33,6 +33,9 @@
#include <sys/_unrhdr.h>
#include <sys/systm.h>
#include <sys/capsicum.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/procdesc.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mman.h>
@@ -1180,6 +1183,14 @@
const struct procctl_cmd_info *cmd_info;
int error, error1;
+ /*
+ * In capability mode only P_PROCDESC is available: the other identifier
+ * types name a process through a global namespace the sandbox is not
+ * supposed to be able to reach into.
+ */
+ if (IN_CAPABILITY_MODE(td) && uap->idtype != P_PROCDESC)
+ return (ECAPMODE);
+
if (uap->com >= PROC_PROCCTL_MD_MIN)
return (cpu_procctl(td, uap->idtype, uap->id,
uap->com, uap->data));
@@ -1221,6 +1232,29 @@
return (error);
}
+/*
+ * Common tail for the idtypes that name exactly one process: check that the
+ * caller may act on it, then run the command. Called with the process
+ * locked, and returns it unlocked.
+ *
+ * Callers acting on the calling process itself need no special case: both
+ * p_cansee() and p_candebug() return success immediately when the target is
+ * the caller's own process.
+ */
+static int
+kern_procctl_found(struct thread *td, struct proc *p,
+ const struct procctl_cmd_info *cmd_info, int com, void *data)
+{
+ int error;
+
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+ error = cmd_info->need_candebug ? p_candebug(td, p) : p_cansee(td, p);
+ if (error == 0)
+ error = kern_procctl_single(td, p, com, data);
+ PROC_UNLOCK(p);
+ return (error);
+}
+
int
kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
{
@@ -1232,7 +1266,8 @@
MPASS(com > 0 && com < nitems(procctl_cmds_info));
cmd_info = &procctl_cmds_info[com];
- if (idtype != P_PID && cmd_info->one_proc)
+ /* P_PROCDESC, like P_PID, names exactly one process. */
+ if (idtype != P_PID && idtype != P_PROCDESC && cmd_info->one_proc)
return (EINVAL);
sapblk = false;
@@ -1257,7 +1292,6 @@
case P_PID:
if (id == 0) {
p = td->td_proc;
- error = 0;
PROC_LOCK(p);
} else {
p = pfind(id);
@@ -1266,13 +1300,41 @@
EINVAL : ESRCH;
break;
}
- error = cmd_info->need_candebug ? p_candebug(td, p) :
- p_cansee(td, p);
+ }
+ error = kern_procctl_found(td, p, cmd_info, com, data);
+ break;
+ case P_PROCDESC: {
+ /*
+ * Name the process by a descriptor rather than a pid, so that
+ * the operation stays available in capability mode.
+ *
+ * fget_procdesc() requires proctree_lock, which the command
+ * may already have taken, and returns the process locked. It
+ * hands back a reference on the file -- on some error paths as
+ * well as on success -- and that reference is what keeps the
+ * process from going away, so it is dropped only once the
+ * command has run. proctree_lock, in contrast, is needed only
+ * for the lookup itself.
+ */
+ struct file *fp;
+
+ {
+ bool slock;
+
+ slock = cmd_info->lock_tree == PCTL_UNLOCKED;
+ if (slock)
+ sx_slock(&proctree_lock);
+ error = fget_procdesc(td, (int)id,
+ &cap_procctl_rights, EBADF, &fp, NULL, &p);
+ if (slock)
+ sx_sunlock(&proctree_lock);
}
if (error == 0)
- error = kern_procctl_single(td, p, com, data);
- PROC_UNLOCK(p);
+ error = kern_procctl_found(td, p, cmd_info, com, data);
+ if (fp != NULL)
+ fdrop(fp, td);
break;
+ }
case P_PGID:
/*
* Attempt to apply the operation to all members of the
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
@@ -93,6 +93,7 @@
const cap_rights_t cap_pdkill_rights = CAP_RIGHTS_INITIALIZER(CAP_PDKILL);
const cap_rights_t cap_pdwait_rights = CAP_RIGHTS_INITIALIZER(CAP_PDWAIT);
const cap_rights_t cap_pread_rights = CAP_RIGHTS_INITIALIZER(CAP_PREAD);
+const cap_rights_t cap_procctl_rights = CAP_RIGHTS_INITIALIZER(CAP_PROCCTL);
const cap_rights_t cap_ptrace_rights = CAP_RIGHTS_INITIALIZER(CAP_PTRACE);
const cap_rights_t cap_pwrite_rights = CAP_RIGHTS_INITIALIZER(CAP_PWRITE);
const cap_rights_t cap_read_rights = CAP_RIGHTS_INITIALIZER(CAP_READ);
diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master
--- a/sys/kern/syscalls.master
+++ b/sys/kern/syscalls.master
@@ -3020,7 +3020,7 @@
_In_ _Contains_long_ptr_ struct aiocb *aiocbp
);
}
-544 AUE_PROCCTL STD {
+544 AUE_PROCCTL STD|CAPENABLED {
int procctl(
idtype_t idtype,
id_t id,
diff --git a/sys/sys/caprights.h b/sys/sys/caprights.h
--- a/sys/sys/caprights.h
+++ b/sys/sys/caprights.h
@@ -95,6 +95,7 @@
extern const cap_rights_t cap_pdkill_rights;
extern const cap_rights_t cap_pdwait_rights;
extern const cap_rights_t cap_pread_rights;
+extern const cap_rights_t cap_procctl_rights;
extern const cap_rights_t cap_ptrace_rights;
extern const cap_rights_t cap_pwrite_rights;
extern const cap_rights_t cap_read_rights;
diff --git a/sys/sys/capsicum.h b/sys/sys/capsicum.h
--- a/sys/sys/capsicum.h
+++ b/sys/sys/capsicum.h
@@ -301,8 +301,9 @@
/* Allows ptrace(PT_PROCDESC) */
#define CAP_PTRACE CAPRIGHT(1, 0x0000000001000000ULL)
+/* Allows procctl(P_PROCDESC) */
+#define CAP_PROCCTL CAPRIGHT(1, 0x0000000002000000ULL)
-#define CAP_UNUSED1_26 CAPRIGHT(1, 0x0000000002000000ULL)
#define CAP_UNUSED1_27 CAPRIGHT(1, 0x0000000004000000ULL)
#define CAP_UNUSED1_28 CAPRIGHT(1, 0x0000000008000000ULL)
#define CAP_UNUSED1_29 CAPRIGHT(1, 0x0000000010000000ULL)
@@ -336,7 +337,7 @@
#define CAP_UNUSED1_57 CAPRIGHT(1, 0x0100000000000000ULL)
/* All default bits for index 1. */
-#define CAP_ALL1 CAPRIGHT(1, 0x0000000001FFFFFFULL)
+#define CAP_ALL1 CAPRIGHT(1, 0x0000000003FFFFFFULL)
/* Backward compatibility. */
#define CAP_POLL_EVENT CAP_EVENT
diff --git a/sys/sys/wait.h b/sys/sys/wait.h
--- a/sys/sys/wait.h
+++ b/sys/sys/wait.h
@@ -118,7 +118,14 @@
P_JAILID, /* A zone identifier. */
P_CTID, /* A (process) contract identifier. */
P_CPUID, /* CPU identifier. */
- P_PSETID /* Processor set identifier. */
+ P_PSETID, /* Processor set identifier. */
+ /*
+ * A process descriptor. Unlike the identifiers above this names a
+ * process by a descriptor the caller holds rather than by a number
+ * in a global namespace, so it remains usable in capability mode.
+ * FreeBSD extension; beyond the range shared with Solaris.
+ */
+ P_PROCDESC
} idtype_t; /* The type of id_t we are using. */
#if __BSD_VISIBLE

File Metadata

Mime Type
text/plain
Expires
Fri, Aug 28, 11:03 PM (9 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
37487059
Default Alt Text
D58694.id183539.diff (11 KB)

Event Timeline