From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Paul Eggert Newsgroups: gmane.emacs.bugs Subject: bug#15015: Fix some minor races in hosts lacking mkostemp Date: Sat, 03 Aug 2013 00:40:05 -0700 Organization: UCLA Computer Science Department Message-ID: <51FCB3D5.7070704@cs.ucla.edu> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-Trace: ger.gmane.org 1375518560 31237 80.91.229.3 (3 Aug 2013 08:29:20 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Sat, 3 Aug 2013 08:29:20 +0000 (UTC) To: 15015@debbugs.gnu.org Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Sat Aug 03 10:29:21 2013 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1V5XDB-0004Gq-UR for geb-bug-gnu-emacs@m.gmane.org; Sat, 03 Aug 2013 10:29:18 +0200 Original-Received: from localhost ([::1]:33042 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1V5XDB-0003O7-Hv for geb-bug-gnu-emacs@m.gmane.org; Sat, 03 Aug 2013 04:29:17 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:46704) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1V5XD1-0003Iz-VK for bug-gnu-emacs@gnu.org; Sat, 03 Aug 2013 04:29:13 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1V5XCx-0004iB-6v for bug-gnu-emacs@gnu.org; Sat, 03 Aug 2013 04:29:07 -0400 Original-Received: from debbugs.gnu.org ([140.186.70.43]:43324) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1V5XCx-0004i7-2R for bug-gnu-emacs@gnu.org; Sat, 03 Aug 2013 04:29:03 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.80) (envelope-from ) id 1V5XCw-0001UY-Gf for bug-gnu-emacs@gnu.org; Sat, 03 Aug 2013 04:29:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Paul Eggert Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sat, 03 Aug 2013 08:29:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 15015 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch X-Debbugs-Original-To: bug-emacs Original-Received: via spool by submit@debbugs.gnu.org id=B.13755185065660 (code B ref -1); Sat, 03 Aug 2013 08:29:02 +0000 Original-Received: (at submit) by debbugs.gnu.org; 3 Aug 2013 08:28:26 +0000 Original-Received: from localhost ([127.0.0.1]:37640 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1V5XCK-0001TB-Ks for submit@debbugs.gnu.org; Sat, 03 Aug 2013 04:28:26 -0400 Original-Received: from eggs.gnu.org ([208.118.235.92]:44768) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1V5XCG-0001Sv-E2 for submit@debbugs.gnu.org; Sat, 03 Aug 2013 04:28:22 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1V5XC6-0004Tx-0u for submit@debbugs.gnu.org; Sat, 03 Aug 2013 04:28:15 -0400 Original-Received: from lists.gnu.org ([2001:4830:134:3::11]:59706) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1V5X9L-0003KZ-Ar for submit@debbugs.gnu.org; Sat, 03 Aug 2013 04:25:19 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:45370) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1V5X99-0002Kl-ON for bug-gnu-emacs@gnu.org; Sat, 03 Aug 2013 04:25:19 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1V5WRh-0003Jb-NF for bug-gnu-emacs@gnu.org; Sat, 03 Aug 2013 03:40:46 -0400 Original-Received: from smtp.cs.ucla.edu ([131.179.128.62]:43604) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1V5WRh-0003Iw-2L; Sat, 03 Aug 2013 03:40:13 -0400 Original-Received: from localhost (localhost.localdomain [127.0.0.1]) by smtp.cs.ucla.edu (Postfix) with ESMTP id 82D25A6002C; Sat, 3 Aug 2013 00:40:11 -0700 (PDT) X-Virus-Scanned: amavisd-new at smtp.cs.ucla.edu Original-Received: from smtp.cs.ucla.edu ([127.0.0.1]) by localhost (smtp.cs.ucla.edu [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id NMQ-EhfaERxV; Sat, 3 Aug 2013 00:40:09 -0700 (PDT) Original-Received: from [192.168.1.9] (pool-71-108-49-126.lsanca.fios.verizon.net [71.108.49.126]) by smtp.cs.ucla.edu (Postfix) with ESMTPSA id 94756A60023; Sat, 3 Aug 2013 00:40:09 -0700 (PDT) User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130623 Thunderbird/17.0.7 X-Enigmail-Version: 1.5.2 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6.x X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.15 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 140.186.70.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.bugs:76919 Archived-At: Tags: patch Here's a proposed patch to fix some minor races in hosts lacking mkostemp. I've tested this on Fedora 17 and Solaris 9. Eli recently wrote that he didn't think it was feasible to close the races on Microsoft platforms, so I'm posting this on bug-gnu-emacs first (with a CC: to Eli to give him a heads-up). It looks to me like it may close the races on Microsoft platforms, at least if they support O_EXCL, and at any rate I hope it doesn't hurt on those platforms. Most of this patch consists of code copied automatically from Gnulib. The meat of the patch is in src/callproc.c and src/filelock.c. === modified file 'ChangeLog' --- ChangeLog 2013-07-29 11:35:16 +0000 +++ ChangeLog 2013-08-03 07:19:27 +0000 @@ -1,3 +1,12 @@ +2013-08-03 Paul Eggert + + Fix some minor races in hosts lacking mkostemp. + Gnulib's emulation of mkostemp doesn't have races that Emacs's does. + * lib/mkostemp.c, lib/secure_getenv.c, lib/tempname.c, lib/tempname.h: + * m4/mkostemp.m4, m4/secure_getenv.m4, m4/tempname.m4: + New files, copied from Gnulib. + * lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate. + 2013-07-29 Michael Albinus * INSTALL (DETAILED BUILDING AND INSTALLATION): Add === modified file 'admin/ChangeLog' --- admin/ChangeLog 2013-07-12 06:45:04 +0000 +++ admin/ChangeLog 2013-08-03 07:19:27 +0000 @@ -1,3 +1,8 @@ +2013-08-03 Paul Eggert + + Using Gnulib's mkostemp module. + * merge-gnulib (GNULIB_MODULES): Add mkostemp. + 2013-07-12 Glenn Morris * admin.el (manual-style-string): Use non-abbreviated url. === modified file 'admin/merge-gnulib' --- admin/merge-gnulib 2013-07-09 17:16:21 +0000 +++ admin/merge-gnulib 2013-08-03 07:19:27 +0000 @@ -32,7 +32,7 @@ fcntl fcntl-h fdatasync fdopendir filemode fstatat fsync getloadavg getopt-gnu gettime gettimeofday intprops largefile lstat - manywarnings memrchr mktime + manywarnings memrchr mkostemp mktime pipe2 pselect pthread_sigmask putenv qacl readlink readlinkat sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat === modified file 'lib/gnulib.mk' --- lib/gnulib.mk 2013-07-09 17:16:21 +0000 +++ lib/gnulib.mk 2013-08-03 07:19:27 +0000 @@ -21,7 +21,7 @@ # the same distribution terms as the rest of that program. # # Generated by gnulib-tool. -# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=close --avoid=dup --avoid=fchdir --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl fcntl-h fdatasync fdopendir filemode fstatat fsync getloadavg getopt-gnu gettime gettimeofday intprops largefile lstat manywarni ngs memrchr mktime pipe2 pselect pthread_sigmask putenv qacl readlink readlinkat sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_tim e time ti mer-time timespec-add timespec-sub unsetenv utimens warnings +# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=close --avoid=dup --avoid=fchdir --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl fcntl-h fdatasync fdopendir filemode fstatat fsync getloadavg getopt-gnu gettime gettimeofday intprops largefile lstat manywarni ngs memrchr mkostemp mktime pipe2 pselect pthread_sigmask putenv qacl readlink readlinkat sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_sta t sys_tim e time timer-time timespec-add timespec-sub unsetenv utimens warnings MOSTLYCLEANFILES += core *.stackdump @@ -560,6 +560,15 @@ ## end gnulib module memrchr +## begin gnulib module mkostemp + + +EXTRA_DIST += mkostemp.c + +EXTRA_libgnu_a_SOURCES += mkostemp.c + +## end gnulib module mkostemp + ## begin gnulib module mktime @@ -657,6 +666,17 @@ ## end gnulib module root-uid +## begin gnulib module secure_getenv + +if gl_GNULIB_ENABLED_secure_getenv + +endif +EXTRA_DIST += secure_getenv.c + +EXTRA_libgnu_a_SOURCES += secure_getenv.c + +## end gnulib module secure_getenv + ## begin gnulib module sig2str @@ -1480,6 +1500,16 @@ ## end gnulib module sys_time +## begin gnulib module tempname + +if gl_GNULIB_ENABLED_tempname +libgnu_a_SOURCES += tempname.c + +endif +EXTRA_DIST += tempname.h + +## end gnulib module tempname + ## begin gnulib module time BUILT_SOURCES += time.h === added file 'lib/mkostemp.c' --- lib/mkostemp.c 1970-01-01 00:00:00 +0000 +++ lib/mkostemp.c 2013-08-03 07:19:27 +0000 @@ -0,0 +1,46 @@ +/* Copyright (C) 1998-1999, 2001, 2005-2007, 2009-2013 Free Software + Foundation, Inc. + This file is derived from the one in the GNU C Library. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#if !_LIBC +# include +#endif + +#include + +#if !_LIBC +# include "tempname.h" +# define __gen_tempname gen_tempname +# ifndef __GTFILE +# define __GT_FILE GT_FILE +# endif +#endif + +#include + +#ifndef __GT_FILE +# define __GT_FILE 0 +#endif + +/* Generate a unique temporary file name from XTEMPLATE. + The last six characters of XTEMPLATE must be "XXXXXX"; + they are replaced with a string that makes the file name unique. + Then open the file and return a fd. */ +int +mkostemp (char *xtemplate, int flags) +{ + return __gen_tempname (xtemplate, 0, flags, __GT_FILE); +} === added file 'lib/secure_getenv.c' --- lib/secure_getenv.c 1970-01-01 00:00:00 +0000 +++ lib/secure_getenv.c 2013-08-03 07:19:27 +0000 @@ -0,0 +1,41 @@ +/* Look up an environment variable more securely. + + Copyright 2013 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include + +#if !HAVE___SECURE_GETENV +# if HAVE_ISSETUGID +# include +# else +# undef issetugid +# define issetugid() 1 +# endif +#endif + +char * +secure_getenv (char const *name) +{ +#if HAVE___SECURE_GETENV + return __secure_getenv (name); +#else + if (issetugid ()) + return 0; + return getenv (name); +#endif +} === added file 'lib/tempname.c' --- lib/tempname.c 1970-01-01 00:00:00 +0000 +++ lib/tempname.c 2013-08-03 07:19:27 +0000 @@ -0,0 +1,306 @@ +/* tempname.c - generate the name of a temporary file. + + Copyright (C) 1991-2003, 2005-2007, 2009-2013 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Extracted from glibc sysdeps/posix/tempname.c. See also tmpdir.c. */ + +#if !_LIBC +# include +# include "tempname.h" +#endif + +#include +#include + +#include +#ifndef __set_errno +# define __set_errno(Val) errno = (Val) +#endif + +#include +#ifndef P_tmpdir +# define P_tmpdir "/tmp" +#endif +#ifndef TMP_MAX +# define TMP_MAX 238328 +#endif +#ifndef __GT_FILE +# define __GT_FILE 0 +# define __GT_DIR 1 +# define __GT_NOCREATE 2 +#endif +#if !_LIBC && (GT_FILE != __GT_FILE || GT_DIR != __GT_DIR \ + || GT_NOCREATE != __GT_NOCREATE) +# error report this to bug-gnulib@gnu.org +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#if _LIBC +# define struct_stat64 struct stat64 +#else +# define struct_stat64 struct stat +# define __gen_tempname gen_tempname +# define __getpid getpid +# define __gettimeofday gettimeofday +# define __mkdir mkdir +# define __open open +# define __lxstat64(version, file, buf) lstat (file, buf) +# define __secure_getenv secure_getenv +#endif + +#ifdef _LIBC +# include +# if HP_TIMING_AVAIL +# define RANDOM_BITS(Var) \ + if (__builtin_expect (value == UINT64_C (0), 0)) \ + { \ + /* If this is the first time this function is used initialize \ + the variable we accumulate the value in to some somewhat \ + random value. If we'd not do this programs at startup time \ + might have a reduced set of possible names, at least on slow \ + machines. */ \ + struct timeval tv; \ + __gettimeofday (&tv, NULL); \ + value = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec; \ + } \ + HP_TIMING_NOW (Var) +# endif +#endif + +/* Use the widest available unsigned type if uint64_t is not + available. The algorithm below extracts a number less than 62**6 + (approximately 2**35.725) from uint64_t, so ancient hosts where + uintmax_t is only 32 bits lose about 3.725 bits of randomness, + which is better than not having mkstemp at all. */ +#if !defined UINT64_MAX && !defined uint64_t +# define uint64_t uintmax_t +#endif + +#if _LIBC +/* Return nonzero if DIR is an existent directory. */ +static int +direxists (const char *dir) +{ + struct_stat64 buf; + return __xstat64 (_STAT_VER, dir, &buf) == 0 && S_ISDIR (buf.st_mode); +} + +/* Path search algorithm, for tmpnam, tmpfile, etc. If DIR is + non-null and exists, uses it; otherwise uses the first of $TMPDIR, + P_tmpdir, /tmp that exists. Copies into TMPL a template suitable + for use with mk[s]temp. Will fail (-1) if DIR is non-null and + doesn't exist, none of the searched dirs exists, or there's not + enough space in TMPL. */ +int +__path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx, + int try_tmpdir) +{ + const char *d; + size_t dlen, plen; + + if (!pfx || !pfx[0]) + { + pfx = "file"; + plen = 4; + } + else + { + plen = strlen (pfx); + if (plen > 5) + plen = 5; + } + + if (try_tmpdir) + { + d = __secure_getenv ("TMPDIR"); + if (d != NULL && direxists (d)) + dir = d; + else if (dir != NULL && direxists (dir)) + /* nothing */ ; + else + dir = NULL; + } + if (dir == NULL) + { + if (direxists (P_tmpdir)) + dir = P_tmpdir; + else if (strcmp (P_tmpdir, "/tmp") != 0 && direxists ("/tmp")) + dir = "/tmp"; + else + { + __set_errno (ENOENT); + return -1; + } + } + + dlen = strlen (dir); + while (dlen > 1 && dir[dlen - 1] == '/') + dlen--; /* remove trailing slashes */ + + /* check we have room for "${dir}/${pfx}XXXXXX\0" */ + if (tmpl_len < dlen + 1 + plen + 6 + 1) + { + __set_errno (EINVAL); + return -1; + } + + sprintf (tmpl, "%.*s/%.*sXXXXXX", (int) dlen, dir, (int) plen, pfx); + return 0; +} +#endif /* _LIBC */ + +/* These are the characters used in temporary file names. */ +static const char letters[] = +"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + +/* Generate a temporary file name based on TMPL. TMPL must match the + rules for mk[s]temp (i.e. end in "XXXXXX", possibly with a suffix). + The name constructed does not exist at the time of the call to + __gen_tempname. TMPL is overwritten with the result. + + KIND may be one of: + __GT_NOCREATE: simply verify that the name does not exist + at the time of the call. + __GT_FILE: create the file using open(O_CREAT|O_EXCL) + and return a read-write fd. The file is mode 0600. + __GT_DIR: create a directory, which will be mode 0700. + + We use a clever algorithm to get hard-to-predict names. */ +int +__gen_tempname (char *tmpl, int suffixlen, int flags, int kind) +{ + int len; + char *XXXXXX; + static uint64_t value; + uint64_t random_time_bits; + unsigned int count; + int fd = -1; + int save_errno = errno; + struct_stat64 st; + + /* A lower bound on the number of temporary files to attempt to + generate. The maximum total number of temporary file names that + can exist for a given template is 62**6. It should never be + necessary to try all of these combinations. Instead if a reasonable + number of names is tried (we define reasonable as 62**3) fail to + give the system administrator the chance to remove the problems. */ +#define ATTEMPTS_MIN (62 * 62 * 62) + + /* The number of times to attempt to generate a temporary file. To + conform to POSIX, this must be no smaller than TMP_MAX. */ +#if ATTEMPTS_MIN < TMP_MAX + unsigned int attempts = TMP_MAX; +#else + unsigned int attempts = ATTEMPTS_MIN; +#endif + + len = strlen (tmpl); + if (len < 6 + suffixlen || memcmp (&tmpl[len - 6 - suffixlen], "XXXXXX", 6)) + { + __set_errno (EINVAL); + return -1; + } + + /* This is where the Xs start. */ + XXXXXX = &tmpl[len - 6 - suffixlen]; + + /* Get some more or less random data. */ +#ifdef RANDOM_BITS + RANDOM_BITS (random_time_bits); +#else + { + struct timeval tv; + __gettimeofday (&tv, NULL); + random_time_bits = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec; + } +#endif + value += random_time_bits ^ __getpid (); + + for (count = 0; count < attempts; value += 7777, ++count) + { + uint64_t v = value; + + /* Fill in the random bits. */ + XXXXXX[0] = letters[v % 62]; + v /= 62; + XXXXXX[1] = letters[v % 62]; + v /= 62; + XXXXXX[2] = letters[v % 62]; + v /= 62; + XXXXXX[3] = letters[v % 62]; + v /= 62; + XXXXXX[4] = letters[v % 62]; + v /= 62; + XXXXXX[5] = letters[v % 62]; + + switch (kind) + { + case __GT_FILE: + fd = __open (tmpl, + (flags & ~O_ACCMODE) + | O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + break; + + case __GT_DIR: + fd = __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR); + break; + + case __GT_NOCREATE: + /* This case is backward from the other three. __gen_tempname + succeeds if __xstat fails because the name does not exist. + Note the continue to bypass the common logic at the bottom + of the loop. */ + if (__lxstat64 (_STAT_VER, tmpl, &st) < 0) + { + if (errno == ENOENT) + { + __set_errno (save_errno); + return 0; + } + else + /* Give up now. */ + return -1; + } + continue; + + default: + assert (! "invalid KIND in __gen_tempname"); + abort (); + } + + if (fd >= 0) + { + __set_errno (save_errno); + return fd; + } + else if (errno != EEXIST) + return -1; + } + + /* We got out of the loop because we ran out of combinations to try. */ + __set_errno (EEXIST); + return -1; +} === added file 'lib/tempname.h' --- lib/tempname.h 1970-01-01 00:00:00 +0000 +++ lib/tempname.h 2013-08-03 07:19:27 +0000 @@ -0,0 +1,50 @@ +/* Create a temporary file or directory. + + Copyright (C) 2006, 2009-2013 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* header written by Eric Blake */ + +#ifndef GL_TEMPNAME_H +# define GL_TEMPNAME_H + +# include + +# ifdef __GT_FILE +# define GT_FILE __GT_FILE +# define GT_DIR __GT_DIR +# define GT_NOCREATE __GT_NOCREATE +# else +# define GT_FILE 0 +# define GT_DIR 1 +# define GT_NOCREATE 2 +# endif + +/* Generate a temporary file name based on TMPL. TMPL must match the + rules for mk[s]temp (i.e. end in "XXXXXX", possibly with a suffix). + The name constructed does not exist at the time of the call to + gen_tempname. TMPL is overwritten with the result. + + KIND may be one of: + GT_NOCREATE: simply verify that the name does not exist + at the time of the call. + GT_FILE: create a large file using open(O_CREAT|O_EXCL) + and return a read-write fd. The file is mode 0600. + GT_DIR: create a directory, which will be mode 0700. + + We use a clever algorithm to get hard-to-predict names. */ +extern int gen_tempname (char *tmpl, int suffixlen, int flags, int kind); + +#endif /* GL_TEMPNAME_H */ === modified file 'm4/gnulib-comp.m4' --- m4/gnulib-comp.m4 2013-07-09 17:16:21 +0000 +++ m4/gnulib-comp.m4 2013-08-03 07:19:27 +0000 @@ -89,6 +89,7 @@ # Code from module lstat: # Code from module manywarnings: # Code from module memrchr: + # Code from module mkostemp: # Code from module mktime: # Code from module multiarch: # Code from module nocrash: @@ -102,6 +103,7 @@ # Code from module readlink: # Code from module readlinkat: # Code from module root-uid: + # Code from module secure_getenv: # Code from module sig2str: # Code from module signal-h: # Code from module snippet/_Noreturn: @@ -134,6 +136,7 @@ # Code from module sys_select: # Code from module sys_stat: # Code from module sys_time: + # Code from module tempname: # Code from module time: # Code from module time_r: # Code from module timer-time: @@ -274,6 +277,13 @@ gl_PREREQ_MEMRCHR fi gl_STRING_MODULE_INDICATOR([memrchr]) + gl_FUNC_MKOSTEMP + if test $HAVE_MKOSTEMP = 0; then + AC_LIBOBJ([mkostemp]) + gl_PREREQ_MKOSTEMP + fi + gl_MODULE_INDICATOR([mkostemp]) + gl_STDLIB_MODULE_INDICATOR([mkostemp]) gl_FUNC_MKTIME if test $REPLACE_MKTIME = 1; then AC_LIBOBJ([mktime]) @@ -381,9 +391,11 @@ gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=false gl_gnulib_enabled_pathmax=false gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false + gl_gnulib_enabled_secure_getenv=false gl_gnulib_enabled_stat=false gl_gnulib_enabled_strtoll=false gl_gnulib_enabled_strtoull=false + gl_gnulib_enabled_tempname=false gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b () { @@ -485,6 +497,18 @@ gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=true fi } + func_gl_gnulib_m4code_secure_getenv () + { + if ! $gl_gnulib_enabled_secure_getenv; then + gl_FUNC_SECURE_GETENV + if test $HAVE_SECURE_GETENV = 0; then + AC_LIBOBJ([secure_getenv]) + gl_PREREQ_SECURE_GETENV + fi + gl_STDLIB_MODULE_INDICATOR([secure_getenv]) + gl_gnulib_enabled_secure_getenv=true + fi + } func_gl_gnulib_m4code_stat () { if ! $gl_gnulib_enabled_stat; then @@ -527,6 +551,14 @@ gl_gnulib_enabled_strtoull=true fi } + func_gl_gnulib_m4code_tempname () + { + if ! $gl_gnulib_enabled_tempname; then + gl_FUNC_GEN_TEMPNAME + gl_gnulib_enabled_tempname=true + func_gl_gnulib_m4code_secure_getenv + fi + } func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec () { if ! $gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec; then @@ -569,6 +601,9 @@ if test $REPLACE_LSTAT = 1; then func_gl_gnulib_m4code_stat fi + if test $HAVE_MKOSTEMP = 0; then + func_gl_gnulib_m4code_tempname + fi if test $HAVE_READLINK = 0 || test $REPLACE_READLINK = 1; then func_gl_gnulib_m4code_stat fi @@ -598,9 +633,11 @@ AM_CONDITIONAL([gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7], [$gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7]) AM_CONDITIONAL([gl_GNULIB_ENABLED_pathmax], [$gl_gnulib_enabled_pathmax]) AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c]) + AM_CONDITIONAL([gl_GNULIB_ENABLED_secure_getenv], [$gl_gnulib_enabled_secure_getenv]) AM_CONDITIONAL([gl_GNULIB_ENABLED_stat], [$gl_gnulib_enabled_stat]) AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoll], [$gl_gnulib_enabled_strtoll]) AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoull], [$gl_gnulib_enabled_strtoull]) + AM_CONDITIONAL([gl_GNULIB_ENABLED_tempname], [$gl_gnulib_enabled_tempname]) AM_CONDITIONAL([gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec], [$gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec]) # End of code from modules m4_ifval(gl_LIBSOURCES_LIST, [ @@ -806,6 +843,7 @@ lib/md5.c lib/md5.h lib/memrchr.c + lib/mkostemp.c lib/mktime-internal.h lib/mktime.c lib/openat-priv.h @@ -821,6 +859,7 @@ lib/readlink.c lib/readlinkat.c lib/root-uid.h + lib/secure_getenv.c lib/sha1.c lib/sha1.h lib/sha256.c @@ -853,6 +892,8 @@ lib/sys_select.in.h lib/sys_stat.in.h lib/sys_time.in.h + lib/tempname.c + lib/tempname.h lib/time.in.h lib/time_r.c lib/timespec-add.c @@ -908,6 +949,7 @@ m4/manywarnings.m4 m4/md5.m4 m4/memrchr.m4 + m4/mkostemp.m4 m4/mktime.m4 m4/multiarch.m4 m4/nocrash.m4 @@ -919,6 +961,7 @@ m4/putenv.m4 m4/readlink.m4 m4/readlinkat.m4 + m4/secure_getenv.m4 m4/setenv.m4 m4/sha1.m4 m4/sha256.m4 @@ -948,6 +991,7 @@ m4/sys_socket_h.m4 m4/sys_stat_h.m4 m4/sys_time_h.m4 + m4/tempname.m4 m4/time_h.m4 m4/time_r.m4 m4/timer_time.m4 === added file 'm4/mkostemp.m4' --- m4/mkostemp.m4 1970-01-01 00:00:00 +0000 +++ m4/mkostemp.m4 2013-08-03 07:19:27 +0000 @@ -0,0 +1,23 @@ +# mkostemp.m4 serial 2 +dnl Copyright (C) 2009-2013 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_FUNC_MKOSTEMP], +[ + AC_REQUIRE([gl_STDLIB_H_DEFAULTS]) + + dnl Persuade glibc to declare mkostemp(). + AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) + + AC_CHECK_FUNCS_ONCE([mkostemp]) + if test $ac_cv_func_mkostemp != yes; then + HAVE_MKOSTEMP=0 + fi +]) + +# Prerequisites of lib/mkostemp.c. +AC_DEFUN([gl_PREREQ_MKOSTEMP], +[ +]) === added file 'm4/secure_getenv.m4' --- m4/secure_getenv.m4 1970-01-01 00:00:00 +0000 +++ m4/secure_getenv.m4 2013-08-03 07:19:27 +0000 @@ -0,0 +1,25 @@ +# Look up an environment variable more securely. +dnl Copyright 2013 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_FUNC_SECURE_GETENV], +[ + dnl Persuade glibc to declare secure_getenv(). + AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) + + AC_REQUIRE([gl_STDLIB_H_DEFAULTS]) + AC_CHECK_FUNCS_ONCE([secure_getenv]) + if test $ac_cv_func_secure_getenv = no; then + HAVE_SECURE_GETENV=0 + fi +]) + +# Prerequisites of lib/secure_getenv.c. +AC_DEFUN([gl_PREREQ_SECURE_GETENV], [ + AC_CHECK_FUNCS([__secure_getenv]) + if test $ac_cv_func___secure_getenv = no; then + AC_CHECK_FUNCS([issetugid]) + fi +]) === added file 'm4/tempname.m4' --- m4/tempname.m4 1970-01-01 00:00:00 +0000 +++ m4/tempname.m4 2013-08-03 07:19:27 +0000 @@ -0,0 +1,19 @@ +#serial 5 + +# Copyright (C) 2006-2007, 2009-2013 Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# glibc provides __gen_tempname as a wrapper for mk[ds]temp. Expose +# it as a public API, and provide it on systems that are lacking. +AC_DEFUN([gl_FUNC_GEN_TEMPNAME], +[ + gl_PREREQ_TEMPNAME +]) + +# Prerequisites of lib/tempname.c. +AC_DEFUN([gl_PREREQ_TEMPNAME], +[ + : +]) === modified file 'src/ChangeLog' --- src/ChangeLog 2013-08-03 03:29:03 +0000 +++ src/ChangeLog 2013-08-03 07:19:27 +0000 @@ -1,3 +1,10 @@ +2013-08-03 Paul Eggert + + Fix some minor races in hosts lacking mkostemp. + * callproc.c (create_temp_file): + * filelock.c (create_lock_file): + Assume mkostemp, since it's now provided by Gnulib. + 2013-08-03 Dmitry Antipov Drop FRAME_PTR typedef. === modified file 'src/callproc.c' --- src/callproc.c 2013-07-30 21:44:43 +0000 +++ src/callproc.c 2013-08-03 07:19:27 +0000 @@ -1009,23 +1009,11 @@ tempfile = SSDATA (filename_string); { - int fd; - -#ifdef HAVE_MKOSTEMP - fd = mkostemp (tempfile, O_CLOEXEC); -#elif defined HAVE_MKSTEMP - fd = mkstemp (tempfile); -#else - errno = EEXIST; - mktemp (tempfile); - fd = *tempfile ? 0 : -1; -#endif + int fd = mkostemp (tempfile, O_CLOEXEC); if (fd < 0) report_file_error ("Failed to open temporary file using pattern", pattern); -#if defined HAVE_MKOSTEMP || defined HAVE_MKSTEMP emacs_close (fd); -#endif } record_unwind_protect (delete_temp_file, filename_string); === modified file 'src/filelock.c' --- src/filelock.c 2013-07-19 18:09:23 +0000 +++ src/filelock.c 2013-08-03 07:35:27 +0000 @@ -411,28 +411,14 @@ memcpy (nonce, lfname, lfdirlen); strcpy (nonce + lfdirlen, nonce_base); -#if HAVE_MKOSTEMP - /* Prefer mkostemp to mkstemp, as it avoids a window where FD is - temporarily open without close-on-exec. */ fd = mkostemp (nonce, O_BINARY | O_CLOEXEC); -#elif HAVE_MKSTEMP - /* Prefer mkstemp to mktemp, as it avoids a race between - mktemp and emacs_open. */ - fd = mkstemp (nonce); -#else - mktemp (nonce); - fd = emacs_open (nonce, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, - S_IRUSR | S_IWUSR); -#endif - if (fd < 0) err = errno; else { ptrdiff_t lock_info_len; -#if ! (HAVE_MKOSTEMP && O_CLOEXEC) - fcntl (fd, F_SETFD, FD_CLOEXEC); -#endif + if (! O_CLOEXEC) + fcntl (fd, F_SETFD, FD_CLOEXEC); lock_info_len = strlen (lock_info_str); err = 0; /* Use 'write', not 'emacs_write', as garbage collection