我试图在用户模式下执行特权指令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
,虽然代码没有内存错误,但是一个非法的指令(从非特权用户速度调用的特权指令)?
我希望得到一个SIGILL
与si_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
)和用户空间的特权指令错误。
这些案件可能是非常黑客和不可移动的。 当特权指令导致SIGSEGV
, siginfo_t
si_code
被设置为一个值,该值不会直接在man 2 sigaction
的SIGSEGV
部分中列出。 记录的值是SEGV_MAPERR
, SEGV_ACCERR
, SEGV_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
这个答案是非常令人不满意的。