代码之家  ›  专栏  ›  技术社区  ›  PiRocks

当机器代码中的直接操作数安全执行时,对该操作数的未对齐写入是否安全?

  •  1
  • PiRocks  · 技术社区  · 2 年前

    假设我有这样的x86-64代码(尽管这个问题更普遍地适用于所有代码):

    mov rbx,7F0140E5247Dh
    jmp rbx
    

    如果目标值未对齐,而代码可能正在执行,那么覆盖目标常量是否安全?换句话说,我能观察到一个部分更新的跳转目标,导致跳转到不存在的地址吗?此外,如果目标常量跨越页面或缓存线边界,这是否安全?

    编辑:

    我只对更改单个指令感兴趣,而不更改指令边界位置。

    1 回复  |  直到 2 年前
        1
  •  3
  •   Peter Cordes    2 年前

    只有当写入是原子的,这是有保证的 with unaligned qword writes on Intel as long as it doesn't span a cache-line boundary ,但AMD不能保证。最低公分母原子性保证是8字节对齐的存储是原子的,仅此而已。

    使用 xchg 做一个有保证的原子RMW。如果常数本身跨越缓存线边界,速度会非常慢,但我相信这是正确的。(总线锁,而不仅仅是缓存锁;速度太慢了,即使只是用于拆分,也有一个性能计数器。)- lock ,甚至是一个CPU特性,至少在内核代码中会出现这种错误,这样你就可以在虚拟机中找到它的实例。)如果常数不跨越任何CPU的问题边界,那么它应该和对齐的原子操作一样快。

    或者,如果您的CPU支持AVX,16字节对齐的SSE/AVX存储保证在带有AVX的CPU上实现原子存储。(经过多年的实践证明,这基本上是安全的,但幸运的是,它可以追溯到所有AVX CPU,没有新的功能位。)所以,如果你能让常数对齐,不跨越16字节的边界,你可以这样更新它。(用自身覆盖周围的字节不会导致问题,除非另一个线程也在附近更新另一个常量。)

    如果性能对这一点很重要(例如,每分钟做一次以上),那么可能值得使用一些填充或NOP来对齐常量8字节,尤其是如果可以的话 just lengthen earlier instructions 不需要真正的NOP,甚至 mov r64,imm64 它本身(虽然它是10字节,一条指令的最大长度是15。)


    完全概括为替换多个指令

    但如果你尊重这个限制,交叉修改代码是非常安全的。我认为Windows热补丁会让其他可能正在运行代码的线程停止运行,但我不知道为什么,因为它已经确保有一条足够大的指令可以覆盖。要么是他们过于谨慎,要么是代码获取不尊重存储原子性存在一些我不知道的风险。也许只是他们不想在函数未对齐的情况下依赖于2字节存储原子性,甚至认为这是默认的,因为正常的编译器设置有不同的原因。