我正在尝试在用户模式下执行特权指令rdmsr
,我希望得到某种特权错误,但我得到了一个段错误。我已经检查了asm
,我将0x186
加载到ecx
,该版本应该是PERFEVTSEL0
,基于manual,第1171页。
段错误的原因是什么,如何修改下面的代码来修复它?
我想在破解内核模块之前解决这个问题,因为我不希望这个段错误炸毁我的内核。
更新:我正在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;
}
答案 0 :(得分:4)
我将尝试回答的问题:为什么上面的代码导致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
看看函数的最后一行。
在your follow up question中,您获得了其他汇编指令的列表,这些汇编指令作为SIGSEGV
传播到用户空间,尽管它们实际上是一般性保护错误。我发现了您的问题,因为我使用cli
触发了行为。
如何修改下面的代码来修复它?
从Linux内核4.9开始,我不知道在内存错误(我希望是SIGSEGV
)和特权之间区分的任何可靠方法来自用户空间的指令错误。
可能有非常黑客和不可移植的方式来解决这些案件。当特权指令导致SIGSEGV
时,siginfo_t
si_code
设置为未直接列在SIGSEGV
的{{1}}部分中的值。记录的值为man 2 sigaction
,SEGV_MAPERR
,SEGV_ACCERR
,但我的系统上有SEGV_PKUERR
(0x80)。根据手册页,SI_KERNEL
是一个代码&#34;它可以放在si_code中,用于任何信号&#34;。在strace中,您会看到SI_KERNEL
。负责的内核代码是here。
还可以为this字符串grep SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0}
。
请永远不要使用这两种方法来区分生产系统上的GPF和内存错误。
代码的具体解决方案:不要从用户空间运行dmesg
。但是,如果您正在寻找一种通用的方法来弄清楚程序收到rdmsr
的原因,那么这个答案实在令人不满意。