静态分析工具(Coverity)将以下代码中的unlink()
语句标记为在access()
和unlink()
之间存在检查时间/使用时间(TOCTOU)问题:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
void del(const char* file) {
if (! file) { return; }
{
struct stat ss;
if (stat(file, &ss)) { return; }
if (! S_ISREG(ss.st_mode)) { return; }
if (ss.st_uid != getuid()) { return; }
if (access(file, W_OK)) { return; }
unlink(file);
}
}
int main(int argc, char* argv[]) {
const char* file = "./foo.txt";
int fd = open(file, O_WRONLY | O_CREAT);
close(fd);
del(file);
return 0;
}
它是否标记了在调用access()
和unlink()
之间,设备上的任何其他进程可能沿着并更改目标文件的权限,从而导致unlink()
失败的可能性?
是否有办法处理这种情况以避免TOCTOU?
我不知道如何通过当前进程“锁定”文件以获得独占删除权限。
TBH,我想我以前没有见过这样的代码,它会检查删除文件是否“可以”--我遇到的大多数这类代码只是无条件地调用unlink()
并继续前进。
1条答案
按热度按时间3pvhb19x1#
那些支票对吗?
!S_ISREG(ss.st_mode)
这确保了文件是一个普通文件。这不是一个多余的检查,因为你可以删除一些不是普通文件的文件。
如果您试图确保它不会删除非纯文本文件,那么代码会遇到TOCTOU争用条件,这不容易解决。
否则,如果您不关心文件的类型,那么执行检查是错误的,因为它错误地限制了哪些文件可以删除,它应该被删除。
ss.st_uid != getuid()
这确保了文件是一个普通文件。这不是一个多余的检查,因为你可以删除不属于你的文件。
如果您试图确保它不会删除其他人拥有的文件,则代码具有TOCTOU争用条件,这不容易解决。
否则,如果您不关心文件的所有者,则执行检查是错误的,因为它错误地限制了哪些文件可以删除,应该将其删除。
access(file, W_OK)
这使得进程具有写入文件的权限。此检查不是多余的,因为删除文件不需要写入文件的权限。(需要写入包含文件的目录的权限。)
如果您试图确保进程具有写入文件的权限,则代码会出现TOCTOU争用条件,这不容易解决。
否则,如果您不关心这一点,那么执行检查是错误的,因为它错误地限制了哪些文件可以删除,它应该被删除。
我怀疑当你修复了所有的错误,你只剩下
如果是,则不存在TOCTOU争用条件。
如果我错了,而你真的想检查这些东西,那么我看不到一个好的解决方案。没有合作,竞争条件实际上是不可避免的。