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

时间:2014-03-10 05:05:34

标签: linux performance assembly x86

我正在尝试在用户模式下执行特权指令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;
}

1 个答案:

答案 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 sigactionSEGV_MAPERRSEGV_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的原因,那么这个答案实在令人不满意。