首页 > 解决方案 > SIDT 指令在 Linux 用户空间进程中返回错误的基地址

问题描述

我制作了以下 x86-64 程序来查看中断描述符表的基地址从哪里开始:

#include <stdio.h>
#include <inttypes.h>

typedef struct __attribute__((packed)) {
        uint16_t limit;
        uint64_t base;
}idt_data_t;

static inline void store_idt(idt_data_t *idt_data)
{
        asm volatile("sidt %0":"=m" (*idt_data));
}

int main(void)
{
        idt_data_t idt_data;

        store_idt(&idt_data);

        printf("IDT Limit : 0x%X\n", idt_data.limit);
        printf("IDT Base  : 0x%lX\n", idt_data.base);

        return 0;
}

它打印以下内容:

IDT Limit : 0xFFF
IDT Base  : 0xFFFFFE0000000000

基地址似乎不正确,因为地址应该始终是物理地址,对吗?

另外,我不确定,但限制似乎太高了。我究竟做错了什么?

标签: linuxassemblyx86-64interruptinstructions

解决方案


这是一个线性地址,不一定是物理地址。换句话说,它像大多数其他地址一样受页表的影响。它必须位于永远不会分页到磁盘的页面中——如果不是,它将无法处理页面错误——但它可以位于物理上与虚拟地址不同的地址中。

在 x86-64 上,IDT 的每个条目是 16 字节长。有 256 个中断向量。256 * 16 = 4096 = 0x1000。IDTR 限制是“小于或等于”检查,因此通常使用 0xFFF。

SIDT如果操作系统启用了某个功能,则它是较新 CPU 上的特权指令,因此建议不要在用户模式下使用它,除非您正在编写利用 PoC 或其他东西。操作系统可能会谎报答案而不是抛出异常,但我不知道。


推荐阅读