While this patch avoids the bug, it isn't quite right: pstat -ft should
be equivalent to "pstat -f && pstat -t" but...

$ pstat -ft
pstat: kvm_openfiles: /dev/mem: Permission denied

The actual problem is in ttymode(): if kd != NULL (which is the case if
-f is combined with -t), there are accesses of the kvm datastructures
without them being properly initialized:

879     if (kd != NULL)
880             KGET(TTY_NTTY, ntty);

and

896             KGET(TTY_TTYLIST, tty_head);
897             for (tp = TAILQ_FIRST(&tty_head); tp;
898                 tp = TAILQ_NEXT(&tty, tty_link)) {
899                     KGET2(tp, &tty, sizeof tty, "tty struct");
900                     tty2itty(&tty, &itty);
901                     ttyprt(&itty);
902             }

These code paths must not be hit unless need_nlist is true, so expose it
globally and replace 'kd != NULL' with the proper condition need_nlist

The following patch works in all affected cases, pstat -t, pstat -f,
pstat -tf, both as root and non-root as well as pstat -tv as root
(combinations with -d aren't interesting since pstat exits before
doing anything else but -d).

Index: pstat.c
===================================================================
RCS file: /var/cvs/src/usr.sbin/pstat/pstat.c,v
retrieving revision 1.108
diff -u -p -r1.108 pstat.c
--- pstat.c     14 Aug 2016 22:47:26 -0000      1.108
+++ pstat.c     17 Sep 2016 17:57:38 -0000
@@ -89,6 +89,7 @@ struct e_vnode {
        struct vnode vnode;
 };
 
+int    need_nlist;
 int    usenumflag;
 int    totalflag;
 int    kflag;
@@ -141,7 +142,7 @@ main(int argc, char *argv[])
        const char *dformat = NULL;
        extern char *optarg;
        extern int optind;
-       int i, need_nlist;
+       int i;
 
        hideroot = getuid();
 
@@ -869,7 +870,7 @@ ttymode(void)
        struct itty itty, *itp;
        size_t nlen;
 
-       if (kd == 0) {
+       if (!need_nlist) {
                mib[0] = CTL_KERN;
                mib[1] = KERN_TTYCOUNT;
                nlen = sizeof(ntty);
@@ -879,7 +880,7 @@ ttymode(void)
                KGET(TTY_NTTY, ntty);
        (void)printf("%d terminal device%s\n", ntty, ntty == 1 ? "" : "s");
        (void)printf("%s", hdr);
-       if (kd == 0) {
+       if (!need_nlist) {
                mib[0] = CTL_KERN;
                mib[1] = KERN_TTY;
                mib[2] = KERN_TTY_INFO;

Reply via email to