The flag O_DIRECTORY
can be used with the syscalls open(2)
and openat(2)
to avoid denial-of-service vulnerabilities when opening directories. However: How can I avoid the same kind of race conditions for regular files?
Some background information: I am trying to develop some kind of backup tool. The programs walks over a directory tree, reads all regular files and only stats other files. If I first call fstatat(2)
for each directory entry, test the result for regular files and open them with openat(2)
, then there is a race condition between the syscalls. An attacker could replace the regular file with a FIFO, and my program would hang on the FIFO.
How can I avoid this race condition? For directories, there is O_DIRECTORY
, for symbolic links, O_PATH
can be used. However, I have found no solution for regular files. I only need a solution that works on recent Linux versions.
If your only concern is fifos, O_NONBLOCK
will prevent blocking and allow you to open a fifo even if it has a no writers (see http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html for where this is specified). However, there are also a few other concerns:
/proc
with bad propertiesSince these normally can't be created in arbitrary locations by non-root users, O_NOFOLLOW
should be sufficient to avoid following symlinks to them.
With that said, on modern Linux there is an even safer solution: perform the initial open
with O_PATH|O_NOFOLLOW
, then perform stat
on /proc/self/fd/%d
to check the file type. You can then open /proc/self/fd/%d
and be completely certain it corresponds to the same file you just stat
'd.
Note that on sufficiently new Linux, you don't need to use /proc/self/fd/%d
to reach the file to which you obtained an inode handle with O_PATH
. You can use fstat
and openat
on it directly to "stat" it and get a descriptor to a real open file description, respectively. However O_PATH
file descriptors had a lot of broken/unimplemented corner cases like this in the range of late 2.6.x (when they were first added) to 3.8 or so, and I find the /proc
method the most reliable. Of course you could always try the direct method and fallback to /proc
if it fails.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With