cfileunlinkfile-accesstocttou

How to handle TOCTOU problem between access() and unlink()?


A static-analysis tool (Coverity) flags the unlink() statement in the following code as having a time-of-check/time-of-use (TOCTOU) problem between the access() and unlink():

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

void del(const char* file) {
  if (! file) { return; }

  {
    struct stat ss;

    if (stat(file, &ss)) { return; }
    if (! S_ISREG(ss.st_mode)) { return; }
    if (ss.st_uid != getuid()) { return; }
    if (access(file, W_OK)) { return; }

    unlink(file);
  }
}

int main(int argc, char* argv[]) {
  const char* file = "./foo.txt";
  int fd = open(file, O_WRONLY | O_CREAT);

  close(fd);
  del(file);

  return 0;
}

Is it flagging the possibility that between the call to access() and unlink(), any other process on the device can come along and change the target file's permissions, so that the unlink() fails?

Is there a way that this situation can be handled to avoid the TOCTOU?
I'm at a loss how to "lock" the file for exclusive deletion-privilege by the current process.

TBH, I don't think I've seen code before that goes to these lengths to check if it's "okay" to delete a file -- most code of this sort that I've encountered just unconditionally call unlink() and move on. :)


Solution

  • Are those checks correct?

    I suspect that when you fix all the bugs, you're left with

    void del( const char* file ) {
       unlink( file );
    }
    

    If so, there's no TOCTOU race condition.

    If I'm wrong and you really want to check those things, then I don't see a good solution. A race condition is virtually unavoidable without cooperation.