2 Replies Latest reply: Mar 13, 2013 2:00 PM by 996449 RSS

    solaris tmpfile() race condition results in hung system

    996449
      In a multi-threaded environment, if one thread calls tmpfile() and has just finished first umask() call inside _common(). During this time if another thread calls tmpfile(), 0 perm file get's created.

      http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libc/port/stdio/tmpfile.c#59

      Test program
      ===
      #include <sys/types.h>
      #include <unistd.h>
      #include <stdio.h>
      #include <string.h>
      #include <stdlib.h>
      #include <thread.h>
      #include <synch.h>
      #include <sys/stat.h>

      int race()
      {
      char tfname[20];
      FILE *p;
      char *q;
      int mkret;
      mode_t current_umask;

      strcpy(tfname, "tempfile1.txt");
      if((mkret = mkstemp(tfname)) == -1)
      return NULL;
      current_umask = umask(0777);
      (void) umask(current_umask);
      (void) fchmod(mkret, 0666 & ~current_umask);
      if(( p = fdopen(mkret, "w+")) == NULL) {
      (void)close(mkret);
      return (NULL);
      }
      (void)close(mkret);
      return (1);
      }
      int main()
      {
      char tfname[20];
      FILE *p;
      FILE *p1;
      char *q;
      int mkret;
      mode_t current_umask;

      strcpy(tfname, "tempfile.txt");
      if((mkret = mkstemp(tfname)) == -1)
      return NULL;
      current_umask = umask(0777);
      /* simulate multithreaded race condition where another thread calls tmpfile() between two umask calls.
      tmpfile() is simulated by race() function here.*/
      race();
      (void) umask(current_umask);
      (void) fchmod(mkret, 0666 & ~current_umask);
      if(( p = fdopen(mkret, "w+")) == NULL) {
      (void)close(mkret);
      return (NULL);
      }
      (void)close(mkret);
      return (1);
      }
      ===

      Thanks
      -Harendra

      Edited by: 993446 on Mar 12, 2013 2:13 PM
        • 1. Re: solaris tmpfile() race condition results in hung system
          800381
          Interesting.

          But since tmpfile() doesn't appear to be labelled MT-safe, I doubt it'll be considered a bug.

          And I'm not sure why the implementation is using umask() anyway. Why not just create the temp file with 0600 permissions and let it go at that? If you do a truss of the tmpfile() call, you see the file gets unlinked immediately anyway.

          I'm also curious as to what impact this has in reality. IIRC (and it has been a while...) since the file descriptor was created upon opening as read/write, that original file descriptor should be usable to read/write the file as needed no matter what the permissions are set to later.
          • 2. Re: solaris tmpfile() race condition results in hung system
            996449
            May be the description is not very clear.
            But since tmpfile() doesn't appear to be labelled MT-safe, I doubt it'll be considered a bug.
            Agreed. But that does not mean that tmpfile() should create a file with 0 perms on it. Which makes file unusable.
            And I'm not sure why the implementation is using umask() anyway. Why not just create the temp file with 0600 permissions and let it go at that? If you do a truss of the tmpfile() call, you see the file gets unlinked immediately anyway.
            Let me clear the confusion. Let's say thread 1 calls tmpfile()->common->umask(0777) and just returned for umask() call. Now thread 2 calls tmpfile() and returns before thread 1 enters second umask()    tmpfile()->common->umask(current_mask). If you see it's not in userland. All inside tmpfile() called from two threads. The sample test code I provided was just copied from _common(). Thread 2 returns a file with 0 perms on it.

            Is this acceptable even though tmpfile() is not MT-safe?
            I'm also curious as to what impact this has in reality. IIRC (and it has been a while...) since the file descriptor was created upon opening as read/write, that original file descriptor should be usable to read/write the file as needed no matter what the permissions are set to later.