git: 0303cc4be853 - stable/13 - Extract proc_get_binpath() from sysctl_kern_proc_pathname()

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Sat, 06 Nov 2021 02:25:24 UTC
The branch stable/13 has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=0303cc4be8538ae16c577cdeda30823ada76f802

commit 0303cc4be8538ae16c577cdeda30823ada76f802
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2021-10-29 01:42:59 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2021-11-06 02:12:32 +0000

    Extract proc_get_binpath() from sysctl_kern_proc_pathname()
    
    (cherry picked from commit f34fc6ba06a10e0f2a505ec0dd2f2fab2a79e53d)
---
 sys/kern/kern_proc.c | 89 +++++++++++++++++++++++++++++++++-------------------
 sys/sys/proc.h       |  2 ++
 2 files changed, 58 insertions(+), 33 deletions(-)

diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c
index c4c01da1faea..2156c5c465ba 100644
--- a/sys/kern/kern_proc.c
+++ b/sys/kern/kern_proc.c
@@ -2226,41 +2226,34 @@ sysctl_kern_proc_auxv(SYSCTL_HANDLER_ARGS)
 }
 
 /*
- * This sysctl allows a process to retrieve the path of the executable for
- * itself or another process.
+ * Look up the canonical executable path running in the specified process.
+ * It tries to return the same hardlink name as was used for execve(2).
+ * This allows the programs that modify their behavior based on their progname,
+ * to operate correctly.
+ *
+ * Result is returned in retbuf, it must not be freed, similar to vn_fullpath()
+ *   calling conventions.
+ * binname is a pointer to temporary string buffer of length MAXPATHLEN,
+ *   allocated and freed by caller.
+ * freebuf should be freed by caller, from the M_TEMP malloc type.
  */
-static int
-sysctl_kern_proc_pathname(SYSCTL_HANDLER_ARGS)
+int
+proc_get_binpath(struct proc *p, char *binname, char **retbuf,
+    char **freebuf)
 {
-	pid_t *pidp = (pid_t *)arg1;
-	unsigned int arglen = arg2;
-	struct proc *p;
-	struct vnode *vp, *dvp;
-	char *retbuf, *freebuf, *binname;
 	struct nameidata nd;
+	struct vnode *vp, *dvp;
 	size_t freepath_size;
 	int error;
 	bool do_fullpath;
 
-	if (arglen != 1)
-		return (EINVAL);
-	binname = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
-	binname[0] = '\0';
-	if (*pidp == -1) {	/* -1 means this process */
-		p = req->td->td_proc;
-	} else {
-		error = pget(*pidp, PGET_CANSEE, &p);
-		if (error != 0) {
-			free(binname, M_TEMP);
-			return (error);
-		}
-	}
+	PROC_LOCK_ASSERT(p, MA_OWNED);
 
 	vp = p->p_textvp;
 	if (vp == NULL) {
-		if (*pidp != -1)
-			PROC_UNLOCK(p);
-		free(binname, M_TEMP);
+		PROC_UNLOCK(p);
+		*retbuf = NULL;
+		*freebuf = NULL;
 		return (0);
 	}
 	vref(vp);
@@ -2269,20 +2262,20 @@ sysctl_kern_proc_pathname(SYSCTL_HANDLER_ARGS)
 		vref(dvp);
 	if (p->p_binname != NULL)
 		strlcpy(binname, p->p_binname, MAXPATHLEN);
-	if (*pidp != -1)
-		PROC_UNLOCK(p);
+	PROC_UNLOCK(p);
+
 	do_fullpath = true;
-	freebuf = NULL;
+	*freebuf = NULL;
 	if (dvp != NULL && binname[0] != '\0') {
 		freepath_size = MAXPATHLEN;
 		if (vn_fullpath_hardlink(vp, dvp, binname, strlen(binname),
-		    &retbuf, &freebuf, &freepath_size) == 0) {
+		    retbuf, freebuf, &freepath_size) == 0) {
 			/*
 			 * Recheck the looked up path.  The binary
 			 * might have been renamed or replaced, in
 			 * which case we should not report old name.
 			 */
-			NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, retbuf,
+			NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, *retbuf,
 			    req->td);
 			error = namei(&nd);
 			if (error == 0) {
@@ -2294,13 +2287,43 @@ sysctl_kern_proc_pathname(SYSCTL_HANDLER_ARGS)
 		}
 	}
 	if (do_fullpath) {
-		free(freebuf, M_TEMP);
-		freebuf = NULL;
-		error = vn_fullpath(vp, &retbuf, &freebuf);
+		free(*freebuf, M_TEMP);
+		*freebuf = NULL;
+		error = vn_fullpath(vp, retbuf, freebuf);
 	}
 	vrele(vp);
 	if (dvp != NULL)
 		vrele(dvp);
+	return (error);
+}
+
+/*
+ * This sysctl allows a process to retrieve the path of the executable for
+ * itself or another process.
+ */
+static int
+sysctl_kern_proc_pathname(SYSCTL_HANDLER_ARGS)
+{
+	pid_t *pidp = (pid_t *)arg1;
+	unsigned int arglen = arg2;
+	struct proc *p;
+	char *retbuf, *freebuf, *binname;
+	int error;
+
+	if (arglen != 1)
+		return (EINVAL);
+	binname = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
+	binname[0] = '\0';
+	if (*pidp == -1) {	/* -1 means this process */
+		error = 0;
+		p = req->td->td_proc;
+		PROC_LOCK(p);
+	} else {
+		error = pget(*pidp, PGET_CANSEE, &p);
+	}
+
+	if (error == 0)
+		error = proc_get_binpath(p, binname, &retbuf, &freebuf);
 	free(binname, M_TEMP);
 	if (error != 0)
 		return (error);
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index a09a006b836b..96bd2cc8a6ce 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -1115,6 +1115,8 @@ struct	pargs *pargs_alloc(int len);
 void	pargs_drop(struct pargs *pa);
 void	pargs_hold(struct pargs *pa);
 void	proc_add_orphan(struct proc *child, struct proc *parent);
+int	proc_get_binpath(struct proc *p, char *binname, char **fullpath,
+	    char **freepath);
 int	proc_getargv(struct thread *td, struct proc *p, struct sbuf *sb);
 int	proc_getauxv(struct thread *td, struct proc *p, struct sbuf *sb);
 int	proc_getenvv(struct thread *td, struct proc *p, struct sbuf *sb);