如何使用try语句避免竞争条件?

时间:2013-01-29 02:05:18

标签: python race-condition

在确定文件是否存在时,如何使用try语句避免“竞争条件”?

我问,因为一个高度赞成的answer(更新:它被删除)似乎意味着使用os.path.exists()创造了一个不存在的机会。

给出的例子是:

try:
   with open(filename): pass
except IOError:
   print 'Oh dear.'

但我不理解如何避免竞争条件:

if not os.path.exists(filename):
    print 'Oh dear.'

如何调用os.path.exists(filename)允许攻击者对他们无法执行的文件执行某些操作?

3 个答案:

答案 0 :(得分:29)

竞争条件当然是在您的程序和其他一些在文件上运行的代码之间(竞争条件总是需要至少两个并行进程或线程,有关详细信息,请参阅this)。这意味着使用open()代替exists()可能只对两种情况有帮助:

  1. 您检查是否存在由某个后台进程创建或删除的文件(但是,如果您在Web服务器内部运行,这通常意味着您的进程的许多副本并行运行以处理HTTP请求,因此对于即使没有其他程序,也可以使用web应用程序竞争条件。)
  2. 可能有一些恶意程序正在尝试通过在您希望它存在的时刻销毁文件来破坏您的代码。
  3. exists()只执行一次检查。如果文件存在,则可能会在exists()返回True后的一微秒内删除。如果文件不存在,可以立即创建。

    然而,open()不仅测试文件是否存在,而且还打开文件(并以原子方式执行这两个操作,因此在检查和开始之间不会发生任何事情)。通常,文件在某人打开时无法删除。这意味着在with内你可能完全确定:文件现在确实存在,因为它是开放的。虽然它只在with内部存在,并且文件仍然可以在with块退出后立即删除,但将需要文件的代码存在于with内可以保证代码不会失败。

答案 1 :(得分:9)

以下是一个使用示例:

try:
    with open('filename') as f:
        do_stuff_that_depends_on_the_existence_of_the_file(f)
except IOError as e:
    print 'Trouble opening file'

如果您打开任何访问权限的文件,那么操作系统将保证该文件存在,否则它将失败并显示错误。如果访问权限是独占的,那么任何其他争用该文件的进程都将被您阻止或阻止您。

try只是检测打开文件的错误或成功的一种方法,因为Python中的文件I / O API通常没有返回代码(而是使用异常)。所以要真正回答你的问题,不是try避免竞争条件,而是open。它在C(Python所基于的)中基本相同,但没有例外。请阅读this以获取更多信息。

请注意,您可能希望执行依赖于对try块内文件的访问的代码。关闭文件后,不再保证其存在。

调用os.path.exists仅在文件可能存在或不存在的时刻给出快照,并且在os.path.exists返回后您不知道文件是否存在。恶意代码或意外逻辑可能会在您不期望时删除或更改文件。它类似于在开车前检查道路是否清晰。一旦你转过头来,你只能猜测你不再寻找的地方。保持文件打开可确保延长的一致状态,这在开车时是不可能的(无论好坏)。 :)

由于try/open的快照特性,您建议检查文件是否存在而不是使用os.path.exists仍然不够。不幸的是,我知道在所有情况下都无法防止在目录中创建文件,因此我认为最好检查文件是否存在,而不是缺少文件。

答案 2 :(得分:0)

我认为你所问的是特定的竞争条件:

  1. 文件已打开
  2. 切换上下文并删除文件
  3. 切换回上下文并尝试对“已打开”文件执行文件操作
  4. 在这种情况下,您受“保护”的方式是将所有文件处理代码放在try块中,如果在任何时候文件变得不可访问/损坏,您的文件操作将能够失败“优雅地“通过catch块。

    当然注意现代操作系统无论如何都不会发生这种情况,当文件被“删除”时,只有解析(释放)文件上的所有打开句柄才会发生删除