在fopen()之后使用stat()以避免TOCTOU问题?

时间:2018-10-23 15:26:10

标签: c posix fopen stat fstat

标题说明了一切:是否可以在{em> stat()之后使用fopen() 来避免“检查时间到使用时间(TOCTOU)”竞赛条件?

一些细节:

我正在编写一个仅 可以读取文件的C程序,但是在要求读取目录时需要正确出错。截至目前,它使用open()(与O_RDWR一起生成错误,然后检查errno中是否有EISDIR,如下所示:

int fd = open(path, O_RDWR);

if (fd == -1) {
    if (errno == EISDIR) return PATH_IS_DIR;
    else return FILE_ERR;
}

上述解决方案的问题在于该程序仅需要读取文件,因此如果用户具有读取权限,但没有写入权限,则通过使用O_RDWR打开文件,我可能会错误地获得权限错误。

是否可以执行以下操作以避免TOCTOU比赛情况?

struct stat pstat;

FILE *f = fopen(path, "r");

if (!f) return FILE_ERR;

if (stat(path, &pstat) == -1) {
    fclose(f);
    return FILE_ERR;
}

if (S_ISDIR(pstat.st_mode)) {
    fclose(f);
    return PATH_IS_DIR;
}

如果不可能,是否有另一种解决方案来防止TOCTOU错误以及错误的权限错误?

3 个答案:

答案 0 :(得分:6)

编辑(2018-10-25):Toby Speight's answer更好。

有一个 解决方案:使用open(),然后 fstat()

一个例子:

struct stat pstat;

int fd = open(path, O_RDONLY);

if (fd == -1) return FILE_ERR;

if (fstat(fd, &pstat) == -1) {
    close(fd);
    return FILE_ERR;
}

if (S_ISDIR(pstat.st_mode)) {
    close(fd);
    return PATH_IS_DIR;
}

我在问这个问题之前检查了我的所有基础时发现了这一点。

答案 1 :(得分:2)

否,问题中显示的代码不能避免参加TOCTOU比赛

使用后的测试容易产生与使用前的测试完全相同的错误。在这两种情况下,名称都在两个不同的时间解析,结果可能不同。这是比赛的原因,无论哪种访问先发生都可能发生。

唯一避免这种情况的方法是打开文件一次,然后将获得的文件描述符用于所需的任何其他检查。现代操作系统为此提供了fstat()之类的接口。

如果您想使用C的缓冲I / O,则可以使用FILE*fileno()获取文件描述符-或者可以使用{ {1}}。

这需要对您的代码进行很小的更改:

FILE*

答案 2 :(得分:0)

Toby Speight 您能否让我知道您为什么使用:if(S_ISDIR(pstat.st_mode))。我们为什么要检查目录? 与缓解种族状况有关吗?