Subject: pstat -f broken on sparc
To: NetBSD tech-userlevel mailing list <tech-userlevel@netbsd.org>
From: Julian Coleman <jdc@coris.org.uk>
List: tech-userlevel
Date: 02/18/2004 15:01:09
If you run `pstat -f` on sparc, you will get a bus error because it tries
to access an 8 byte variable on a 4 byte address.  It looks like it has
been broken for quite a while (I checked back 15 months).

The problem is in getfiles() in pstat.c where it calls sysctl() with the
KERN_FILE mib.  This fills in a buffer with a single (struct filelist)
followed by a number of (struct file)'s.  The size of (struct filelist) is
4 bytes, so the following (struct file)'s are aligned on a 4 byte boundary.
However, because they contain an 8 byte variable (f_offset - an off_t),
they need to be aligned on an 8 byte boundary.

It seems to me that the real fix is don't return a buffer containing
different kind of structs like this.  Alternatively, a workround is
appended.  Comments?

J

  - - 8< - - - - - - - - - - - - - Cut here - - - - - - - - - - - - - >8 - -
--- /usr/src/usr.sbin/pstat/pstat.c	2003-12-21 03:12:59.000000000 +0000
+++ pstat.c	2004-02-18 09:40:49.000000000 +0000
@@ -154,7 +154,7 @@
 #endif
 
 void	filemode __P((void));
-int	getfiles __P((char **, int *));
+int	getfiles __P((char **, int *, int *));
 int	getflags __P((const struct flagbit_desc *, char *, u_int));
 struct mount *
 	getmnt __P((struct mount *));
@@ -879,7 +879,7 @@
 	struct file *addr;
 	char flags[sizeof(filemode_flags) / sizeof(filemode_flags[0])];
 	char *buf;
-	int len, maxfile, nfile, ovflw;
+	int len, offset, maxfile, nfile, ovflw;
 	static const char * const dtypes[] =
 		{ "???", "inode", "socket", "pipe" };
 
@@ -889,22 +889,22 @@
 		(void)printf("%3d/%3d files\n", nfile, maxfile);
 		return;
 	}
-	if (getfiles(&buf, &len) == -1)
+	if (getfiles(&buf, &len, &offset) == -1)
 		return;
 	/*
 	 * Getfiles returns in malloc'd memory a pointer to the first file
 	 * structure, and then an array of file structs (whose addresses are
 	 * derivable from the previous entry).
 	 */
-	addr = ((struct filelist *)buf)->lh_first;
-	fp = (struct file *)(buf + sizeof(struct filelist));
+	addr = ((struct filelist *)buf + offset)->lh_first;
+	fp = (struct file *)(buf + offset + sizeof(struct filelist));
 	nfile = (len - sizeof(struct filelist)) / sizeof(struct file);
 
 	(void)printf("%d/%d open files\n", nfile, maxfile);
 	(void)printf("%*s%s%*s TYPE    FLG     CNT  MSG  %*s%s%*s  OFFSET\n",
 	    (PTRSTRWIDTH - 4) / 2, "", " LOC", (PTRSTRWIDTH - 4) / 2, "",
 	    (PTRSTRWIDTH - 4) / 2, "", "DATA", (PTRSTRWIDTH - 4) / 2, "");
-	for (; (char *)fp < buf + len; addr = fp->f_list.le_next, fp++) {
+	for (; (char *)fp < buf + offset + len; addr = fp->f_list.le_next, fp++) {
 		if ((unsigned)fp->f_type > DTYPE_PIPE)
 			continue;
 		ovflw = 0;
@@ -916,7 +916,7 @@
 		PRWORD(ovflw, " %*d", 5, 1, fp->f_msgcount);
 		PRWORD(ovflw, "  %*lx", PTRSTRWIDTH + 1, 2, (long)fp->f_data);
 		if (fp->f_offset < 0)
-			PRWORD(ovflw, "  %-*llx\n", PTRSTRWIDTH + 1, 2,
+			PRWORD(ovflw, "  %-*lld\n", PTRSTRWIDTH + 1, 2,
 			    (long long)fp->f_offset);
 		else
 			PRWORD(ovflw, "  %-*lld\n", PTRSTRWIDTH + 1, 2,
@@ -926,11 +926,13 @@
 }
 
 int
-getfiles(abuf, alen)
+getfiles(abuf, alen, aoffset)
 	char **abuf;
 	int *alen;
+	int *aoffset;
 {
 	size_t len;
+	int offset;
 	int mib[2];
 	char *buf;
 
@@ -947,14 +949,16 @@
 		warn("sysctl: KERN_FILE");
 		return (-1);
 	}
-	if ((buf = malloc(len)) == NULL)
+	offset = len % sizeof(off_t);	/* Need to align (struct file *). */
+	if ((buf = malloc(len + offset)) == NULL)
 		err(1, "malloc");
-	if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) {
+	if (sysctl(mib, 2, buf + offset, &len, NULL, 0) == -1) {
 		warn("sysctl: KERN_FILE");
 		return (-1);
 	}
 	*abuf = buf;
 	*alen = len;
+	*aoffset = offset;
 	return (0);
 }
 
  - - 8< - - - - - - - - - - - - - Cut here - - - - - - - - - - - - - >8 - -
-- 
  My other computer also runs NetBSD    /        Sailing at Newbiggin
        http://www.netbsd.org/        /   http://www.newbigginsailingclub.org/