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

mer*_*011 5 linux performance x86 assembly

我试图rdmsr在用户模式下执行特权指令,我希望得到某种特权错误,但我得到一个段错误.我检查了asm,我装0x186ecx,这被认为是PERFEVTSEL0,基于上手动,1171页.

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;
}
Run Code Online (Sandbox Code Playgroud)

cor*_*rny 4

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


我也希望得到一个SIGILLwithsi_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)和来自用户空间的特权指令错误。

可能有一种非常老套且不可移植的方法来区分这些情况。当特权指令导致 a 时SIGSEGV, 会被设置为部分siginfo_t si_code中未直接列出的值。记录的值为, , ,但我在系统上得到 (0x80)。根据手册页,这是一个代码“可以放置在任何信号的 si_code 中”。在 strace 中,你会看到. 负责的内核代码在这里SIGSEGVman 2 sigactionSEGV_MAPERRSEGV_ACCERRSEGV_PKUERRSI_KERNELSI_KERNELSIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0}

也可以通过 grep 查找dmesg字符串

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

针对您的代码的具体解决方案:只是不要rdmsr从用户空间运行。但如果您正在寻找一种通用方法来找出程序收到SIGSEGV.