core-util, cpu-x86: use __get_cpuid() instead of homegrown assembly
authorTanu Kaskinen <tanuk@iki.fi>
Wed, 22 Nov 2017 15:14:10 +0000 (17:14 +0200)
committerTanu Kaskinen <tanuk@iki.fi>
Wed, 6 Dec 2017 23:06:48 +0000 (01:06 +0200)
The get_cpuid() function in cpu-x86.c was buggy on x86-64. When building
without optimizations, the homegrown assembly code overwrote the
beginning of the function argument list on the stack. That happened to
work fine on regular x86-64, but caused crashing with the x32 ABI.

At least GCC and clang provide cpuid.h, which has the __get_cpuid()
function that can be used instead of the homegrown assembly.

The PA_REG_* constants can be removed as well, because they're not used
any more.

BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=103656
configure.ac
src/pulsecore/core-util.c
src/pulsecore/cpu-x86.c
src/pulsecore/cpu-x86.h

index 0c38fbb..013918f 100644 (file)
@@ -410,7 +410,7 @@ AC_SUBST([LIBLTDL])
 AC_HEADER_STDC
 
 # POSIX
-AC_CHECK_HEADERS_ONCE([arpa/inet.h glob.h grp.h netdb.h netinet/in.h \
+AC_CHECK_HEADERS_ONCE([arpa/inet.h cpuid.h glob.h grp.h netdb.h netinet/in.h \
     netinet/in_systm.h netinet/tcp.h poll.h pwd.h sched.h \
     sys/mman.h sys/select.h sys/socket.h sys/wait.h \
     sys/uio.h syslog.h sys/dl.h dlfcn.h linux/sockios.h])
index d4cfa20..64e9f21 100644 (file)
 #include <sys/personality.h>
 #endif
 
+#ifdef HAVE_CPUID_H
+#include <cpuid.h>
+#endif
+
 #include <pulse/xmalloc.h>
 #include <pulse/util.h>
 #include <pulse/utf8.h>
 #include <pulsecore/strbuf.h>
 #include <pulsecore/usergroup.h>
 #include <pulsecore/strlist.h>
-#include <pulsecore/cpu-x86.h>
 #include <pulsecore/pipe.h>
 #include <pulsecore/once.h>
 
@@ -3663,11 +3666,13 @@ bool pa_running_in_vm(void) {
 
     /* Both CPUID and DMI are x86 specific interfaces... */
 
-    uint32_t eax = 0x40000000;
+#ifdef HAVE_CPUID_H
+    uint32_t eax;
     union {
         uint32_t sig32[3];
         char text[13];
     } sig;
+#endif
 
 #ifdef __linux__
     const char *const dmi_vendors[] = {
@@ -3701,19 +3706,18 @@ bool pa_running_in_vm(void) {
 
 #endif
 
-    /* http://lwn.net/Articles/301888/ */
-    pa_zero(sig);
-
-    __asm__ __volatile__ (
-        /* ebx/rbx is being used for PIC! */
-        "  push %%"PA_REG_b"         \n\t"
-        "  cpuid                     \n\t"
-        "  mov %%ebx, %1             \n\t"
-        "  pop %%"PA_REG_b"          \n\t"
+#ifdef HAVE_CPUID_H
 
-        : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
-        : "0" (eax)
-    );
+    /* Hypervisors provide their signature on the 0x40000000 cpuid leaf.
+     * http://lwn.net/Articles/301888/
+     *
+     * XXX: Why are we checking a list of signatures instead of the
+     * "hypervisor present bit"? According to the LWN article, the "hypervisor
+     * present bit" would be available on bit 31 of ECX on leaf 0x1, and that
+     * bit would tell us directly whether we're in a virtual machine or not. */
+    pa_zero(sig);
+    if (__get_cpuid(0x40000000, &eax, &sig.sig32[0], &sig.sig32[1], &sig.sig32[2]) == 0)
+        return false;
 
     if (pa_streq(sig.text, "XenVMMXenVMM") ||
         pa_streq(sig.text, "KVMKVMKVM") ||
@@ -3722,8 +3726,9 @@ bool pa_running_in_vm(void) {
         /* http://msdn.microsoft.com/en-us/library/bb969719.aspx */
         pa_streq(sig.text, "Microsoft Hv"))
         return true;
+#endif /* HAVE_CPUID_H */
 
-#endif
+#endif /* defined(__i386__) || defined(__x86_64__) */
 
     return false;
 }
index a86c26d..4e59e14 100644 (file)
 
 #include <stdint.h>
 
+#ifdef HAVE_CPUID_H
+#include <cpuid.h>
+#endif
+
 #include <pulsecore/log.h>
 
 #include "cpu-x86.h"
 
-#if defined (__i386__) || defined (__amd64__)
-static void get_cpuid(uint32_t op, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) {
-    __asm__ __volatile__ (
-        "  push %%"PA_REG_b"   \n\t"
-        "  cpuid               \n\t"
-        "  mov %%ebx, %%esi    \n\t"
-        "  pop %%"PA_REG_b"    \n\t"
-
-        : "=a" (*a), "=S" (*b), "=c" (*c), "=d" (*d)
-        : "0" (op)
-    );
-}
-#endif
-
 void pa_cpu_get_x86_flags(pa_cpu_x86_flag_t *flags) {
-#if defined (__i386__) || defined (__amd64__)
+#if (defined(__i386__) || defined(__amd64__)) && defined(HAVE_CPUID_H)
     uint32_t eax, ebx, ecx, edx;
     uint32_t level;
 
     *flags = 0;
 
     /* get standard level */
-    get_cpuid(0x00000000, &level, &ebx, &ecx, &edx);
+    if (__get_cpuid(0x00000000, &level, &ebx, &ecx, &edx) == 0)
+        goto finish;
+
     if (level >= 1) {
-        get_cpuid(0x00000001, &eax, &ebx, &ecx, &edx);
+        if (__get_cpuid(0x00000001, &eax, &ebx, &ecx, &edx) == 0)
+            goto finish;
 
         if (edx & (1<<15))
           *flags |= PA_CPU_X86_CMOV;
@@ -80,9 +73,12 @@ void pa_cpu_get_x86_flags(pa_cpu_x86_flag_t *flags) {
     }
 
     /* get extended level */
-    get_cpuid(0x80000000, &level, &ebx, &ecx, &edx);
+    if (__get_cpuid(0x80000000, &level, &ebx, &ecx, &edx) == 0)
+        goto finish;
+
     if (level >= 0x80000001) {
-        get_cpuid(0x80000001, &eax, &ebx, &ecx, &edx);
+        if (__get_cpuid(0x80000001, &eax, &ebx, &ecx, &edx) == 0)
+            goto finish;
 
         if (edx & (1<<22))
           *flags |= PA_CPU_X86_MMXEXT;
@@ -97,6 +93,7 @@ void pa_cpu_get_x86_flags(pa_cpu_x86_flag_t *flags) {
           *flags |= PA_CPU_X86_3DNOW;
     }
 
+finish:
     pa_log_info("CPU flags: %s%s%s%s%s%s%s%s%s%s%s",
     (*flags & PA_CPU_X86_CMOV) ? "CMOV " : "",
     (*flags & PA_CPU_X86_MMX) ? "MMX " : "",
@@ -109,7 +106,7 @@ void pa_cpu_get_x86_flags(pa_cpu_x86_flag_t *flags) {
     (*flags & PA_CPU_X86_MMXEXT) ? "MMXEXT " : "",
     (*flags & PA_CPU_X86_3DNOW) ? "3DNOW " : "",
     (*flags & PA_CPU_X86_3DNOWEXT) ? "3DNOWEXT " : "");
-#endif /* defined (__i386__) || defined (__amd64__) */
+#endif /* (defined(__i386__) || defined(__amd64__)) && defined(HAVE_CPUID_H) */
 }
 
 bool pa_cpu_init_x86(pa_cpu_x86_flag_t *flags) {
index 30bbc80..eff2014 100644 (file)
@@ -43,20 +43,8 @@ bool pa_cpu_init_x86 (pa_cpu_x86_flag_t *flags);
 
 #if defined (__i386__)
 typedef int32_t pa_reg_x86;
-#define PA_REG_a "eax"
-#define PA_REG_b "ebx"
-#define PA_REG_c "ecx"
-#define PA_REG_d "edx"
-#define PA_REG_D "edi"
-#define PA_REG_S "esi"
 #elif defined (__amd64__)
 typedef int64_t pa_reg_x86;
-#define PA_REG_a "rax"
-#define PA_REG_b "rbx"
-#define PA_REG_c "rcx"
-#define PA_REG_d "rdx"
-#define PA_REG_D "rdi"
-#define PA_REG_S "rsi"
 #endif
 
 /* some optimized functions */