Question
对于可能在 x86 或 x86-x64 系统上触发意外行为的交叉修改代码有哪些想法,在这些系统中,交叉修改代码中的所有操作均已正确完成,但在执行处理器之前执行序列化指令除外修改代码?
如下所述,我有一个 Core 2 Duo E6600 处理器进行测试,它被明确提到是一个容易出现相关问题的处理器。我将在这台机器上测试与我分享的任何想法并提供更新。
背景
在 x86 和 x64 系统上,编写交叉修改代码的官方指南是执行以下操作:
; Action of Modifying Processor
Store modified code (as data) into code segment;
Memory_Flag ← 1;
; Action of Executing Processor
WHILE (Memory_Flag ≠ 1)
Wait for code to update;
ELIHW;
Execute serializing instruction; (* For example, CPUID instruction *)
Begin executing modified code;
对于某些处理器,在勘误表中明确提到了序列化指令的必要性。例如,Intel Core 2 Duo E6000 系列有以下勘误:(来自http://www.mathemainzel.info/files/intelX6800andintelE6000.pdf http://www.mathemainzel.info/files/intelX6800andintelE6000.pdf)
一个处理器或系统总线主控器将数据写入
当前正在执行第二个处理器的代码段,其目的是
让第二个处理器执行该数据作为代码称为
交叉修改代码 (XMC)。不强制第二个的XMC
处理器在执行之前执行同步指令
新代码称为非同步 XMC。
软件使用非同步XMC修改指令字节
处理器的流可能会出现意外或不可预测的执行
正在执行修改后的代码的处理器的行为。
有一些猜测是,如果不使用序列化指令,为什么会出现意外的执行行为?http://linux.kernel.narkive.com/FDc9TB0d/patch-linux-kernel-markers http://linux.kernel.narkive.com/FDc9TB0d/patch-linux-kernel-markers:
当 i-fetch 完成并且微操作处于跟踪中时
缓存然后原始之间不再有直接关联
机器指令边界和微操作。这是因为
优化。例如(出于说明目的而人造的):
移动eax,ebx
mov内存,eax
移动eax,1
(使用英特尔符号而不是 ATT - 习惯的力量)
在跟踪缓存中,不会有微操作来用 ebx 更新 eax。
动态地将“mov eax,ebx”更改为“mov ecx,ebx”会使
优化的跟踪缓存,因此唯一的资源是 GPF。如果
修改不会使跟踪缓存无效,然后没有 GPF。这
问题是:“当跟踪缓存发生时,我们能否预测情况?
没有被无效”,并且一般来说答案是否定的,因为
微架构不公开。但可以猜测,修改
带有中断指令的单字节操作码 - int3 - 不
造成无法处理的不一致。这就是英特尔
确认的。继续存储int3,无需同步
(即强制刷新跟踪缓存)。
还有更多信息https://sourceware.org/ml/systemtap/2005-q3/msg00208.html https://sourceware.org/ml/systemtap/2005-q3/msg00208.html:
当我们意识到这一点时,我与英特尔进行了长时间的讨论
微架构的家伙。事实证明,这个错误的原因
(顺便说一下,英特尔不打算修复)是因为跟踪
缓存 - 由指令产生的微指令流
解释 - 不能保证有效。之间的阅读
我认为这个问题的出现是因为在
跟踪缓存,不再可能识别原始的
指令边界。如果 CPU 发现跟踪缓存
由于不同步的交叉修改而已失效
指令执行将通过 GPF 中止。进一步讨论
与英特尔透露,用 int3 替换第一个操作码字节
不会受到此勘误的影响。
除了我在这里发布的内容之外,我在互联网上看到的关于这个问题的信息并不多。此外,我还没有发现任何公开的例子表明人们在 x86 和 x86-64 系统上使用交叉修改代码时因未能执行序列化指令而被咬。
我有一台运行 Intel Core 2 Duo E6600 处理器的计算机,该处理器被明确记录为容易出现此问题,并且我有not能够编写触发此问题的代码。
编写代码来执行此操作对我来说是一种个人好奇心。在生产代码中,我只是遵循规则,但我认为在重现这一点时我可能需要学习一些东西。