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

Gav*_*ard 5 c fopen posix stat fstat

标题说明了一切:可以使用stat() 之后 fopen()避免使用时间(TOCTOU)竞争条件吗?

一些细节:

我正在编写一个读取文件的C程序,但在被要求读取目录时需要正确错误.截至目前,它使用open()(与O_RDWR),以产生一个错误,然后检查errnoEISDIR,像这样:

int fd = open(path, O_RDWR);

if (fd == -1) {
    if (errno == EISDIR) return PATH_IS_DIR;
    else return FILE_ERR;
}
Run Code Online (Sandbox Code Playgroud)

上述解决方案的问题是该程序只需要读取文件,因此通过打开文件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;
}
Run Code Online (Sandbox Code Playgroud)

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

Gav*_*ard 6

编辑(2018-10-25):Toby Speight的答案更好.

这里一个解决方案:使用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;
}
Run Code Online (Sandbox Code Playgroud)

在问这个问题之前,我在检查我已经覆盖了所有基地时找到了这个.


Tob*_*ght 5

不,问题中出现的代码不能避免参加TOCTOU竞赛

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

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

如果要使用C的缓冲I / O,则可以FILE*使用fileno()- 获得文件描述符,也可以使用使用- FILE*从文件描述符创建文件描述符fdopen()

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

# Untested

struct stat pstat;

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

if (!f) return FILE_ERR;

if (fstat(fileno(f), &pstat) == -1) {
//  ^^^^^^^^^^^^^^^                         <-- CHANGED HERE
    fclose(f);
    return FILE_ERR;
}

if (S_ISDIR(pstat.st_mode)) {
    fclose(f);
    return PATH_IS_DIR;
}
Run Code Online (Sandbox Code Playgroud)