C 保护x86-64上的内存

C 保护x86-64上的内存,c,linux,assembly,x86-64,C,Linux,Assembly,X86 64,我正在实现一个编译器,它将模拟处理器的指令集编译成x86指令。不存在在待模拟指令集上运行的物理处理器;只有x86上的模拟。在执行编译后的机器代码(模拟)时,我希望确保只读取或写入为模拟指定的内存。这有两个目的:1)指定内存区域之外的访问可能意味着我的编译器中有一个bug。我想找到这样的虫子。2) 在指定内存区域之外的访问也可能意味着我编译的源指令有逻辑错误,因此尝试访问模拟中不存在的内存地址,因此应该引发错误 在更简单的形式中,您可以想象我的代码如下所示: void simulate(char*

我正在实现一个编译器,它将模拟处理器的指令集编译成x86指令。不存在在待模拟指令集上运行的物理处理器;只有x86上的模拟。在执行编译后的机器代码(模拟)时,我希望确保只读取或写入为模拟指定的内存。这有两个目的:1)指定内存区域之外的访问可能意味着我的编译器中有一个bug。我想找到这样的虫子。2) 在指定内存区域之外的访问也可能意味着我编译的源指令有逻辑错误,因此尝试访问模拟中不存在的内存地址,因此应该引发错误

在更简单的形式中,您可以想象我的代码如下所示:

void simulate(char* designated_memory, size_t len) {
    // code intended to access *designated_memory till *(designated_memory + len - 1) only
}
restrict_access_to(designated_memory, designated_memory + len - 1);
simulate(designated_memory, len);
remove_access_restriction();
在x86-64和/或linux上是否有办法强制执行
simulate()
只能访问其自己的堆栈和
指定的内存
,任何其他访问都会产生错误。例如,代码可能如下所示:

void simulate(char* designated_memory, size_t len) {
    // code intended to access *designated_memory till *(designated_memory + len - 1) only
}
restrict_access_to(designated_memory, designated_memory + len - 1);
simulate(designated_memory, len);
remove_access_restriction();
用C语言编写一个解决方案就好了;asm也很好

更新

根据Jester的评论,我来尝试一下:

    #include <stdio.h>
    #include <unistd.h>
    #include <malloc.h>
    #include <sys/mman.h>

    int main() {
        size_t pagesize = sysconf(_SC_PAGESIZE);

        printf("pagesize...........: %lu\n", pagesize);

        char* m;
        size_t len = 12345;
        len = (len + pagesize - 1) / pagesize * pagesize;
        posix_memalign(&m, pagesize, len);

        printf("page aligned memory: %lx - %lx\n", (unsigned long) m, (unsigned long) m + len);

        printf("protecting 0 till m..."); fflush(stdout);
        mprotect(0, (size_t) m, PROT_NONE);
        printf("done\n");

        printf("protecting (m + len) till ?..."); fflush(stdout);
        mprotect(m + len, 0x7fffffff, PROT_NONE);
        printf("done\n");

        printf("trying to modify memory..."); fflush(stdout);
        *(m - 1000) = 5;
        printf("done: %i\n", *(m - 1000));

        free(m);
    }
我认为这表明在允许的区域之外修改数据仍然有效,但这不应该发生

我不用翻译。我使用编译器将模拟>指令编译为x86指令。出于性能原因,我不想在编译x86指令时包含所有范围检查>

因此,您的任务非常类似于
qemu
,当它在
x86
上进行模拟时,例如
ARM
,它将
ARM
指令转换为
x86
指令, 所以我建议您看看qemu的源代码:

下一个与您的项目非常相似的是
valrgrind
valgrind
的工作方式与您的工作方式非常相似: 它在某种虚拟
cpu
上执行程序来检查内存访问, 为了加快速度,它使用了
jit
()

最后一个开源项目,它解决了与您的类似的问题

是的,它是插入指令的代码,但结果比valgrind快得多, 如何在生成的代码中插入指令,但将性能保持在适当水平的想法,您可以在本视频中找到关于asan的
internal:

我不用翻译。我使用编译器将模拟>指令编译为x86指令。出于性能原因,我不想在编译x86指令时包含所有范围检查>

因此,您的任务非常类似于
qemu
,当它在
x86
上进行模拟时,例如
ARM
,它将
ARM
指令转换为
x86
指令, 所以我建议您看看qemu的源代码:

下一个与您的项目非常相似的是
valrgrind
valgrind
的工作方式与您的工作方式非常相似: 它在某种虚拟
cpu
上执行程序来检查内存访问, 为了加快速度,它使用了
jit
()

最后一个开源项目,它解决了与您的类似的问题

是的,它是插入指令的代码,但结果比valgrind快得多, 如何在生成的代码中插入指令,但将性能保持在适当水平的想法,您可以在本视频中找到关于asan的
internal:


如果您在用户空间代码中执行任意汇编指令,实际上并没有办法强制执行内存保护——内存保护通常需要能够激活CPU上的内核模式标志

但是,由于您正在编写模拟器,并将另一种语言编译为您选择的一组汇编指令,因此您有一个不同的选择:控制要编译的指令。与其为模拟内存访问发出原始内存访问指令,不如让模拟编译器将访问内存的指令替换为调用您自己设计的内存访问函数的指令,并在该函数中实现内存访问保护。这也可以通过让内存区域类似于
std::vector
来实现

请注意,仅仅用自己的move函数替换普通的move指令并不足以保护自己的包装器代码免受“模拟”代码的攻击。如果您的模拟发出原始跳转命令,那么它可能会被跳转到模拟之外的模拟代码破坏。或者通过精心规划的push/pop指令,这些指令会导致包装器代码稍后在您应该离开模拟之后返回到其他模拟器代码

要真正做到(某种程度上)安全(这绝非详尽无遗,或保证足以使模拟代码安全),您需要确保您的模拟编译器生成安全代码;影响内存访问的内容,包括mov、jump、push、pop、call和ret,需要用安全执行等效操作的函数调用来代替,而不仅仅是执行您不信任的汇编代码


您还需要确保将其包装在代码中,以便为外部程序保存寄存器,因为内部代码可能会任意更改其内容。

如果您在用户空间代码中执行任意汇编指令,实际上并没有一种方法可以强制执行内存保护——内存保护通常需要能够激活CPU上的内核模式标志

但是,由于您正在编写模拟器,并将另一种语言编译为您选择的一组汇编指令,因此您有一个不同的选择:控制要编译的指令。而不是发出原始内存