用于C ++的侧通道抗性数学函数

时间:2017-10-11 18:05:42

标签: c++ security floating-point numerical-methods sgx

我正在处理一个处理秘密数据的SGX项目,在某些时候,我需要评估一个浮点数的自然对数。评估过程应该是旁道抗性的,这意味着它的运行时间和内存访问模式应与其输入和输出无关。

野外是否有这样的实施?问题是否已在文献中得到解决?

1 个答案:

答案 0 :(得分:4)

标记表明您的硬件平台是一个非常新的x86_64 Intel CPU,它也支持AVX2和FMA操作。在运行时和内存访问模式中不变的实现的关键是避免分支。如果编译器合作并将简单的条件赋值转换为适当的条件移动或混合指令,则下面logf()的实现应该可以正常工作。然而,依赖于编译器代码生成是脆弱的,并且在编译器资源管理器提供的各种编译器之外,我只能得到一些事情来传递close to the desired result,除了一个之外所有分支都被转换({{1的条件赋值在非正常输入的处理中。)

因此,您可能需要通过适当的指令而不是分支代码来执行手动工作来强制执行结果选择,例如通过使用内在函数。

正如EOF在注释中指出的那样,消除分支是必要但不充分的条件,因为单个浮点运算也可以具有变量运行时,即使它们只是加,乘和FMA。这对于在速度上处理诸如次正规(通常称为非正规)之类的特殊操作数的体系结构来说不是问题,例如,图形处理器。但是,这是我使用过的x86处理器上的一个问题。通常,最严重的可变性是由于非正规结果而发生的,而来自非正规源操作数的影响要小得多。

下面显示的代码包含使用原始参数a = t作为源操作数的多个操作,这会导致由于非正规输入而导致运行时变化的风险。运行时的潜在变化是否超过噪声水平(例如由于在调用函数时管道状态的变化)应该针对打算部署代码的特定平台进行仔细测试。

a

通过在对数计算中执行特殊情况处理和通过基于便携式整数的代码进行结果选择,可以解决上面确定的潜在问题。明显的权衡是性能损失。处理非正规参数需要基于前导零(CLZ)的计数进行归一化。虽然x86处理器有这方面的说明,但它们可能无法通过C ++以便携方式访问。但是可以以直接的方式构造具有不变运行时的可移植实现。这导致了一个无分支的实现,我期望它与大多数编译器一起工作,但是仔细检查生成的机器代码将是必不可少的。我曾经使用Compiler Explorer来验证它是否按照gcc

的要求进行编译
#include <cstdint>
#include <cstring>
#include <cmath>

int __float_as_int (float a)
{
    int r;
    memcpy (&r, &a, sizeof(r));
    return r;
}

float __int_as_float (int a)
{
    float r;
    memcpy (&r, &a, sizeof(r));
    return r;
}

/* maximum error 0.85417 ulp */
float my_logf (float a)
{
    float m, r, s, t, i, f, u;
    int32_t e;

    /* result for exceptional cases */
    u = a + a;  // silence NaNs if necessary
    if (a  < 0.0f) u =  0.0f / 0.0f; //  NaN
    if (a == 0.0f) u = -1.0f / 0.0f; // -Inf

    /* result for non-exceptional cases */
    i = 0.0f;

    /* fix up denormal input if needed */
    t = a * 8388608.0f;
    if (a < 1.17549435e-38f) {
        a = t;
        i = -23.0f;
    }

    /* split argument into exponent and mantissa parts */
    e = (__float_as_int (a) - 0x3f2aaaab) & 0xff800000;
    m = __int_as_float (__float_as_int (a) - e);
    i = fmaf ((float)e, 1.19209290e-7f, i);

    /* m in [2/3, 4/3] */
    f = m - 1.0f;
    s = f * f;
    /* Compute log1p(f) for f in [-1/3, 1/3] */
    r =             -0.130310059f; 
    t =              0.140869141f; 
    r = fmaf (r, s, -0.121489234f);
    t = fmaf (t, s,  0.139809728f);
    r = fmaf (r, s, -0.166844666f);
    t = fmaf (t, s,  0.200121239f);
    r = fmaf (r, s, -0.249996305f);
    r = fmaf (t, f, r);
    r = fmaf (r, f,  0.333331943f);
    r = fmaf (r, f, -0.500000000f);
    r = fmaf (r, s, f);
    r = fmaf (i, 0.693147182f, r); // log(2) 

    /* late selection between exceptional and non-exceptional result */
    if (!((a > 0.0f) && (a <= 3.40282347e+38f))) r = u;

    return r;
}