C++ 避免stat和rename之间的TOCTOU(检查时间、使用时间)竞争条件

C++ 避免stat和rename之间的TOCTOU(检查时间、使用时间)竞争条件,c++,c++11,race-condition,coverity,coverity-prevent,C++,C++11,Race Condition,Coverity,Coverity Prevent,如何避免日志文件的stat和rename之间的toctu(检查时间、使用时间)争用条件 需要在日志文件的大小值超过最大大小后移动日志文件 result=stat(日志文件和数据); 如果(结果!=0){ //统计失败 //文件可能不存在 }否则如果(data.st\u size>MAX\u LOGSIZE){ 取消链接(上一个日志文件); (无效)重命名(日志文件,上一个日志文件); } 避免文件操作出错的标准方法是打开文件一次,然后通过文件描述符而不是文件名执行所需的所有操作 但是,重命名和取

如何避免日志文件的stat和rename之间的toctu(检查时间、使用时间)争用条件

需要在日志文件的大小值超过最大大小后移动日志文件

result=stat(日志文件和数据);
如果(结果!=0){
//统计失败
//文件可能不存在
}否则如果(data.st\u size>MAX\u LOGSIZE){
取消链接(上一个日志文件);
(无效)重命名(日志文件,上一个日志文件);
}

避免文件操作出错的标准方法是
打开文件一次,然后通过文件描述符而不是文件名执行所需的所有操作

但是,重命名和取消链接文件都需要其路径(因为它们需要知道要重命名或删除的链接),所以在这里不能使用这种方法。另一种方法可能是将文件的内容复制到其他地方,然后将其截断为零字节,尽管使用日志文件的场景可能要求操作是原子的,这可能很难实现。另一种方法是要求对目录进行严格的访问控制:如果攻击者无法写入目录,那么它就无法使用您的进程玩toctou游戏。您可以使用
unlinkat
renameat
将路径限制为特定目录的文件描述符,这样您就不必担心目录本身的更改

假设有一个类似POSIX的平台,像这样未经测试的代码可以完成这项工作:

dirfd = open(LOGDIR, O_DIRECTORY);
// check for failure
res = fstatat(dirfd, LOGFILE, statbuf, AT_SYMLINK_NOFOLLOW);
if ((0 == res) && (S_ISREG(statbuf) && (data.st_size > MAX_LOGSIZE)) {
    unlinkat(dirfd, PREV_LOGFILE, 0);
    renameat(dirfd, LOGFILE, dirfd, PREV_LOGFILE);
}
close(dirfd);

打开文件并使用fstat,这样您操作的文件就不能“更改”@Oz file应该始终打开..为什么这甚至是一个问题?您是否从其他线程写入日志文件?你可能应该把它静音。它在任何情况下都不会收缩,所以成功的检查也不会失效。@n.m是的,不同的线程,它是一个覆盖率问题,指出它是一个假阳性。