在 x86-64 CPU 上通过交叉修改代码重现意外行为

2024-05-07

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能够编写触发此问题的代码。

编写代码来执行此操作对我来说是一种个人好奇心。在生产代码中,我只是遵循规则,但我认为在重现这一点时我可能需要学习一些东西。


想象一个具有很长的处理器指令流水线 http://en.wikipedia.org/wiki/Instruction_pipeline其中寄存器和内存仅在最后一个流水线阶段修改。当您为此处理器编写自修改代码并修改内存中已存在于管道中的指令时,修改将无效。在这种情况下,程序的行为取决于处理器的管道有多长。

为了使具有更长管道的新处理器的行为与旧型号完全相同,英特尔处理器包含一种机制,可以在检测到这种情况时刷新(清空)管道。刷新后,修改后的代码将被提取到管道中,因此新处理器的行为与旧处理器完全相同。

序列化指令是刷新管道的另一种方法。当它到达管道末端时,管道被刷新并在串行化指令之后再次开始获取。

因此,勘误表本质上是说,某些处理器模型不会检查来自其他处理器的写入是否会覆盖已在其管道中执行的指令。该检查仅适用于本地写入,不适用于外部写入。但是,如果插入序列化指令,则会强制处理器刷新管道,一切都会按预期运行。

要重现勘误表中描述的行为,您需要确保从一个处理器修改的代码位于另一个处理器的管道内。看一下分支预测(决定哪个代码路径位于管道内)和同步原语。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 x86-64 CPU 上通过交叉修改代码重现意外行为 的相关文章

  • Haskell 中多核编程的现状如何?

    Haskell 中多核编程的现状如何 现在有哪些项目 工具和库可用 有哪些经验报道 2009年至2012年期间 发生了以下事件 2012 从 2012 年开始 并行 Haskell 状态更新开始出现在并行 Haskell 摘要 http w
  • 当一种语言是另一种语言的平行超集时,这意味着什么?

    我正在阅读关于实时并发 C 的期刊文章 http link springer com article 10 1007 2FBF00365999 并且它在摘要中提到 因此你们中的任何人都可以通过该链接查看上下文 Concurrent C 是
  • x86 asm 图形设置的分辨率高于 640x480?

    我刚刚开始使用汇编语言 感觉像学习新东西 并且遇到了一些问题 到目前为止 我一直在浏览的所有教程都没有回答 或者太旧而无法知道 1 我尝试了一些搜索 也许我只是不知道正确的关键字 但我找不到用于更改屏幕分辨率等的图形模式的更新列表 我发现的
  • 将以下机器语言代码(0x2237FFF1)翻译成MIPS汇编

    到目前为止我已经翻译了这段代码 但我不明白的是如何计算 计算 16 位立即地址的数量 0x2237FFF1 转为二进制 0010 0010 0011 0111 1111 1111 1111 0001 现在我正在读取操作码 001000 并知
  • 在后台线程上搜索

    我试图在 iPhone 应用程序中搜索数千个对象 但是搜索严重滞后 每次击键后 UI 都会冻结 1 2 秒 为了防止这种情况 我必须在后台线程上执行搜索 我想知道是否有人有一些在后台线程上搜索的提示 我读了一点NSOperation并在网上
  • 无论线程如何,对象是否总是能看到其最新的内部状态?

    假设我有一个带有简单整数计数变量的可运行对象 每次可运行对象运行时该变量都会递增 该对象的一个 实例被提交以在计划的执行程序服务中定期运行 class Counter implements Runnable private int coun
  • 如何构建gcc multilib工具链?

    我正在尝试在新安装的 ubuntu 14 04 的 AMD64 版本上构建 gcc multilib 工具链 它只有 x86 64 gcc 和 g 安装 没有 multilib 支持 我的配置行是 configure disable che
  • Java HashSet 是线程安全的只读吗?

    如果我通过 Collections unmodifyingSet 运行 HashSet 实例后 它是线程安全的吗 我问这个是因为 Set 文档声明它不是 但我只是执行读取操作 来自 Javadoc 请注意 此实现不是同步的 如果多个线程同时
  • INT 13h 无法读取超出特定扇区的数据

    我正在为我的操作系统编写内核 在将磁盘扇区加载到内存时遇到问题 以下是从磁盘加载扇区的函数代码部分 mov ax 0x3000 mov es ax mov ax 0x0201 mov bx word ptr bp 6 bx 0x000 0x
  • +=、|=、&= 等是原子的吗? [复制]

    这个问题在这里已经有答案了 修改 运算符是这样的吗 等等原子 I know 是原子的 如果你执行x 同时 在两个不同的线程中 你总是会得到x增加了 2 而不是x x 1优化关闭 我想知道是否variable constant 以及类似的东西
  • 如何在汇编中使用 ReadString?

    mov edx offset Prompt1 call WriteString mov ecx 32 mov edx offset String1 call ReadString 现在 我该如何访问String1 如何将其移入寄存器以便对其
  • Java 空值检查

    我有一个thread1 if object null object play 和另一个thread2可以写null into object随时参考 我将同时运行这些线程 我知道thread2可以重写object后参考null检查并会抛出Nu
  • 在现代 x86-64 上计算 64 位整数的整数 Log10 的最快方法是什么?

    标题 我找到了大量 32 位示例 但没有找到完整的 64 位示例 使用这个帖子 https codegolf stackexchange com questions 47290 fastest way to compute order of
  • 为什么 GCC 在堆栈上压入额外的返回地址?

    我目前正在学习汇编的基础知识 在查看 GCC 6 1 1 生成的指令时遇到了一些奇怪的情况 这是来源 include
  • Java 线程 JavaDoc

    我编写了一个只能在特定线程上调用的方法 是否应该将标准注释或注释添加到方法的 javadoc 中来表示这一点 不知道有任何这样的标准注释 Java 并发实践 http www javaconcurrencyinpractice com 在第
  • 微软怎么能说WinAPI中一个字的大小是16位呢?

    我刚刚开始学习WinAPI 在MSDN中 对WORD数据类型提供了以下解释 WORD16 位无符号整数 范围是十进制 0 到 65535 该类型在 WinDef h 中声明如下 typedef 无符号短 WORD 很简单 而且它与我一直在使
  • 使用 volatile bool 强制另一个线程等待是否安全? (C++)

    我读到的有关 volatile 的所有内容都说它永远不安全 但我仍然倾向于尝试它 而且我还没有看到这种特定场景被宣布为不安全 我有一个单独的线程来渲染场景 从主模拟线程中提取数据 这没有同步 并且工作正常 问题是 当程序退出时 渲染器需要停
  • shell脚本中是否有互斥/信号量机制?

    我正在 shell 脚本中寻找互斥 信号量 并发机制 考虑以下情况 除非 a 用户不关闭共享文件 否则 b 用户应该无法打开 更新它 我只是想知道如何在 shell 脚本中实现互斥量 信号量 临界区等 在 shell 脚本中实现锁定机制 文
  • 当内存排序放宽时,C++ 延迟会增加

    我在 Windows 7 64 位 VS2013 x64 发行版 上尝试内存排序 我想使用最快的同步来共享对容器的访问 我选择了原子比较和交换 我的程序产生两个线程 写入器推送到向量 读取器检测到这一点 最初我没有指定任何内存顺序 所以我假
  • 为什么 VC++ 编译器 MOV+PUSH args 而不是仅仅 PUSH 它们? x86

    在 VC 的反汇编中 正在进行函数调用 编译器在压入本地指针之前将其 MOV 到寄存器 memcpy nodeNewLocation pNode sizeCurrentNode 0041A5DA 8B 45 F8 mov eax dword

随机推荐