perf: x86: Fix GPF with bad events
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 26 Oct 2017 18:29:43 +0000 (14:29 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 26 Oct 2017 18:33:29 +0000 (14:33 -0400)
Userspace could give us a bad 'event' parameter with reserved bits set.
That would blow up write_msr() with a GPF.

I noticed this by looking at the code, but it turns out you can recreate
the bug in qemu by trying to use 'anythread' with a non-fixed counter.
e.g.:

perf stat -e TLB_FLUSH:t hello

will die.  Using -e cycles won't do it, since fixed counters were checking
the 'any thread' bit.  Hacking up perf and writing bits to the upper half
of the event / MSR would also trigger the GPF.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/x86/perfmon.c

index cab0f3d..41173bc 100644 (file)
@@ -138,7 +138,7 @@ static uint64_t perfmon_apply_fixevent_mask(uint64_t event, int idx,
                m |= (1 << 0);
        if (PMEV_GET_USR(event))
                m |= (1 << 1);
-       if (PMEV_GET_ANYTH(event) && (cpu_caps.perfmon_version >= 3))
+       if (PMEV_GET_ANYTH(event))
                m |= (1 << 2);
        if (PMEV_GET_INTEN(event))
                m |= (1 << 3);
@@ -588,6 +588,11 @@ int perfmon_open_event(const struct core_set *cset, struct perfmon_session *ps,
                perfmon_destroy_alloc(pa);
                nexterror();
        }
+       /* Ensure the user did not set reserved bits or otherwise give us a bad
+        * event.  pev (now pa->ev) must be a valid IA32_PERFEVTSEL MSR. */
+       pa->ev.event &= 0xffffffff;
+       if (cpu_caps.perfmon_version < 3)
+               PMEV_SET_ANYTH(pa->ev.event, 0);
        /* Ensure we're turning on the event.  The user could have forgotten to set
         * it.  Our tracking of whether or not a counter is in use depends on it
         * being enabled, or at least that some bit is set. */