短路评估可以触发竞争条件吗?

时间:2016-11-11 08:45:56

标签: java c++ security race-condition short-circuiting

我喜欢代码高尔夫。根据{{​​3}},我想知道我的任何“高尔夫”代码是否会引发竞争条件。

假设我们有两个函数,它们都返回一个布尔值,我们正在初始化一个名为result的布尔变量:

result = foo() || bar();

在理想的世界中,我们有两种情景:

  1. foo返回true致电barresult等于true [短路情景]
  2. foo返回false 致电bar()result等于bar返回的内容。
  3. 我的问题:是否会有违反短路评估的时间,尽管bar返回foo,甚至会调用true,甚至更糟糕的是,在调用bar之前调用foo,可能是因为多线程?如果是这样,你能提供一段会触发这种行为的代码吗?

    你的答案可能是关于这种语法有效的任何语言,尽管我认为某些语言对这类事物的要求会比其他语言更严格。

3 个答案:

答案 0 :(得分:11)

当一系列操作的结果取决于执行它们的顺序时,就会出现竞争条件。

C ++中的&&||运算符保证从左到右的评估,如果第一个运算符分别为false / true,则不评估第二个运算符。由于操作序列得到保证,因此foo的操作与bar的操作之间不存在竞争条件。但是,每个中的操作之间仍然存在竞争条件。

违反上述保证的代码不是C ++代码,同样符合C ++编译器的代码也绝不会发出违反这些保证的代码。

答案 1 :(得分:3)

您指的情况不是竞争条件。 让我们再回到你的问题:

  

是否会有违反短路评估的时间,   尽管foo返回true,但仍然会调用bar,或者更糟糕的是,bar就是   调用foo之前调用,可能是因为多线程?

我想正确的问题是:

是否有时间 bar 被调用 AFTER foo 使用 false 结果调用,但是当它被称为 foo 时将返回 true ,如果再次被称为

好的,让我们做一些潜在的race condition州的代码。

#define BUFFER_SIZE 0x1000
char globalBuffer[BUFFER_SIZE];

bool foo() { // have user an access to the path_to_file file?
   return access(path_to_file, 0666) != 0; // path_to_file declared somewhere
}

bool bar() {
    FILE *file = fopen(path_to_file, "r");
    if (file == NULL) return false;
    char *ptr = gfets(globalBuffer, BUFFER_SIZE, file);
    if (ptr == NULL) return false;
    return true;
}
...
result = foo() || bar(); // if foo is false, then user have an access
printf("%s", globalBuffer);

让我们假设我们可以控制path_to_file。

如果我们像这样进行无限循环

,将会出现竞争条件
#!/bin/bash

while :
do
    ln -s /path/to/good/access/file /path/to/file
    rm -f /path/to/file
    ln -s /etc/shadow /path/to/file
done

经过一些尝试,如果您的应用程序有suid位 - 我将读取/ etc / shadow

的内容

但让我们回到你的问题:

不,没办法。如果foo将返回true,则不会调用bar。即使是其他线程,如果你有多线程。每个线程都有自己的寄存器,自己的堆栈。因此,如果在同一个表达式中有两个函数调用,您应该相信c ++标准。但这并不意味着您的代码是安全的。并且竞争条件是100个可能的问题之一。

答案 2 :(得分:1)

请注意,C ++支持运算符重载,但该短路仅是内置运算符(pre c ++ 17)的一个特性,而不是用户提供的重载。

即。在所有当前版本的C ++中,假设发生短路是不安全的,除非你知道这两种类型并且知道没有自由函数运算符||是阴影。

请参阅p0145了解这在c ++ 17中的变化情况。

(粗体是添加,斜体删除)

  

更改第5/2段如下:[注意:操作员可能会超载,   也就是说,当应用于类类型的表达式时给定含义   (第9条)或查点类型(7.2)。重载运算符的使用是   转换为函数调用,如13.5中所述。重载   运营商遵守指定的语法和评估顺序的规则   第5条,但操作数类型*,* 值类别的要求,   和评估顺序被函数调用的规则所取代。   运算符之间的关系,例如++ a表示+ = 1,则不是   保证重载运算符(13.5),并且不保证   bool类型的操作数。 - 后注]