首页 > 解决方案 > arm 编译器 5 不完全尊重 volatile 限定符

问题描述

考虑以下代码:

volatile int status;

status = process_package_header(&pack_header, PACK_INFO_CONST);

if ((((status) == (SUCCESS_CONST)) ? ((random_delay() && ((SUCCESS_CONST) == (status))) ? 0 : side_channel_sttack_detected()) : 1))
{
    ...
}

生成此机器代码(使用工具链的objdump生成):

  60:   f7ff fffe       bl      0 <process_package_header>
  64:   9000            str     r0, [sp, #0]     /* <- storing to memory as status is volatile */
  66:   42a0            cmp     r0, r4           /* <- where is the load before compare? status is volatile, it could have change between the last store instruction (above line) and now */
  68:   d164            bne.n   134 <func+0x134>
  6a:   f7ff fffe       bl      0 <random_delay>

现在,由于它是易失的,当到达语句status时它应该已经从内存中读取。if我希望在将其 ( cmp) 与进行比较之前看到一些加载命令SUCCESS_CONST,不管它被分配了函数的返回值process_package_header()并存储在内存中,因为status它是易失性的,并且可能在str指令和cmp指令之间进行更改。

请尝试忽略if条件的动机,其目的是尝试检测对 CPU 的物理攻击,其中条件标志和寄存器可以通过物理设备从外部更改。

工具链ARM DS-5_v5.27.0 arm 编译器:ARMCompiler5.06u5 (armcc)

目标是 ARM CortexM0+ CPU

标签: carmvolatilearmcc

解决方案


从C11 6.7.3/7volatile开始,管理对象的主要规则如下:

引用此类对象的任何表达式都应严格按照抽象机的规则进行评估,如 5.1.2.3 中所述。此外,在每个序列点,最后存储在对象中的值应与抽象机规定的值一致,除非由前面提到的未知因素修改。

它继续说

构成对具有 volatile 限定类型的对象的访问是实现定义的。

,这适用于如何解释其他规则(例如,在 5.1.2.3 中)。您的编译器的用户指南讨论了 volatile accesses 的详细信息,但那里似乎没有什么令人惊讶的地方。5.1.2.3节本身主要讲的是排序规则;评估表达式的规则在别处(但在访问 volatile 对象时仍必须遵循给定的规则)。

以下是抽象机行为的相关细节:

  1. 赋值操作具有将值存储在由 标识的对象中的副作用status。该语句的末尾有一个序列点,所以

    • 在执行后续语句中出现的任何评估之前应用副作用,并且
    • 因为status是易失的,所以该行表示的赋值是程序在序列点之前执行的最后一次写入。status
  2. if接下来评估语句中的条件表达式,其中

    • (status) == (SUCCESS_CONST)任何其他子表达式之前首先计算子表达式。
    • 评估status发生在==操作评估之前,以及
    • 采用将该标识符转换为存储在它标识的对象中的值的形式(左值转换,根据第 6.3.2.1/2 段)。
    • 为了对当时存储 status的值执行任何操作,必须首先读取该值。

该标准要求 volatile 对象驻留在可寻址存储中,因此原则上,您的 volatile 自动变量可以专门分配给寄存器。在这种情况下,只要使用该对象的机器指令直接从其寄存器读取其值或直接对其寄存器进行更新,就不需要单独的加载或存储来实现适当的易失性语义。但是,您的特定对象似乎不属于此类别,因为您生成的程序集中的存储指令似乎表明它确实与内存中的某个位置相关联。

此外,如果程序为分配给寄存器的对象正确实现了易失语义,则该寄存器必须是 r0。我不熟悉这种汇编语言的细节和运行代码的处理器,但它肯定看起来不像 r0 是这种存储的可行轨迹。

在这种情况下,我同意应该从内存中读回它,如果需要评估它在条件表达式中的第二次出现,status则应该再次从内存中读回它。这是抽象机器的行为,符合实现对所有易失性访问都表现出这种行为。那么,我的分析是,您的实现在这方面是不合格的,我倾向于将其报告为错误。

至于解决方法,我认为最好的办法是在汇编中编写重要的位 - 如果您的实现支持内联汇编,或者如果需要,作为在汇编中实现的完整函数。


推荐阅读