Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 29 Jan 2012 21:08:24 +0000 (UTC)
From:      Mikolaj Golub <trociny@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org
Subject:   svn commit: r230754 - in stable/9/sys: compat/linprocfs fs/procfs kern sys
Message-ID:  <201201292108.q0TL8O61052689@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: trociny
Date: Sun Jan 29 21:08:24 2012
New Revision: 230754
URL: http://svn.freebsd.org/changeset/base/230754

Log:
  MFC r227833, r227834, r227836, r227874, r227955, r228029, r228030, r228046,
   r228264, r228288, r228302, r228648, r228666, r230145, r230470, r230550:
  
  New kern.proc sysctls.
  
  r227833, r227874:
  
  Add new sysctls, KERN_PROC_ENV and KERN_PROC_AUXV, to return
  environment strings and ELF auxiliary vectors from a process stack.
  
  Make sysctl_kern_proc_args to read not cached arguments from the
  process stack.
  
  Export proc_getargv() and proc_getenvv() so they can be reused by
  procfs and linprocfs.
  
  Suggested by:	kib
  Reviewed by:	kib
  Discussed with:	kib, rwatson, jilles
  Tested by:	pho
  
  MFC r227834:
  
  In procfs_doproccmdline() if arguments are not cashed read them from
  the process stack.
  
  Suggested by:	kib
  Reviewed by:	kib
  Tested by:	pho
  
  MFC r227836:
  
  Retire linprocfs_doargv(). Instead use new functions, proc_getargv()
  and proc_getenvv(), which were implemented using linprocfs_doargv() as
  a reference.
  
  Suggested by:	kib
  Reviewed by:	kib
  Approved by:	des (linprocfs maintainer)
  
  r227955:
  
  Add sysctl to get process resource limits.
  
  Reviewed by:	kib
  
  r228029:
  
  In sysctl_kern_proc_auxv the process was released too early: we still
  need to hold it when checking process sv_flags.
  
  r228030, r228046:
  
  Add sysctl to retrieve ps_strings structure location of another process.
  
  Suggested by:	kib
  Reviewed by:	kib
  
  r228264:
  
  In sysctl_kern_proc_ps_strings() there is no much sense in checking
  for P_WEXIT and P_SYSTEM flags.
  
  Reviewed by:	kib
  
  r228288, r228302:
  
  Protect kern.proc.auxv and kern.proc.ps_strings sysctls with p_candebug().
  
  Citing jilles:
  
  If we are ever going to do ASLR, the AUXV information tells an attacker
  where the stack, executable and RTLD are located, which defeats much of
  the point of randomizing the addresses in the first place.
  
  Given that the AUXV information seems to be used by debuggers only anyway,
  I think it would be good to move it to p_candebug() now.
  
  The full virtual memory maps (KERN_PROC_VMMAP, procstat -v) are already
  under p_candebug().
  
  Suggested by:	jilles
  Discussed with:	rwatson
  
  r228648:
  
  On start most of sysctl_kern_proc functions use the same pattern:
  locate a process calling pfind() and do some additional checks like
  p_candebug(). To reduce this code duplication a new function pget() is
  introduced and used.
  
  As the function may be useful not only in kern_proc.c it is in the
  kernel name space.
  
  Suggested by:	kib
  Reviewed by:	kib
  
  r228666:
  
  Fix style and white spaces.
  
  MFC r230145:
  
  Abrogate nchr argument in proc_getargv() and proc_getenvv(): we always want
  to read strings completely to know the actual size.
  
  As a side effect it fixes the issue with kern.proc.args and kern.proc.env
  sysctls, which didn't return the size of available data when calling
  sysctl(3) with the NULL argument for oldp.
  
  Note, in get_ps_strings(), which does actual work for proc_getargv() and
  proc_getenvv(), we still have a safety limit on the size of data read in
  case of a corrupted procces stack.
  
  Suggested by:	kib
  
  r230470:
  
  Change kern.proc.rlimit sysctl to:
  
  - retrive only one, specified limit for a process, not the whole
    array, as it was previously (the sysctl has been added recently and
    has not been backported to stable yet, so this change is ok);
  
  - allow to set a resource limit for another process.
  
  Submitted by:	Andrey Zonov <andrey at zonov.org>
  Discussed with:	kib
  Reviewed by:	kib
  
  r230550:
  
  Fix CTL flags in the declarations of KERN_PROC_ENV, AUXV and
  PS_STRINGS sysctls: they are read only.

Modified:
  stable/9/sys/compat/linprocfs/linprocfs.c
  stable/9/sys/fs/procfs/procfs_status.c
  stable/9/sys/kern/kern_proc.c
  stable/9/sys/kern/kern_resource.c
  stable/9/sys/sys/proc.h
  stable/9/sys/sys/resourcevar.h
  stable/9/sys/sys/sysctl.h
Directory Properties:
  stable/9/sys/   (props changed)

Modified: stable/9/sys/compat/linprocfs/linprocfs.c
==============================================================================
--- stable/9/sys/compat/linprocfs/linprocfs.c	Sun Jan 29 20:39:42 2012	(r230753)
+++ stable/9/sys/compat/linprocfs/linprocfs.c	Sun Jan 29 21:08:24 2012	(r230754)
@@ -919,150 +919,6 @@ linprocfs_doprocroot(PFS_FILL_ARGS)
 	return (0);
 }
 
-#define MAX_ARGV_STR	512	/* Max number of argv-like strings */
-#define UIO_CHUNK_SZ	256	/* Max chunk size (bytes) for uiomove */
-
-static int
-linprocfs_doargv(struct thread *td, struct proc *p, struct sbuf *sb,
-    void (*resolver)(const struct ps_strings, u_long *, int *))
-{
-	struct iovec iov;
-	struct uio tmp_uio;
-	struct ps_strings pss;
-	int ret, i, n_elements, elm_len;
-	u_long addr, pbegin;
-	char **env_vector, *envp;
-	char env_string[UIO_CHUNK_SZ];
-#ifdef COMPAT_FREEBSD32
-	struct freebsd32_ps_strings pss32;
-	uint32_t *env_vector32;
-#endif
-
-#define	UIO_HELPER(uio, iov, base, len, cnt, offset, sz, flg, rw, td)	\
-do {									\
-	iov.iov_base = (caddr_t)(base);					\
-	iov.iov_len = (len); 						\
-	uio.uio_iov = &(iov); 						\
-	uio.uio_iovcnt = (cnt);	 					\
-	uio.uio_offset = (off_t)(offset);				\
-	uio.uio_resid = (sz); 						\
-	uio.uio_segflg = (flg);						\
-	uio.uio_rw = (rw); 						\
-	uio.uio_td = (td);						\
-} while (0)
-
-	env_vector = malloc(sizeof(char *) * MAX_ARGV_STR, M_TEMP, M_WAITOK);
-
-#ifdef COMPAT_FREEBSD32
-	env_vector32 = NULL;
-	if (SV_PROC_FLAG(p, SV_ILP32) != 0) {
-		env_vector32 = malloc(sizeof(*env_vector32) * MAX_ARGV_STR,
-		    M_TEMP, M_WAITOK);
-		elm_len = sizeof(int32_t);
-		envp = (char *)env_vector32;
-
-		UIO_HELPER(tmp_uio, iov, &pss32, sizeof(pss32), 1,
-		    (off_t)(p->p_sysent->sv_psstrings),
-		    sizeof(pss32), UIO_SYSSPACE, UIO_READ, td);
-		ret = proc_rwmem(p, &tmp_uio);
-		if (ret != 0)
-			goto done;
-		pss.ps_argvstr = PTRIN(pss32.ps_argvstr);
-		pss.ps_nargvstr = pss32.ps_nargvstr;
-		pss.ps_envstr = PTRIN(pss32.ps_envstr);
-		pss.ps_nenvstr = pss32.ps_nenvstr;
-	} else {
-#endif
-		elm_len = sizeof(char *);
-		envp = (char *)env_vector;
-
-		UIO_HELPER(tmp_uio, iov, &pss, sizeof(pss), 1,
-		    (off_t)(p->p_sysent->sv_psstrings),
-		    sizeof(pss), UIO_SYSSPACE, UIO_READ, td);
-		ret = proc_rwmem(p, &tmp_uio);
-		if (ret != 0)
-			goto done;
-#ifdef COMPAT_FREEBSD32
-	}
-#endif
-
-	/* Get the array address and the number of elements */
-	resolver(pss, &addr, &n_elements);
-
-	/* Consistent with lib/libkvm/kvm_proc.c */
-	if (n_elements > MAX_ARGV_STR) {
-		ret = E2BIG;
-		goto done;
-	}
-
-	UIO_HELPER(tmp_uio, iov, envp, n_elements * elm_len, 1,
-	    (vm_offset_t)(addr), iov.iov_len, UIO_SYSSPACE, UIO_READ, td);
-	ret = proc_rwmem(p, &tmp_uio);
-	if (ret != 0)
-		goto done;
-#ifdef COMPAT_FREEBSD32
-	if (env_vector32 != NULL) {
-		for (i = 0; i < n_elements; i++)
-			env_vector[i] = PTRIN(env_vector32[i]);
-	}
-#endif
-
-	/* Now we can iterate through the list of strings */
-	for (i = 0; i < n_elements; i++) {
-		pbegin = (vm_offset_t)env_vector[i];
-		for (;;) {
-			UIO_HELPER(tmp_uio, iov, env_string, sizeof(env_string),
-			    1, pbegin, iov.iov_len, UIO_SYSSPACE, UIO_READ, td);
-			ret = proc_rwmem(p, &tmp_uio);
-			if (ret != 0)
-				goto done;
-
-			if (!strvalid(env_string, UIO_CHUNK_SZ)) {
-				/*
-				 * We didn't find the end of the string.
-				 * Add the string to the buffer and move
-				 * the pointer.  But do not allow strings
-				 * of unlimited length.
-				 */
-				sbuf_bcat(sb, env_string, UIO_CHUNK_SZ);
-				if (sbuf_len(sb) >= ARG_MAX) {
-					ret = E2BIG;
-					goto done;
-				}
-				pbegin += UIO_CHUNK_SZ;
-			} else {
-				sbuf_cat(sb, env_string);
-				break;
-			}
-		}
-		sbuf_bcat(sb, "", 1);
-	}
-#undef UIO_HELPER
-
-done:
-	free(env_vector, M_TEMP);
-#ifdef COMPAT_FREEBSD32
-	free(env_vector32, M_TEMP);
-#endif
-	return (ret);
-}
-
-static void
-ps_string_argv(const struct ps_strings ps, u_long *addr, int *n)
-{
-
-	*addr = (u_long) ps.ps_argvstr;
-	*n = ps.ps_nargvstr;
-}
-
-static void
-ps_string_env(const struct ps_strings ps, u_long *addr, int *n)
-{
-
-	*addr = (u_long) ps.ps_envstr;
-	*n = ps.ps_nenvstr;
-}
-
 /*
  * Filler function for proc/pid/cmdline
  */
@@ -1090,9 +946,15 @@ linprocfs_doproccmdline(PFS_FILL_ARGS)
 		PROC_UNLOCK(p);
 		return (0);
 	}
+
+	if ((p->p_flag & P_SYSTEM) != 0) {
+		PROC_UNLOCK(p);
+		return (0);
+	}
+
 	PROC_UNLOCK(p);
 
-	ret = linprocfs_doargv(td, p, sb, ps_string_argv);
+	ret = proc_getargv(td, p, sb);
 	return (ret);
 }
 
@@ -1118,9 +980,15 @@ linprocfs_doprocenviron(PFS_FILL_ARGS)
 		PROC_UNLOCK(p);
 		return (0);
 	}
+
+	if ((p->p_flag & P_SYSTEM) != 0) {
+		PROC_UNLOCK(p);
+		return (0);
+	}
+
 	PROC_UNLOCK(p);
 
-	ret = linprocfs_doargv(td, p, sb, ps_string_env);
+	ret = proc_getenvv(td, p, sb);
 	return (ret);
 }
 

Modified: stable/9/sys/fs/procfs/procfs_status.c
==============================================================================
--- stable/9/sys/fs/procfs/procfs_status.c	Sun Jan 29 20:39:42 2012	(r230753)
+++ stable/9/sys/fs/procfs/procfs_status.c	Sun Jan 29 21:08:24 2012	(r230754)
@@ -170,15 +170,10 @@ procfs_doprocstatus(PFS_FILL_ARGS)
 int
 procfs_doproccmdline(PFS_FILL_ARGS)
 {
-	struct ps_strings pstr;
-	char **ps_argvstr;
-	int error, i;
 
 	/*
 	 * If we are using the ps/cmdline caching, use that.  Otherwise
-	 * revert back to the old way which only implements full cmdline
-	 * for the current process and just p->p_comm for all other
-	 * processes.
+	 * read argv from the process space.
 	 * Note that if the argv is no longer available, we deliberately
 	 * don't fall back on p->p_comm or return an error: the authentic
 	 * Linux behaviour is to return zero-length in this case.
@@ -190,30 +185,13 @@ procfs_doproccmdline(PFS_FILL_ARGS)
 		PROC_UNLOCK(p);
 		return (0);
 	}
-	PROC_UNLOCK(p);
-	if (p != td->td_proc) {
-		sbuf_printf(sb, "%.*s", MAXCOMLEN, p->p_comm);
-	} else {
-		error = copyin((void *)p->p_sysent->sv_psstrings, &pstr,
-		    sizeof(pstr));
-		if (error)
-			return (error);
-		if (pstr.ps_nargvstr > ARG_MAX)
-			return (E2BIG);
-		ps_argvstr = malloc(pstr.ps_nargvstr * sizeof(char *),
-		    M_TEMP, M_WAITOK);
-		error = copyin((void *)pstr.ps_argvstr, ps_argvstr,
-		    pstr.ps_nargvstr * sizeof(char *));
-		if (error) {
-			free(ps_argvstr, M_TEMP);
-			return (error);
-		}
-		for (i = 0; i < pstr.ps_nargvstr; i++) {
-			sbuf_copyin(sb, ps_argvstr[i], 0);
-			sbuf_printf(sb, "%c", '\0');
-		}
-		free(ps_argvstr, M_TEMP);
+
+	if ((p->p_flag & P_SYSTEM) != 0) {
+		PROC_UNLOCK(p);
+		return (0);
 	}
 
-	return (0);
+	PROC_UNLOCK(p);
+
+	return (proc_getargv(td, p, sb));
 }

Modified: stable/9/sys/kern/kern_proc.c
==============================================================================
--- stable/9/sys/kern/kern_proc.c	Sun Jan 29 20:39:42 2012	(r230753)
+++ stable/9/sys/kern/kern_proc.c	Sun Jan 29 21:08:24 2012	(r230754)
@@ -41,6 +41,8 @@ __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/systm.h>
+#include <sys/elf.h>
+#include <sys/exec.h>
 #include <sys/kernel.h>
 #include <sys/limits.h>
 #include <sys/lock.h>
@@ -50,7 +52,9 @@ __FBSDID("$FreeBSD$");
 #include <sys/mount.h>
 #include <sys/mutex.h>
 #include <sys/proc.h>
+#include <sys/ptrace.h>
 #include <sys/refcount.h>
+#include <sys/resourcevar.h>
 #include <sys/sbuf.h>
 #include <sys/sysent.h>
 #include <sys/sched.h>
@@ -326,6 +330,55 @@ pgfind(pgid)
 }
 
 /*
+ * Locate process and do additional manipulations, depending on flags.
+ */
+int
+pget(pid_t pid, int flags, struct proc **pp)
+{
+	struct proc *p;
+	int error;
+
+	p = pfind(pid);
+	if (p == NULL)
+		return (ESRCH);
+	if ((flags & PGET_CANSEE) != 0) {
+		error = p_cansee(curthread, p);
+		if (error != 0)
+			goto errout;
+	}
+	if ((flags & PGET_CANDEBUG) != 0) {
+		error = p_candebug(curthread, p);
+		if (error != 0)
+			goto errout;
+	}
+	if ((flags & PGET_ISCURRENT) != 0 && curproc != p) {
+		error = EPERM;
+		goto errout;
+	}
+	if ((flags & PGET_NOTWEXIT) != 0 && (p->p_flag & P_WEXIT) != 0) {
+		error = ESRCH;
+		goto errout;
+	}
+	if ((flags & PGET_NOTINEXEC) != 0 && (p->p_flag & P_INEXEC) != 0) {
+		/*
+		 * XXXRW: Not clear ESRCH is the right error during proc
+		 * execve().
+		 */
+		error = ESRCH;
+		goto errout;
+	}
+	if ((flags & PGET_HOLD) != 0) {
+		_PHOLD(p);
+		PROC_UNLOCK(p);
+	}
+	*pp = p;
+	return (0);
+errout:
+	PROC_UNLOCK(p);
+	return (error);
+}
+
+/*
  * Create a new process group.
  * pgid must be equal to the pid of p.
  * Begin a new session if required.
@@ -1153,7 +1206,7 @@ sysctl_out_proc(struct proc *p, struct s
 static int
 sysctl_kern_proc(SYSCTL_HANDLER_ARGS)
 {
-	int *name = (int*) arg1;
+	int *name = (int *)arg1;
 	u_int namelen = arg2;
 	struct proc *p;
 	int flags, doingzomb, oid_number;
@@ -1168,18 +1221,14 @@ sysctl_kern_proc(SYSCTL_HANDLER_ARGS)
 		oid_number &= ~KERN_PROC_INC_THREAD;
 	}
 	if (oid_number == KERN_PROC_PID) {
-		if (namelen != 1) 
+		if (namelen != 1)
 			return (EINVAL);
 		error = sysctl_wire_old_buffer(req, 0);
 		if (error)
-			return (error);		
-		p = pfind((pid_t)name[0]);
-		if (!p)
-			return (ESRCH);
-		if ((error = p_cansee(curthread, p))) {
-			PROC_UNLOCK(p);
 			return (error);
-		}
+		error = pget((pid_t)name[0], PGET_CANSEE, &p);
+		if (error != 0)
+			return (error);
 		error = sysctl_out_proc(p, req, flags);
 		return (error);
 	}
@@ -1198,7 +1247,7 @@ sysctl_kern_proc(SYSCTL_HANDLER_ARGS)
 			return (EINVAL);
 		break;
 	}
-	
+
 	if (!req->oldptr) {
 		/* overestimate by 5 procs */
 		error = SYSCTL_OUT(req, 0, sizeof (struct kinfo_proc) * 5);
@@ -1278,7 +1327,7 @@ sysctl_kern_proc(SYSCTL_HANDLER_ARGS)
 				/* XXX proctree_lock */
 				SESS_LOCK(p->p_session);
 				if (p->p_session->s_ttyp == NULL ||
-				    tty_udev(p->p_session->s_ttyp) != 
+				    tty_udev(p->p_session->s_ttyp) !=
 				    (dev_t)name[0]) {
 					SESS_UNLOCK(p->p_session);
 					PROC_UNLOCK(p);
@@ -1358,6 +1407,290 @@ pargs_drop(struct pargs *pa)
 		pargs_free(pa);
 }
 
+static int
+proc_read_mem(struct thread *td, struct proc *p, vm_offset_t offset, void* buf,
+    size_t len)
+{
+	struct iovec iov;
+	struct uio uio;
+
+	iov.iov_base = (caddr_t)buf;
+	iov.iov_len = len;
+	uio.uio_iov = &iov;
+	uio.uio_iovcnt = 1;
+	uio.uio_offset = offset;
+	uio.uio_resid = (ssize_t)len;
+	uio.uio_segflg = UIO_SYSSPACE;
+	uio.uio_rw = UIO_READ;
+	uio.uio_td = td;
+
+	return (proc_rwmem(p, &uio));
+}
+
+static int
+proc_read_string(struct thread *td, struct proc *p, const char *sptr, char *buf,
+    size_t len)
+{
+	size_t i;
+	int error;
+
+	error = proc_read_mem(td, p, (vm_offset_t)sptr, buf, len);
+	/*
+	 * Reading the chunk may validly return EFAULT if the string is shorter
+	 * than the chunk and is aligned at the end of the page, assuming the
+	 * next page is not mapped.  So if EFAULT is returned do a fallback to
+	 * one byte read loop.
+	 */
+	if (error == EFAULT) {
+		for (i = 0; i < len; i++, buf++, sptr++) {
+			error = proc_read_mem(td, p, (vm_offset_t)sptr, buf, 1);
+			if (error != 0)
+				return (error);
+			if (*buf == '\0')
+				break;
+		}
+		error = 0;
+	}
+	return (error);
+}
+
+#define PROC_AUXV_MAX	256	/* Safety limit on auxv size. */
+
+enum proc_vector_type {
+	PROC_ARG,
+	PROC_ENV,
+	PROC_AUX,
+};
+
+#ifdef COMPAT_FREEBSD32
+static int
+get_proc_vector32(struct thread *td, struct proc *p, char ***proc_vectorp,
+    size_t *vsizep, enum proc_vector_type type)
+{
+	struct freebsd32_ps_strings pss;
+	Elf32_Auxinfo aux;
+	vm_offset_t vptr, ptr;
+	uint32_t *proc_vector32;
+	char **proc_vector;
+	size_t vsize, size;
+	int i, error;
+
+	error = proc_read_mem(td, p, (vm_offset_t)(p->p_sysent->sv_psstrings),
+	    &pss, sizeof(pss));
+	if (error != 0)
+		return (error);
+	switch (type) {
+	case PROC_ARG:
+		vptr = (vm_offset_t)PTRIN(pss.ps_argvstr);
+		vsize = pss.ps_nargvstr;
+		if (vsize > ARG_MAX)
+			return (ENOEXEC);
+		size = vsize * sizeof(int32_t);
+		break;
+	case PROC_ENV:
+		vptr = (vm_offset_t)PTRIN(pss.ps_envstr);
+		vsize = pss.ps_nenvstr;
+		if (vsize > ARG_MAX)
+			return (ENOEXEC);
+		size = vsize * sizeof(int32_t);
+		break;
+	case PROC_AUX:
+		vptr = (vm_offset_t)PTRIN(pss.ps_envstr) +
+		    (pss.ps_nenvstr + 1) * sizeof(int32_t);
+		if (vptr % 4 != 0)
+			return (ENOEXEC);
+		for (ptr = vptr, i = 0; i < PROC_AUXV_MAX; i++) {
+			error = proc_read_mem(td, p, ptr, &aux, sizeof(aux));
+			if (error != 0)
+				return (error);
+			if (aux.a_type == AT_NULL)
+				break;
+			ptr += sizeof(aux);
+		}
+		if (aux.a_type != AT_NULL)
+			return (ENOEXEC);
+		vsize = i + 1;
+		size = vsize * sizeof(aux);
+		break;
+	default:
+		KASSERT(0, ("Wrong proc vector type: %d", type));
+		return (EINVAL);
+	}
+	proc_vector32 = malloc(size, M_TEMP, M_WAITOK);
+	error = proc_read_mem(td, p, vptr, proc_vector32, size);
+	if (error != 0)
+		goto done;
+	if (type == PROC_AUX) {
+		*proc_vectorp = (char **)proc_vector32;
+		*vsizep = vsize;
+		return (0);
+	}
+	proc_vector = malloc(vsize * sizeof(char *), M_TEMP, M_WAITOK);
+	for (i = 0; i < (int)vsize; i++)
+		proc_vector[i] = PTRIN(proc_vector32[i]);
+	*proc_vectorp = proc_vector;
+	*vsizep = vsize;
+done:
+	free(proc_vector32, M_TEMP);
+	return (error);
+}
+#endif
+
+static int
+get_proc_vector(struct thread *td, struct proc *p, char ***proc_vectorp,
+    size_t *vsizep, enum proc_vector_type type)
+{
+	struct ps_strings pss;
+	Elf_Auxinfo aux;
+	vm_offset_t vptr, ptr;
+	char **proc_vector;
+	size_t vsize, size;
+	int error, i;
+
+#ifdef COMPAT_FREEBSD32
+	if (SV_PROC_FLAG(p, SV_ILP32) != 0)
+		return (get_proc_vector32(td, p, proc_vectorp, vsizep, type));
+#endif
+	error = proc_read_mem(td, p, (vm_offset_t)(p->p_sysent->sv_psstrings),
+	    &pss, sizeof(pss));
+	if (error != 0)
+		return (error);
+	switch (type) {
+	case PROC_ARG:
+		vptr = (vm_offset_t)pss.ps_argvstr;
+		vsize = pss.ps_nargvstr;
+		if (vsize > ARG_MAX)
+			return (ENOEXEC);
+		size = vsize * sizeof(char *);
+		break;
+	case PROC_ENV:
+		vptr = (vm_offset_t)pss.ps_envstr;
+		vsize = pss.ps_nenvstr;
+		if (vsize > ARG_MAX)
+			return (ENOEXEC);
+		size = vsize * sizeof(char *);
+		break;
+	case PROC_AUX:
+		/*
+		 * The aux array is just above env array on the stack. Check
+		 * that the address is naturally aligned.
+		 */
+		vptr = (vm_offset_t)pss.ps_envstr + (pss.ps_nenvstr + 1)
+		    * sizeof(char *);
+#if __ELF_WORD_SIZE == 64
+		if (vptr % sizeof(uint64_t) != 0)
+#else
+		if (vptr % sizeof(uint32_t) != 0)
+#endif
+			return (ENOEXEC);
+		/*
+		 * We count the array size reading the aux vectors from the
+		 * stack until AT_NULL vector is returned.  So (to keep the code
+		 * simple) we read the process stack twice: the first time here
+		 * to find the size and the second time when copying the vectors
+		 * to the allocated proc_vector.
+		 */
+		for (ptr = vptr, i = 0; i < PROC_AUXV_MAX; i++) {
+			error = proc_read_mem(td, p, ptr, &aux, sizeof(aux));
+			if (error != 0)
+				return (error);
+			if (aux.a_type == AT_NULL)
+				break;
+			ptr += sizeof(aux);
+		}
+		/*
+		 * If the PROC_AUXV_MAX entries are iterated over, and we have
+		 * not reached AT_NULL, it is most likely we are reading wrong
+		 * data: either the process doesn't have auxv array or data has
+		 * been modified. Return the error in this case.
+		 */
+		if (aux.a_type != AT_NULL)
+			return (ENOEXEC);
+		vsize = i + 1;
+		size = vsize * sizeof(aux);
+		break;
+	default:
+		KASSERT(0, ("Wrong proc vector type: %d", type));
+		return (EINVAL); /* In case we are built without INVARIANTS. */
+	}
+	proc_vector = malloc(size, M_TEMP, M_WAITOK);
+	if (proc_vector == NULL)
+		return (ENOMEM);
+	error = proc_read_mem(td, p, vptr, proc_vector, size);
+	if (error != 0) {
+		free(proc_vector, M_TEMP);
+		return (error);
+	}
+	*proc_vectorp = proc_vector;
+	*vsizep = vsize;
+
+	return (0);
+}
+
+#define GET_PS_STRINGS_CHUNK_SZ	256	/* Chunk size (bytes) for ps_strings operations. */
+
+static int
+get_ps_strings(struct thread *td, struct proc *p, struct sbuf *sb,
+    enum proc_vector_type type)
+{
+	size_t done, len, nchr, vsize;
+	int error, i;
+	char **proc_vector, *sptr;
+	char pss_string[GET_PS_STRINGS_CHUNK_SZ];
+
+	PROC_ASSERT_HELD(p);
+
+	/*
+	 * We are not going to read more than 2 * (PATH_MAX + ARG_MAX) bytes.
+	 */
+	nchr = 2 * (PATH_MAX + ARG_MAX);
+
+	error = get_proc_vector(td, p, &proc_vector, &vsize, type);
+	if (error != 0)
+		return (error);
+	for (done = 0, i = 0; i < (int)vsize && done < nchr; i++) {
+		/*
+		 * The program may have scribbled into its argv array, e.g. to
+		 * remove some arguments.  If that has happened, break out
+		 * before trying to read from NULL.
+		 */
+		if (proc_vector[i] == NULL)
+			break;
+		for (sptr = proc_vector[i]; ; sptr += GET_PS_STRINGS_CHUNK_SZ) {
+			error = proc_read_string(td, p, sptr, pss_string,
+			    sizeof(pss_string));
+			if (error != 0)
+				goto done;
+			len = strnlen(pss_string, GET_PS_STRINGS_CHUNK_SZ);
+			if (done + len >= nchr)
+				len = nchr - done - 1;
+			sbuf_bcat(sb, pss_string, len);
+			if (len != GET_PS_STRINGS_CHUNK_SZ)
+				break;
+			done += GET_PS_STRINGS_CHUNK_SZ;
+		}
+		sbuf_bcat(sb, "", 1);
+		done += len + 1;
+	}
+done:
+	free(proc_vector, M_TEMP);
+	return (error);
+}
+
+int
+proc_getargv(struct thread *td, struct proc *p, struct sbuf *sb)
+{
+
+	return (get_ps_strings(curthread, p, sb, PROC_ARG));
+}
+
+int
+proc_getenvv(struct thread *td, struct proc *p, struct sbuf *sb)
+{
+
+	return (get_ps_strings(curthread, p, sb, PROC_ENV));
+}
+
 /*
  * This sysctl allows a process to retrieve the argument list or process
  * title for another process without groping around in the address space
@@ -1367,35 +1700,42 @@ pargs_drop(struct pargs *pa)
 static int
 sysctl_kern_proc_args(SYSCTL_HANDLER_ARGS)
 {
-	int *name = (int*) arg1;
+	int *name = (int *)arg1;
 	u_int namelen = arg2;
 	struct pargs *newpa, *pa;
 	struct proc *p;
-	int error = 0;
+	struct sbuf sb;
+	int flags, error = 0, error2;
 
-	if (namelen != 1) 
+	if (namelen != 1)
 		return (EINVAL);
 
-	p = pfind((pid_t)name[0]);
-	if (!p)
-		return (ESRCH);
-
-	if ((error = p_cansee(curthread, p)) != 0) {
-		PROC_UNLOCK(p);
+	flags = PGET_CANSEE;
+	if (req->newptr != NULL)
+		flags |= PGET_ISCURRENT;
+	error = pget((pid_t)name[0], flags, &p);
+	if (error)
 		return (error);
-	}
-
-	if (req->newptr && curproc != p) {
-		PROC_UNLOCK(p);
-		return (EPERM);
-	}
 
 	pa = p->p_args;
-	pargs_hold(pa);
-	PROC_UNLOCK(p);
-	if (pa != NULL)
+	if (pa != NULL) {
+		pargs_hold(pa);
+		PROC_UNLOCK(p);
 		error = SYSCTL_OUT(req, pa->ar_args, pa->ar_length);
-	pargs_drop(pa);
+		pargs_drop(pa);
+	} else if ((p->p_flag & (P_WEXIT | P_SYSTEM)) == 0) {
+		_PHOLD(p);
+		PROC_UNLOCK(p);
+		sbuf_new_for_sysctl(&sb, NULL, GET_PS_STRINGS_CHUNK_SZ, req);
+		error = proc_getargv(curthread, p, &sb);
+		error2 = sbuf_finish(&sb);
+		PRELE(p);
+		sbuf_delete(&sb);
+		if (error == 0 && error2 != 0)
+			error = error2;
+	} else {
+		PROC_UNLOCK(p);
+	}
 	if (error != 0 || req->newptr == NULL)
 		return (error);
 
@@ -1416,6 +1756,78 @@ sysctl_kern_proc_args(SYSCTL_HANDLER_ARG
 }
 
 /*
+ * This sysctl allows a process to retrieve environment of another process.
+ */
+static int
+sysctl_kern_proc_env(SYSCTL_HANDLER_ARGS)
+{
+	int *name = (int *)arg1;
+	u_int namelen = arg2;
+	struct proc *p;
+	struct sbuf sb;
+	int error, error2;
+
+	if (namelen != 1)
+		return (EINVAL);
+
+	error = pget((pid_t)name[0], PGET_WANTREAD, &p);
+	if (error != 0)
+		return (error);
+	if ((p->p_flag & P_SYSTEM) != 0) {
+		PRELE(p);
+		return (0);
+	}
+
+	sbuf_new_for_sysctl(&sb, NULL, GET_PS_STRINGS_CHUNK_SZ, req);
+	error = proc_getenvv(curthread, p, &sb);
+	error2 = sbuf_finish(&sb);
+	PRELE(p);
+	sbuf_delete(&sb);
+	return (error != 0 ? error : error2);
+}
+
+/*
+ * This sysctl allows a process to retrieve ELF auxiliary vector of
+ * another process.
+ */
+static int
+sysctl_kern_proc_auxv(SYSCTL_HANDLER_ARGS)
+{
+	int *name = (int *)arg1;
+	u_int namelen = arg2;
+	struct proc *p;
+	size_t vsize, size;
+	char **auxv;
+	int error;
+
+	if (namelen != 1)
+		return (EINVAL);
+
+	error = pget((pid_t)name[0], PGET_WANTREAD, &p);
+	if (error != 0)
+		return (error);
+	if ((p->p_flag & P_SYSTEM) != 0) {
+		PRELE(p);
+		return (0);
+	}
+	error = get_proc_vector(curthread, p, &auxv, &vsize, PROC_AUX);
+	if (error == 0) {
+#ifdef COMPAT_FREEBSD32
+		if (SV_PROC_FLAG(p, SV_ILP32) != 0)
+			size = vsize * sizeof(Elf32_Auxinfo);
+		else
+#endif
+		size = vsize * sizeof(Elf_Auxinfo);
+		PRELE(p);
+		error = SYSCTL_OUT(req, auxv, size);
+		free(auxv, M_TEMP);
+	} else {
+		PRELE(p);
+	}
+	return (error);
+}
+
+/*
  * This sysctl allows a process to retrieve the path of the executable for
  * itself or another process.
  */
@@ -1434,13 +1846,9 @@ sysctl_kern_proc_pathname(SYSCTL_HANDLER
 	if (*pidp == -1) {	/* -1 means this process */
 		p = req->td->td_proc;
 	} else {
-		p = pfind(*pidp);
-		if (p == NULL)
-			return (ESRCH);
-		if ((error = p_cansee(curthread, p)) != 0) {
-			PROC_UNLOCK(p);
+		error = pget(*pidp, PGET_CANSEE, &p);
+		if (error != 0)
 			return (error);
-		}
 	}
 
 	vp = p->p_textvp;
@@ -1473,16 +1881,13 @@ sysctl_kern_proc_sv_name(SYSCTL_HANDLER_
 	int error;
 
 	namelen = arg2;
-	if (namelen != 1) 
+	if (namelen != 1)
 		return (EINVAL);
 
 	name = (int *)arg1;
-	if ((p = pfind((pid_t)name[0])) == NULL)
-		return (ESRCH);
-	if ((error = p_cansee(curthread, p))) {
-		PROC_UNLOCK(p);
+	error = pget((pid_t)name[0], PGET_CANSEE, &p);
+	if (error != 0)
 		return (error);
-	}
 	sv_name = p->p_sysent->sv_name;
 	PROC_UNLOCK(p);
 	return (sysctl_handle_string(oidp, sv_name, 0, req));
@@ -1509,18 +1914,9 @@ sysctl_kern_proc_ovmmap(SYSCTL_HANDLER_A
 	struct vmspace *vm;
 
 	name = (int *)arg1;
-	if ((p = pfind((pid_t)name[0])) == NULL)
-		return (ESRCH);
-	if (p->p_flag & P_WEXIT) {
-		PROC_UNLOCK(p);
-		return (ESRCH);
-	}
-	if ((error = p_candebug(curthread, p))) {
-		PROC_UNLOCK(p);
+	error = pget((pid_t)name[0], PGET_WANTREAD, &p);
+	if (error != 0)
 		return (error);
-	}
-	_PHOLD(p);
-	PROC_UNLOCK(p);
 	vm = vmspace_acquire_ref(p);
 	if (vm == NULL) {
 		PRELE(p);
@@ -1687,18 +2083,9 @@ sysctl_kern_proc_vmmap(SYSCTL_HANDLER_AR
 	vm_map_t map;
 
 	name = (int *)arg1;
-	if ((p = pfind((pid_t)name[0])) == NULL)
-		return (ESRCH);
-	if (p->p_flag & P_WEXIT) {
-		PROC_UNLOCK(p);
-		return (ESRCH);
-	}
-	if ((error = p_candebug(curthread, p))) {
-		PROC_UNLOCK(p);
+	error = pget((pid_t)name[0], PGET_WANTREAD, &p);
+	if (error != 0)
 		return (error);
-	}
-	_PHOLD(p);
-	PROC_UNLOCK(p);
 	vm = vmspace_acquire_ref(p);
 	if (vm == NULL) {
 		PRELE(p);
@@ -1872,19 +2259,9 @@ sysctl_kern_proc_kstack(SYSCTL_HANDLER_A
 	struct proc *p;
 
 	name = (int *)arg1;
-	if ((p = pfind((pid_t)name[0])) == NULL)
-		return (ESRCH);
-	/* XXXRW: Not clear ESRCH is the right error during proc execve(). */
-	if (p->p_flag & P_WEXIT || p->p_flag & P_INEXEC) {
-		PROC_UNLOCK(p);
-		return (ESRCH);
-	}
-	if ((error = p_candebug(curthread, p))) {
-		PROC_UNLOCK(p);
+	error = pget((pid_t)name[0], PGET_NOTINEXEC | PGET_WANTREAD, &p);
+	if (error != 0)
 		return (error);
-	}
-	_PHOLD(p);
-	PROC_UNLOCK(p);
 
 	kkstp = malloc(sizeof(*kkstp), M_TEMP, M_WAITOK);
 	st = stack_create();
@@ -1979,13 +2356,9 @@ sysctl_kern_proc_groups(SYSCTL_HANDLER_A
 	if (*pidp == -1) {	/* -1 means this process */
 		p = req->td->td_proc;
 	} else {
-		p = pfind(*pidp);
-		if (p == NULL)
-			return (ESRCH);
-		if ((error = p_cansee(curthread, p)) != 0) {
-			PROC_UNLOCK(p);
+		error = pget(*pidp, PGET_CANSEE, &p);
+		if (error != 0)
 			return (error);
-		}
 	}
 
 	cred = crhold(p->p_ucred);
@@ -1998,6 +2371,106 @@ sysctl_kern_proc_groups(SYSCTL_HANDLER_A
 	return (error);
 }
 
+/*
+ * This sysctl allows a process to retrieve or/and set the resource limit for
+ * another process.
+ */
+static int
+sysctl_kern_proc_rlimit(SYSCTL_HANDLER_ARGS)
+{
+	int *name = (int *)arg1;
+	u_int namelen = arg2;
+	struct rlimit rlim;
+	struct proc *p;
+	u_int which;
+	int flags, error;
+
+	if (namelen != 2)
+		return (EINVAL);
+
+	which = (u_int)name[1];
+	if (which >= RLIM_NLIMITS)
+		return (EINVAL);
+
+	if (req->newptr != NULL && req->newlen != sizeof(rlim))
+		return (EINVAL);
+
+	flags = PGET_HOLD | PGET_NOTWEXIT;
+	if (req->newptr != NULL)
+		flags |= PGET_CANDEBUG;
+	else
+		flags |= PGET_CANSEE;
+	error = pget((pid_t)name[0], flags, &p);
+	if (error != 0)
+		return (error);
+
+	/*
+	 * Retrieve limit.
+	 */
+	if (req->oldptr != NULL) {
+		PROC_LOCK(p);
+		lim_rlimit(p, which, &rlim);
+		PROC_UNLOCK(p);
+	}
+	error = SYSCTL_OUT(req, &rlim, sizeof(rlim));
+	if (error != 0)
+		goto errout;
+
+	/*
+	 * Set limit.
+	 */
+	if (req->newptr != NULL) {
+		error = SYSCTL_IN(req, &rlim, sizeof(rlim));
+		if (error == 0)
+			error = kern_proc_setrlimit(curthread, p, which, &rlim);
+	}
+
+errout:
+	PRELE(p);
+	return (error);
+}
+
+/*
+ * This sysctl allows a process to retrieve ps_strings structure location of
+ * another process.
+ */
+static int
+sysctl_kern_proc_ps_strings(SYSCTL_HANDLER_ARGS)
+{
+	int *name = (int *)arg1;
+	u_int namelen = arg2;
+	struct proc *p;
+	vm_offset_t ps_strings;
+	int error;
+#ifdef COMPAT_FREEBSD32
+	uint32_t ps_strings32;
+#endif
+
+	if (namelen != 1)
+		return (EINVAL);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201201292108.q0TL8O61052689>