在 fopen() 之后使用 stat() 来避免 TOCTOU 问题?
Using stat() after fopen() to Avoid TOCTOU Problems?
标题说明了一切:可以使用 stat()
after 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错误和错误的权限错误?
编辑 (2018-10-25): 更好。
一个例子:
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;
}
我在问这个问题之前检查我是否涵盖了所有基础时发现了这一点。
不,问题中提供的代码不会避免 TOCTOU 比赛。
使用后测试容易出现与使用前测试完全相同的错误。在这两种情况下,名称在两个不同的时间解析,结果可能不同。这是争用的原因,无论哪个访问先发生,它都可能发生。
避免这种情况的唯一方法是打开文件一次,然后将如此获得的文件描述符用于您需要的任何其他检查。现代操作系统正是为此目的提供了诸如 fstat()
之类的接口。
如果您想使用 C 的缓冲 I/O,您可以使用 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;
}
标题说明了一切:可以使用 stat()
after 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错误和错误的权限错误?
编辑 (2018-10-25):
一个例子:
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;
}
我在问这个问题之前检查我是否涵盖了所有基础时发现了这一点。
不,问题中提供的代码不会避免 TOCTOU 比赛。
使用后测试容易出现与使用前测试完全相同的错误。在这两种情况下,名称在两个不同的时间解析,结果可能不同。这是争用的原因,无论哪个访问先发生,它都可能发生。
避免这种情况的唯一方法是打开文件一次,然后将如此获得的文件描述符用于您需要的任何其他检查。现代操作系统正是为此目的提供了诸如 fstat()
之类的接口。
如果您想使用 C 的缓冲 I/O,您可以使用 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;
}