---
 proc/readproc.c |   63 ++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 41 insertions(+), 22 deletions(-)

--- proc/readproc.c
+++ proc/readproc.c	2018-06-06 07:30:21.342230289 +0000
@@ -26,6 +26,7 @@
 #include <sys/dir.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <limits.h>
 
 // sometimes it's easier to do this manually, w/o gcc helping
 #ifdef PROF
@@ -420,9 +421,10 @@ static void statm2proc(const char* s, pr
 
 static int file2str(const char *directory, const char *what, char *ret, int cap) {
     static char filename[80];
-    int fd, num_read;
+    int fd, num_read, len;
 
-    sprintf(filename, "%s/%s", directory, what);
+    len = sprintf(filename, "%s/%s", directory, what);
+    if (len <= 0 || (size_t)len >= sizeof filename) return -1;
     fd = open(filename, O_RDONLY, 0);
     if(unlikely(fd==-1)) return -1;
     num_read = read(fd, ret, cap - 1);
@@ -434,27 +436,36 @@ static int file2str(const char *director
 
 static char** file2strvec(const char* directory, const char* what) {
     char buf[2048];	/* read buf bytes at a time */
-    char *p, *rbuf = 0, *endbuf, **q, **ret;
+    char *p, *rbuf = 0, *endbuf, **q, **ret, *strp;
     int fd, tot = 0, n, c, end_of_file = 0;
     int align;
 
-    sprintf(buf, "%s/%s", directory, what);
+    const int len = snprintf(buf, sizeof buf, "%s/%s", directory, what);
+    if(len <= 0 || (size_t)len >= sizeof buf) return NULL;
     fd = open(buf, O_RDONLY, 0);
     if(fd==-1) return NULL;
 
     /* read whole file into a memory buffer, allocating as we go */
-    while ((n = read(fd, buf, sizeof buf - 1)) > 0) {
+    while ((n = read(fd, buf, sizeof buf - 1)) >= 0) {
 	if (n < (int)(sizeof buf - 1))
 	    end_of_file = 1;
-	if (n == 0 && rbuf == 0)
-	    return NULL;	/* process died between our open and read */
-	if (n < 0) {
-	    if (rbuf)
-		free(rbuf);
-	    return NULL;	/* read error */
+	if (n <= 0 && tot <= 0) { /* nothing read now, nothing read before */
+	    break;		/* process died between our open and read */
+	}
+	/* ARG_LEN is our guesstimated median length of a command-line argument
+	    or environment variable (the minimum is 1, the maximum is 131072) */
+	#define ARG_LEN 64
+	if (tot >= INT_MAX / (ARG_LEN + (int)sizeof(char*)) * ARG_LEN - n) {
+	    end_of_file = 1; /* integer overflow: null-terminate and break */
+	    n = 0; /* but tot > 0 */
 	}
-	if (end_of_file && buf[n-1])		/* last read char not null */
+	#undef ARG_LEN
+	if (end_of_file &&
+	    ((n > 0 && buf[n-1] != '\0') ||	/* last read char not null */
+	     (n <= 0 && rbuf[tot-1] != '\0')))	/* last read char not null */
 	    buf[n++] = '\0';			/* so append null-terminator */
+
+	if (n <= 0) break; /* unneeded (end_of_file = 1) but avoid realloc */
 	rbuf = xrealloc(rbuf, tot + n);		/* allocate more memory */
 	memcpy(rbuf + tot, buf, n);		/* copy buffer into it */
 	tot += n;				/* increment total byte ctr */
@@ -462,26 +473,34 @@ static char** file2strvec(const char* di
 	    break;
     }
     close(fd);
-    if (n <= 0 && !end_of_file) {
+    if (n < 0 || tot <= 0) {	/* error, or nothing read */
 	if (rbuf) free(rbuf);
 	return NULL;		/* read error */
     }
+    rbuf[tot-1] = '\0'; /* belt and suspenders (the while loop did it, too) */
     endbuf = rbuf + tot;			/* count space for pointers */
     align = (sizeof(char*)-1) - ((tot + sizeof(char*)-1) & (sizeof(char*)-1));
-    for (c = 0, p = rbuf; p < endbuf; p++)
-    	if (!*p)
+    c = sizeof(char*);				/* one extra for NULL term */
+    for (p = rbuf; p < endbuf; p++) {
+	if (!*p || *p == '\n') {
+	    if (c >= INT_MAX - (tot + (int)sizeof(char*) + align)) break;
 	    c += sizeof(char*);
-    c += sizeof(char*);				/* one extra for NULL term */
+	}
+	if (*p == '\n')
+	    *p = 0;
+    }
 
     rbuf = xrealloc(rbuf, tot + c + align);	/* make room for ptrs AT END */
     endbuf = rbuf + tot;			/* addr just past data buf */
     q = ret = (char**) (endbuf+align);		/* ==> free(*ret) to dealloc */
-    *q++ = p = rbuf;				/* point ptrs to the strings */
-    endbuf--;					/* do not traverse final NUL */
-    while (++p < endbuf) 
-    	if (!*p)				/* NUL char implies that */
-	    *q++ = p+1;				/* next string -> next char */
-
+    for (strp = p = rbuf; p < endbuf; p++) {
+	if (!*p) {				/* NUL char implies that */
+	    if (c < 2 * (int)sizeof(char*)) break;
+	    c -= sizeof(char*);
+	    *q++ = strp;			/* point ptrs to the strings */
+	    strp = p+1;				/* next string -> next char */
+	}
+    }
     *q = 0;					/* null ptr list terminator */
     return ret;
 }
