为什么segfault而不是特权指令错误?

我试图在用户模式下执行特权指令rdmsr ,我希望得到某种特权错误,但是我得到一个segfault。 我已经检查了这个asm并且根据手册 (1171页),将PERFEVTSEL0加载到了ecx ,这应该是PERFEVTSEL0

段错误的原因是什么,如何修改下面的代码来修复它?

我想在黑客攻击一个内核模块之前解决这个问题,因为我不希望这个segfault炸毁我的内核。

更新:我在Intel(R) Xeon(R) CPU X3470

 #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <inttypes.h> #include <sched.h> #include <assert.h> uint64_t read_msr(int ecx) { unsigned int a, d; __asm __volatile("rdmsr" : "=a"(a), "=d"(d) : "c"(ecx)); return ((uint64_t)a) | (((uint64_t)d) << 32); } int main(int ac, char **av) { uint64_t start, end; cpu_set_t cpuset; unsigned int c = 0x186; int i = 0; CPU_ZERO(&cpuset); CPU_SET(i, &cpuset); assert(sched_setaffinity(0, sizeof(cpuset), &cpuset) == 0); printf("%lu\n", read_msr(c)); return 0; } 

我将试图回答的问题:为什么上面的代码会导致SIGSEGV而不是SIGILL ,虽然代码没有内存错误,但是一个非法的指令(从非特权用户速度调用的特权指令)?


我希望得到一个SIGILLsi_code ILL_PRVOPC而不是一个段错误,也是。 你的问题目前是3岁,今天我偶然发现了同样的行为。 我也很失望:-(


段错误的原因是什么?

原因似乎是Linux内核代码决定发送SIGSEGV 。 这里是负责任的功能: http : //elixir.free-electrons.com/linux/v4.9/source/arch/x86/kernel/traps.c#L487看看功能的最后一行。

在你的后续问题中 ,你得到了其他汇编指令的列表,它们被传播为用户空间的SIGSEGV ,尽管它们实际上是一般的保护错误。 我发现你的问题,因为我触发了与cli的行为。

以及如何修改下面的代码来修复它?

从Linux内核4.9开始,我不知道有什么可靠的方法来区分内存错误(我希望是SIGSEGV )和用户空间的特权指令错误。

这些案件可能是非常黑客和不可移动的。 当特权指令导致SIGSEGVsiginfo_t si_code被设置为一个值,该值不会直接在man 2 sigactionSIGSEGV部分中列出。 记录的值是SEGV_MAPERRSEGV_ACCERRSEGV_PKUERR ,但我得到我系统上的SI_KERNEL (0x80)。 根据手册页, SI_KERNEL是一个“可以放在任何信号的si_code中”的代码。 在strace中,您会看到SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} 。 负责的内核代码在这里 。

这个字符串也可以用grep dmesg

请永远不要使用这两种方法来区分生产系统中的GPF和内存错误。

你的代码的具体解决方案:只是不要从用户空间运行rdmsr 。 但是,如果你正在寻找一个通用的方法来弄清楚为什么一个程序收到一个SIGSEGV这个答案是非常令人不满意的。