cpuid faulting is a feature made available after intel ivy bridge CPUs, Linux kernel support have been pushed by rr
,
via arch_prctl(ARCH_SET_CPUID)
[1]. It allows user space programs simulate CPUID instruction, by trapping cpuid instruction to SEGSEGV.
This is done by query PLATFORM_INFO
(0xce) MSR, bit-31. When bit-31 is set, cpuid_fault
can be done by write/clear MISC_FEATURES_ENABLES
, bit-0.
On an AWS EC2 t2.micro (virtualized) instance, cpuid_fault
is present:
$ sudo modprobe msr
$ sudo rdmsr -x 0xce
20080c33f3811800 # bit-31 is set.
And can be confirmed by cat /proc/cpuinfo as well:
$ cat /proc/cpuinfo | grep cpuid_fault
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm cpuid_fault invpcid_single pti fsgsbase bmi1 avx2 smep bmi2 erms invpcid xsaveopt
However, below program doesn’t work as expected:
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/prctl.h>
#include <asm/prctl.h>
#include <assert.h>
#include <unistd.h>
#include <stdio.h>
#include <cpuid.h>
#include <signal.h>
#include <string.h>
int arch_prctl(int code, unsigned long addr);
static void sigsegv_action(int signo, siginfo_t* siginfo, void* ucontext) {
const char msg[] = "got expected sigsegv\n";
write(STDOUT_FILENO, msg, sizeof(msg));
_exit(0);
}
int main(int argc, char* argv[])
{
assert(arch_prctl(ARCH_SET_CPUID, 0) == 0);
unsigned long eax, ebx, ecx, edx;
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_flags = SA_ONESHOT | SA_RESTART | SA_SIGINFO | SA_RESETHAND;
sa.sa_sigaction = sigsegv_action;
assert(sigaction(SIGSEGV, &sa, NULL) == 0);
// should segfault
__cpuid(0x1, eax, ebx, ecx, edx);
printf("eax=%lx\n", eax);
return 0;
}
The expected result is it should segfault (print got expected sigsegv), but on t2.micro
, the program prints eax=306f2
instead. It implies the kernel indeed received the arch_prctl
request, and set the hardware register, but the HVM (nitro
??) somehow ignored the write to MISC_FEATURES_ENABLES
, bit-0.
More details can be found in this[2] application note.
At first I thought it might be an AWS kernel (optimization) issue, hence tried both on kernel: 4.15.0-1051-aws and 4.15.0-65-generic, but the results were the same.
More information about the system setup:
AWS ec2 t2.micro stock ubuntu 18.04
[1] Add arch_prctl for controlling CPUID instruction
[2] virtualization technology flexmigration application note