Subject: kern/7164: procfs: procfs_cmdline.c fixes
To: None <gnats-bugs@gnats.netbsd.org>
From: Jaromir Dolecek <dolecek@ics.muni.cz>
List: netbsd-bugs
Date: 03/16/1999 00:19:16
>Number:         7164
>Category:       kern
>Synopsis:       procfs: procfs_cmdline.c fixes
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    kern-bug-people (Kernel Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Mar 15 15:35:01 1999
>Last-Modified:
>Originator:     Jaromir Dolecek
>Organization:
	Per4mance, Brno, Czech Republic
>Release:        NetBSD-19990115 or so
>Environment:
	
System: NetBSD jdolecek.per4mance.cz 1.3I NetBSD 1.3I (JDOLECEK) #7: Thu Mar 11 18:33:22 MET 1999 dolecek@jdolecek.per4mance.cz:/home/dolecek/tmp/src/sys/arch/i386/compile/JDOLECEK i386


>Description:
	There are two main problems with code in procfs_cmdline.c:
	1. it uses ARG_MAX (about 256K) big buffer to hold the data;
		as huge amount of programs have much smaller argv,
		it's greatly inefficient
	2. the argv string is mapped by one character at time

	besides that, I found out that name of SZOMB or kernel
	process is cut to 4 characters -- the second parameter
	in call to snprintf() stayed accidentaly on sizeof(arg)
	aka sizeof(char **) from time when arg was automatic array.
	It's also not necessary to call strlen() on resulting string,
	the same value can be obtained directly from snprintf() call.

>How-To-Repeat:
>Fix:
	The patch below uses PAGE_SIZE for both the buffer size
	and unit for argv copying, I assumed it's the most
	efficient value for this kind of things.
	
	The argv data are copied from position uio->uio_offset
	to the end of the repective memory page, in pythonic
	terms:
		to_write = argv[uio->uio_offset:round_page(uio->uio_offset+1)]

	I tested it with /proc/curproc/cmdline and /proc/*{1|2}/cmdline.
	It seems to work okay for processes with very long (>PAGE_SIZE)
	argv too, but I didn't stress it very hard.

Index: procfs_cmdline.c
===================================================================
RCS file: /home/dolecek/cvsroot/procfs/procfs_cmdline.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- procfs_cmdline.c	1999/03/15 18:57:05	1.3
+++ procfs_cmdline.c	1999/03/15 22:53:07	1.4
@@ -48,6 +48,7 @@
 #include <miscfs/procfs/procfs.h>
 
 #include <vm/vm.h>
+#include <vm/vm_param.h>
 #if defined(UVM)
 #include <uvm/uvm_extern.h>
 #endif
@@ -63,7 +64,8 @@
 	struct uio *uio;
 {
 	struct ps_strings pss;
-	int xlen, len, count, error;
+	int xlen, count, error, i;
+	size_t len, upper_bound;
 	struct uio auio;
 	struct iovec aiov;
 	vaddr_t argv;
@@ -75,11 +77,8 @@
 
 	/*
 	 * Allocate a temporary buffer to hold the arguments.
-	 *
-	 * XXX THIS COULD BE HANDLED MUCH MORE INTELLIGENTLY,
-	 * XXX WITHOUT REQUIRING A 256k TEMPORARY BUFFER!
 	 */
-	arg = malloc(ARG_MAX, M_TEMP, M_WAITOK);
+	arg = malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
 
 	/*
 	 * Zombies don't have a stack, so we can't read their psstrings.
@@ -87,8 +86,7 @@
 	 * ps(1) would display.
 	 */
 	if (p->p_stat == SZOMB || (p->p_flag & P_SYSTEM) != 0) {
-		snprintf(arg, sizeof(arg), "(%s)", p->p_comm);
-		len = strlen(arg);	/* exclude last NUL */
+		len = snprintf(arg, PAGE_SIZE, "(%s)", p->p_comm);
 		goto doio;
 	}
 
@@ -152,19 +150,20 @@
 		goto bad;
 
 	/*
-	 * Now copy in the actual argument vector, one byte at a time,
+	 * Now copy in the actual argument vector, one page at a time,
 	 * since we don't know how long the vector is (though, we do
 	 * know how many NUL-terminated strings are in the vector).
 	 */
 	len = 0;
 	count = pss.ps_nargvstr;
-	while (count && len < ARG_MAX) {
-		aiov.iov_base = &arg[len];
-		aiov.iov_len = 1;
+	upper_bound = round_page(uio->uio_offset+1);
+	for (; count && len < upper_bound; len += PAGE_SIZE) {
+		aiov.iov_base = arg;
+		aiov.iov_len = PAGE_SIZE;
 		auio.uio_iov = &aiov;
 		auio.uio_iovcnt = 1;
 		auio.uio_offset = argv + len;
-		auio.uio_resid = 1;
+		auio.uio_resid = PAGE_SIZE;
 		auio.uio_segflg = UIO_SYSSPACE;
 		auio.uio_rw = UIO_READ;
 		auio.uio_procp = NULL;
@@ -176,9 +175,16 @@
 		if (error)
 			goto bad;
 
-		if (len > 0 && arg[len] == '\0')
-			count--;	/* one full string */
-		len++;
+		for(i=len; i < (len + PAGE_SIZE) && count; i++) {
+			if (arg[i] == '\0')
+				count--;	/* one full string */
+		}
+
+		if (!count) {
+			/* no more argv strings, set up len and break */
+			len = i;
+			break;
+		}
 	}
 	if (len > 0)
 		len--;			/* exclude last NUL */
@@ -186,30 +192,24 @@
 	/*
 	 * Release the process.
 	 */
-#if defined(UVM)
 	PRELE(p);
+#if defined(UVM)
 	uvmspace_free(p->p_vmspace);
-#else
-	PRELE(p);
 #endif
 
  doio:
 	xlen = len - uio->uio_offset;
-	xlen = imin(xlen, uio->uio_resid);
 	if (xlen <= 0) 
 		error = 0;
 	else
-		error = uiomove(arg + uio->uio_offset, xlen, uio);
-
+		error = uiomove(arg + trunc_page(len), xlen, uio);
 	free(arg, M_TEMP);
 	return (error);
 
  bad:
-#if defined(UVM)
 	PRELE(p);
+#if defined(UVM)
 	uvmspace_free(p->p_vmspace);
-#else
-	PRELE(p);
 #endif
 	free(arg, M_TEMP);
 	return (error);
>Audit-Trail:
>Unformatted: