Patch 8.0.1345
Problem:    Race condition between stat() and open() for the viminfo temp
            file. (Simon Ruderich)
Solution:   use open() with O_EXCL to atomically check if the file exists.
            Don't try using a temp file, renaming it will fail anyway.
Files:      src/ex_cmds.c


*** ../vim-8.0.1344/src/ex_cmds.c       2017-11-11 16:45:14.782308811 +0100
--- src/ex_cmds.c       2017-11-26 15:55:07.274872225 +0100
***************
*** 1825,1831 ****
      FILE      *fp_out = NULL; /* output viminfo file */
      char_u    *tempname = NULL;       /* name of temp viminfo file */
      stat_T    st_new;         /* mch_stat() of potential new file */
-     char_u    *wp;
  #if defined(UNIX) || defined(VMS)
      mode_t    umask_save;
  #endif
--- 1825,1830 ----
***************
*** 1847,1873 ****
      fp_in = mch_fopen((char *)fname, READBIN);
      if (fp_in == NULL)
      {
        /* if it does exist, but we can't read it, don't try writing */
        if (mch_stat((char *)fname, &st_new) == 0)
            goto end;
! #if defined(UNIX) || defined(VMS)
!       /*
!        * For Unix we create the .viminfo non-accessible for others,
!        * because it may contain text from non-accessible documents.
!        */
!       umask_save = umask(077);
! #endif
!       fp_out = mch_fopen((char *)fname, WRITEBIN);
! #if defined(UNIX) || defined(VMS)
!       (void)umask(umask_save);
! #endif
      }
      else
      {
        /*
         * There is an existing viminfo file.  Create a temporary file to
         * write the new viminfo into, in the same directory as the
!        * existing viminfo file, which will be renamed later.
         */
  #ifdef UNIX
        /*
--- 1846,1874 ----
      fp_in = mch_fopen((char *)fname, READBIN);
      if (fp_in == NULL)
      {
+       int fd;
+ 
        /* if it does exist, but we can't read it, don't try writing */
        if (mch_stat((char *)fname, &st_new) == 0)
            goto end;
! 
!       /* Create the new .viminfo non-accessible for others, because it may
!        * contain text from non-accessible documents. It is up to the user to
!        * widen access (e.g. to a group). This may also fail if there is a
!        * race condition, then just give up. */
!       fd = mch_open((char *)fname,
!                           O_CREAT|O_EXTRA|O_EXCL|O_WRONLY|O_NOFOLLOW, 0600);
!       if (fd < 0)
!           goto end;
!       fp_out = fdopen(fd, WRITEBIN);
      }
      else
      {
        /*
         * There is an existing viminfo file.  Create a temporary file to
         * write the new viminfo into, in the same directory as the
!        * existing viminfo file, which will be renamed once all writing is
!        * successful.
         */
  #ifdef UNIX
        /*
***************
*** 1901,1912 ****
  #endif
  
        /*
!        * Make tempname.
         * May try twice: Once normal and once with shortname set, just in
         * case somebody puts his viminfo file in an 8.3 filesystem.
         */
        for (;;)
        {
            tempname = buf_modname(
  #ifdef UNIX
                                    shortname,
--- 1902,1919 ----
  #endif
  
        /*
!        * Make tempname, find one that does not exist yet.
!        * Beware of a race condition: If someone logs out and all Vim
!        * instances exit at the same time a temp file might be created between
!        * stat() and open().  Use mch_open() with O_EXCL to avoid that.
         * May try twice: Once normal and once with shortname set, just in
         * case somebody puts his viminfo file in an 8.3 filesystem.
         */
        for (;;)
        {
+           int         next_char = 'z';
+           char_u      *wp;
+ 
            tempname = buf_modname(
  #ifdef UNIX
                                    shortname,
***************
*** 1924,2050 ****
                break;
  
            /*
!            * Check if tempfile already exists.  Never overwrite an
!            * existing file!
             */
!           if (mch_stat((char *)tempname, &st_new) == 0)
            {
- #ifdef UNIX
                /*
!                * Check if tempfile is same as original file.  May happen
!                * when modname() gave the same file back.  E.g.  silly
!                * link, or file name-length reached.  Try again with
!                * shortname set.
                 */
!               if (!shortname && st_new.st_dev == st_old.st_dev
!                                           && st_new.st_ino == st_old.st_ino)
!               {
!                   vim_free(tempname);
!                   tempname = NULL;
!                   shortname = TRUE;
!                   continue;
!               }
! #endif
!               /*
!                * Try another name.  Change one character, just before
!                * the extension.  This should also work for an 8.3
!                * file name, when after adding the extension it still is
!                * the same file as the original.
!                */
!               wp = tempname + STRLEN(tempname) - 5;
!               if (wp < gettail(tempname))         /* empty file name? */
!                   wp = gettail(tempname);
!               for (*wp = 'z'; mch_stat((char *)tempname, &st_new) == 0;
!                                                                   --*wp)
                {
                    /*
!                    * They all exist?  Must be something wrong! Don't
!                    * write the viminfo file then.
                     */
!                   if (*wp == 'a')
                    {
-                       EMSG2(_("E929: Too many viminfo temp files, like %s!"),
-                                                                   tempname);
                        vim_free(tempname);
                        tempname = NULL;
                        break;
                    }
                }
!           }
!           break;
!       }
! 
!       if (tempname != NULL)
!       {
  #ifdef VMS
!           /* fdopen() fails for some reason */
!           umask_save = umask(077);
!           fp_out = mch_fopen((char *)tempname, WRITEBIN);
!           (void)umask(umask_save);
  #else
!           int fd;
  
!           /* Use mch_open() to be able to use O_NOFOLLOW and set file
!            * protection:
!            * Unix: same as original file, but strip s-bit.  Reset umask to
!            * avoid it getting in the way.
!            * Others: r&w for user only. */
  # ifdef UNIX
!           umask_save = umask(0);
!           fd = mch_open((char *)tempname,
!                   O_CREAT|O_EXTRA|O_EXCL|O_WRONLY|O_NOFOLLOW,
!                                      (int)((st_old.st_mode & 0777) | 0600));
!           (void)umask(umask_save);
  # else
!           fd = mch_open((char *)tempname,
!                           O_CREAT|O_EXTRA|O_EXCL|O_WRONLY|O_NOFOLLOW, 0600);
  # endif
!           if (fd < 0)
!               fp_out = NULL;
!           else
!               fp_out = fdopen(fd, WRITEBIN);
  #endif /* VMS */
  
!           /*
!            * If we can't create in the same directory, try creating a
!            * "normal" temp file.  This is just an attempt, renaming the temp
!            * file might fail as well.
!            */
!           if (fp_out == NULL)
!           {
!               vim_free(tempname);
!               if ((tempname = vim_tempname('o', TRUE)) != NULL)
!                   fp_out = mch_fopen((char *)tempname, WRITEBIN);
            }
  
  #if defined(UNIX) && defined(HAVE_FCHOWN)
            /*
             * Make sure the original owner can read/write the tempfile and
             * otherwise preserve permissions, making sure the group matches.
             */
!           if (fp_out != NULL)
            {
!               stat_T  tmp_st;
! 
!               if (mch_stat((char *)tempname, &tmp_st) >= 0)
!               {
!                   if (st_old.st_uid != tmp_st.st_uid)
!                       /* Changing the owner might fail, in which case the
!                        * file will now owned by the current user, oh well. */
!                       ignored = fchown(fileno(fp_out), st_old.st_uid, -1);
!                   if (st_old.st_gid != tmp_st.st_gid
!                           && fchown(fileno(fp_out), -1, st_old.st_gid) == -1)
!                       /* can't set the group to what it should be, remove
!                        * group permissions */
!                       (void)mch_setperm(tempname, 0600);
!               }
!               else
!                   /* can't stat the file, set conservative permissions */
                    (void)mch_setperm(tempname, 0600);
            }
! #endif
        }
      }
  
      /*
       * Check if the new viminfo file can be written to.
--- 1931,2059 ----
                break;
  
            /*
!            * Try a series of names.  Change one character, just before
!            * the extension.  This should also work for an 8.3
!            * file name, when after adding the extension it still is
!            * the same file as the original.
             */
!           wp = tempname + STRLEN(tempname) - 5;
!           if (wp < gettail(tempname))     /* empty file name? */
!               wp = gettail(tempname);
!           for (;;)
            {
                /*
!                * Check if tempfile already exists.  Never overwrite an
!                * existing file!
                 */
!               if (mch_stat((char *)tempname, &st_new) == 0)
                {
+ #ifdef UNIX
                    /*
!                    * Check if tempfile is same as original file.  May happen
!                    * when modname() gave the same file back.  E.g.  silly
!                    * link, or file name-length reached.  Try again with
!                    * shortname set.
                     */
!                   if (!shortname && st_new.st_dev == st_old.st_dev
!                                               && st_new.st_ino == 
st_old.st_ino)
                    {
                        vim_free(tempname);
                        tempname = NULL;
+                       shortname = TRUE;
                        break;
                    }
+ #endif
                }
!               else
!               {
!                   /* Try creating the file exclusively.  This may fail if
!                    * another Vim tries to do it at the same time. */
  #ifdef VMS
!                   /* fdopen() fails for some reason */
!                   umask_save = umask(077);
!                   fp_out = mch_fopen((char *)tempname, WRITEBIN);
!                   (void)umask(umask_save);
  #else
!                   int fd;
  
!                   /* Use mch_open() to be able to use O_NOFOLLOW and set file
!                    * protection:
!                    * Unix: same as original file, but strip s-bit.  Reset
!                    * umask to avoid it getting in the way.
!                    * Others: r&w for user only. */
  # ifdef UNIX
!                   umask_save = umask(0);
!                   fd = mch_open((char *)tempname,
!                           O_CREAT|O_EXTRA|O_EXCL|O_WRONLY|O_NOFOLLOW,
!                                       (int)((st_old.st_mode & 0777) | 0600));
!                   (void)umask(umask_save);
  # else
!                   fd = mch_open((char *)tempname,
!                            O_CREAT|O_EXTRA|O_EXCL|O_WRONLY|O_NOFOLLOW, 0600);
  # endif
!                   if (fd < 0)
!                   {
!                       fp_out = NULL;
! # ifdef EEXIST
!                       /* Avoid trying lots of names while the problem is lack
!                        * of premission, only retry if the file already
!                        * exists. */
!                       if (errno != EEXIST)
!                           break;
! # endif
!                   }
!                   else
!                       fp_out = fdopen(fd, WRITEBIN);
  #endif /* VMS */
+                   if (fp_out != NULL)
+                       break;
+               }
  
!               /* Assume file exists, try again with another name. */
!               if (next_char == 'a' - 1)
!               {
!                   /* They all exist?  Must be something wrong! Don't write
!                    * the viminfo file then. */
!                   EMSG2(_("E929: Too many viminfo temp files, like %s!"),
!                                                                    tempname);
!                   break;
!               }
!               *wp = next_char;
!               --next_char;
            }
  
+           if (tempname != NULL)
+               break;
+           /* continue if shortname was set */
+       }
+ 
  #if defined(UNIX) && defined(HAVE_FCHOWN)
+       if (tempname != NULL && fp_out != NULL)
+       {
+               stat_T  tmp_st;
+ 
            /*
             * Make sure the original owner can read/write the tempfile and
             * otherwise preserve permissions, making sure the group matches.
             */
!           if (mch_stat((char *)tempname, &tmp_st) >= 0)
            {
!               if (st_old.st_uid != tmp_st.st_uid)
!                   /* Changing the owner might fail, in which case the
!                    * file will now owned by the current user, oh well. */
!                   ignored = fchown(fileno(fp_out), st_old.st_uid, -1);
!               if (st_old.st_gid != tmp_st.st_gid
!                       && fchown(fileno(fp_out), -1, st_old.st_gid) == -1)
!                   /* can't set the group to what it should be, remove
!                    * group permissions */
                    (void)mch_setperm(tempname, 0600);
            }
!           else
!               /* can't stat the file, set conservative permissions */
!               (void)mch_setperm(tempname, 0600);
        }
      }
+ #endif
  
      /*
       * Check if the new viminfo file can be written to.
*** ../vim-8.0.1344/src/version.c       2017-11-26 14:56:11.128133277 +0100
--- src/version.c       2017-11-26 15:57:59.209925680 +0100
***************
*** 773,774 ****
--- 773,776 ----
  {   /* Add new patch number below this line */
+ /**/
+     1345,
  /**/

-- 
Change is inevitable, except from a vending machine.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui