看完这个讨论后,我意识到我几乎完全误解了这件事:)
由于C++抽象机器的描述不够严格(例如,与JVM规范进行比较),如果无法得到准确的答案,我宁愿希望得到关于合理"好"的规则的非正式澄清(非恶意) )实施应遵循.
标准中关于实现自由的第1.9部分的关键概念被称为as-if规则:
只要可以从程序的可观察行为中确定,结果就好像符合要求一样,实施可以自由地忽略本标准的任何要求.
根据标准(I引用n3092),术语"可观察行为"表示以下内容:
- 严格根据抽象机的规则来评估对易失性对象的访问.
- 在程序终止时,写入文件的所有数据应与根据抽象语义产生的程序执行的可能结果之一相同.
- 交互设备的输入和输出动态应以在程序等待输入之前提示输出实际传送的方式进行.构成交互设备的是实现定义的.
因此,粗略地说,应保留易失性访问操作和io操作的顺序和操作数; 实现可以在保存这些不变量的程序中进行任意更改(与抽象c ++机器的某些允许行为相比)
期望非恶意实现对操作进行足够广泛的处理是否合理(例如,来自用户代码的任何系统调用都被视为此类操作)?(例如,如果RAII包装器不包含挥发物,则编译器不会丢弃RAII互斥锁定/解锁)
"行为观察"应该从用户定义的c ++程序级别深入到库/系统调用中有多深?现在的问题是,当然,只有该库的调用不打算有从用户角度来看IO /挥发性接入(例如,作为新/删除操作),而且可能(通常如此)访问挥发,或在图书馆/系统IO 实现.应该从用户的角度来看,编译器将这类电话(并考虑副作用,如没有可观察到的),或从"库"的观点(以及考虑的副作用观察到的)?
如果我需要通过编译器,以防止一些代码消除,这是一个好的做法,是不会问上面的所有问题,并简单地增加(可能是假的)挥发性存取操作(换行需要挥发的方法的行动,并呼吁他们对挥发性情况我在任何情况下似乎可疑?
或者我完全错了,除了标准明确提到的情况(作为副本消除)之外,编译器不允许删除任何c ++代码
看看Go标准库,有一个ConstantTimeByteEq函数,如下所示:
func ConstantTimeByteEq(x, y uint8) int {
z := ^(x ^ y)
z &= z >> 4
z &= z >> 2
z &= z >> 1
return int(z)
}
Run Code Online (Sandbox Code Playgroud)
现在,我理解需要恒定时间串(数组等)比较,因为常规算法可能会在第一个不等元素之后短路.但在这种情况下,是不是定期比较两个固定大小的整数已经在CPU级别进行恒定时间操作?
一个简单的实现memcmp()是这样的(来自这个答案):
int memcmp_test(const char *cs_in, const char *ct_in, size_t n)
{
size_t i;
const unsigned char * cs = (const unsigned char*) cs_in;
const unsigned char * ct = (const unsigned char*) ct_in;
for (i = 0; i < n; i++, cs++, ct++)
{
if (*cs < *ct)
{
return -1;
}
else if (*cs > *ct)
{
return 1;
}
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
一旦找到第一个不匹配字节,块遍历就会停止。这对于加密应用程序来说可能没有好处,因为它使得执行时间依赖于块内容,并且这可能允许定时攻击。所以 OpenSSL 使用这个(取自这里):
int CRYPTO_memcmp(const void *in_a, const void …Run Code Online (Sandbox Code Playgroud)