[PATCH 1/2] posix: Sync tempname with gnulib [BZ #26648]

Adhemerval Zanella adhemerval.zanella@linaro.org
Mon Oct 26 19:46:30 GMT 2020


Ping.

On 29/09/2020 09:55, Adhemerval Zanella wrote:
> It syncs with commit 6160ee8e4d2b88d934c3c4c8c5930a75b835723f.  It
> now uses getrandom on each iteration to get entropy and only uses
> the clock as source of entropy if getrandom fails.
> 
> Checked on x86_64-linux-gnu.
> ---
>  sysdeps/posix/tempname.c | 280 +++++++++++++++++++++++----------------
>  1 file changed, 163 insertions(+), 117 deletions(-)
> 
> diff --git a/sysdeps/posix/tempname.c b/sysdeps/posix/tempname.c
> index 1864c860ad..03426c23cf 100644
> --- a/sysdeps/posix/tempname.c
> +++ b/sysdeps/posix/tempname.c
> @@ -16,7 +16,7 @@
>     <https://www.gnu.org/licenses/>.  */
>  
>  #if !_LIBC
> -# include <config.h>
> +# include <libc-config.h>
>  # include "tempname.h"
>  #endif
>  
> @@ -24,9 +24,6 @@
>  #include <assert.h>
>  
>  #include <errno.h>
> -#ifndef __set_errno
> -# define __set_errno(Val) errno = (Val)
> -#endif
>  
>  #include <stdio.h>
>  #ifndef P_tmpdir
> @@ -36,12 +33,12 @@
>  # define TMP_MAX 238328
>  #endif
>  #ifndef __GT_FILE
> -# define __GT_FILE	0
> -# define __GT_DIR	1
> -# define __GT_NOCREATE	2
> +# 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)
> +#if !_LIBC && (GT_FILE != __GT_FILE || GT_DIR != __GT_DIR       \
> +               || GT_NOCREATE != __GT_NOCREATE)
>  # error report this to bug-gnulib@gnu.org
>  #endif
>  
> @@ -50,11 +47,11 @@
>  #include <string.h>
>  
>  #include <fcntl.h>
> -#include <time.h>
> +#include <stdalign.h>
>  #include <stdint.h>
> -#include <unistd.h>
> -
> +#include <sys/random.h>
>  #include <sys/stat.h>
> +#include <time.h>
>  
>  #if _LIBC
>  # define struct_stat64 struct stat64
> @@ -62,32 +59,36 @@
>  #else
>  # define struct_stat64 struct stat
>  # define __gen_tempname gen_tempname
> -# define __getpid getpid
>  # define __mkdir mkdir
>  # define __open open
> -# define __secure_getenv secure_getenv
> +# define __lxstat64(version, file, buf) lstat (file, buf)
> +# define __getrandom getrandom
> +# define __clock_gettime64 clock_gettime
> +# define __timespec64 timespec
>  #endif
>  
> -#ifdef _LIBC
> -# include <random-bits.h>
> -# define RANDOM_BITS(Var) ((Var) = random_bits ())
> -# else
> -# define RANDOM_BITS(Var) \
> -    {                                                                         \
> -      struct timespec ts;                                                     \
> -      clock_gettime (CLOCK_REALTIME, &ts);                                    \
> -      (Var) = ((uint64_t) tv.tv_nsec << 16) ^ tv.tv_sec;                      \
> -    }
> -#endif
> +/* Use getrandom if it works, falling back on a 64-bit linear
> +   congruential generator that starts with Var's value
> +   mixed in with a clock's low-order bits if available.  */
> +typedef uint_fast64_t random_value;
> +#define RANDOM_VALUE_MAX UINT_FAST64_MAX
> +#define BASE_62_DIGITS 10 /* 62**10 < UINT_FAST64_MAX */
> +#define BASE_62_POWER (62LL * 62 * 62 * 62 * 62 * 62 * 62 * 62 * 62 * 62)
>  
> -/* 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
> +static random_value
> +random_bits (random_value var)
> +{
> +  random_value r;
> +  if (__getrandom (&r, sizeof r, 0) == sizeof r)
> +    return r;
> +#if _LIBC || (defined CLOCK_MONOTONIC && HAVE_CLOCK_GETTIME)
> +  /* Add entropy if getrandom is not supported.  */
> +  struct __timespec64 tv;
> +  __clock_gettime64 (CLOCK_MONOTONIC, &tv);
> +  var ^= tv.tv_nsec;
>  #endif
> +  return 2862933555777941757 * var + 3037000493;
> +}
>  
>  #if _LIBC
>  /* Return nonzero if DIR is an existent directory.  */
> @@ -95,7 +96,7 @@ static int
>  direxists (const char *dir)
>  {
>    struct_stat64 buf;
> -  return __stat64 (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
> +  return __xstat64 (_STAT_VER, dir, &buf) == 0 && S_ISDIR (buf.st_mode);
>  }
>  
>  /* Path search algorithm, for tmpnam, tmpfile, etc.  If DIR is
> @@ -106,7 +107,7 @@ direxists (const char *dir)
>     enough space in TMPL. */
>  int
>  __path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx,
> -	       int try_tmpdir)
> +               int try_tmpdir)
>  {
>    const char *d;
>    size_t dlen, plen;
> @@ -120,35 +121,35 @@ __path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx,
>      {
>        plen = strlen (pfx);
>        if (plen > 5)
> -	plen = 5;
> +        plen = 5;
>      }
>  
>    if (try_tmpdir)
>      {
>        d = __secure_getenv ("TMPDIR");
>        if (d != NULL && direxists (d))
> -	dir = d;
> +        dir = d;
>        else if (dir != NULL && direxists (dir))
> -	/* nothing */ ;
> +        /* nothing */ ;
>        else
> -	dir = NULL;
> +        dir = NULL;
>      }
>    if (dir == NULL)
>      {
>        if (direxists (P_tmpdir))
> -	dir = P_tmpdir;
> +        dir = P_tmpdir;
>        else if (strcmp (P_tmpdir, "/tmp") != 0 && direxists ("/tmp"))
> -	dir = "/tmp";
> +        dir = "/tmp";
>        else
> -	{
> -	  __set_errno (ENOENT);
> -	  return -1;
> -	}
> +        {
> +          __set_errno (ENOENT);
> +          return -1;
> +        }
>      }
>  
>    dlen = strlen (dir);
>    while (dlen > 1 && dir[dlen - 1] == '/')
> -    dlen--;			/* remove trailing slashes */
> +    dlen--;                     /* remove trailing slashes */
>  
>    /* check we have room for "${dir}/${pfx}XXXXXX\0" */
>    if (tmpl_len < dlen + 1 + plen + 6 + 1)
> @@ -162,39 +163,91 @@ __path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx,
>  }
>  #endif /* _LIBC */
>  
> +#if _LIBC
> +static int try_tempname_len (char *, int, void *, int (*) (char *, void *),
> +                             size_t);
> +#endif
> +
> +static int
> +try_file (char *tmpl, void *flags)
> +{
> +  int *openflags = flags;
> +  return __open (tmpl,
> +                 (*openflags & ~O_ACCMODE)
> +                 | O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
> +}
> +
> +static int
> +try_dir (char *tmpl, void *flags _GL_UNUSED)
> +{
> +  return __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR);
> +}
> +
> +static int
> +try_nocreate (char *tmpl, void *flags _GL_UNUSED)
> +{
> +  struct_stat64 st;
> +
> +  if (__lxstat64 (_STAT_VER, tmpl, &st) == 0 || errno == EOVERFLOW)
> +    __set_errno (EEXIST);
> +  return errno == ENOENT ? 0 : -1;
> +}
> +
>  /* 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).
> +   rules for mk[s]temp (i.e., end in at least X_SUFFIX_LEN "X"s,
> +   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.
> +   this function.  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.
> +   __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. */
> +#ifdef _LIBC
> +static
> +#endif
>  int
> -__gen_tempname (char *tmpl, int suffixlen, int flags, int kind)
> +gen_tempname_len (char *tmpl, int suffixlen, int flags, int kind,
> +                  size_t x_suffix_len)
>  {
> -  int len;
> +  static int (*const tryfunc[]) (char *, void *) =
> +    {
> +      [__GT_FILE] = try_file,
> +      [__GT_DIR] = try_dir,
> +      [__GT_NOCREATE] = try_nocreate
> +    };
> +  return try_tempname_len (tmpl, suffixlen, &flags, tryfunc[kind],
> +                           x_suffix_len);
> +}
> +
> +#ifdef _LIBC
> +static
> +#endif
> +int
> +try_tempname_len (char *tmpl, int suffixlen, void *args,
> +                  int (*tryfunc) (char *, void *), size_t x_suffix_len)
> +{
> +  size_t len;
>    char *XXXXXX;
>    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.  */
> +     give the system administrator the chance to remove the problems.
> +     This value requires that X_SUFFIX_LEN be at least 3.  */
>  #define ATTEMPTS_MIN (62 * 62 * 62)
>  
>    /* The number of times to attempt to generate a temporary file.  To
> @@ -205,82 +258,75 @@ __gen_tempname (char *tmpl, int suffixlen, int flags, int kind)
>    unsigned int attempts = ATTEMPTS_MIN;
>  #endif
>  
> +  /* A random variable.  The initial value is used only the for fallback path
> +     on 'random_bits' on 'getrandom' failure.  Its initial value tries to use
> +     some entropy from the ASLR and ignore possible bits from the stack
> +     alignment.  */
> +  random_value v = ((uintptr_t) &v) / alignof (max_align_t);
> +
> +  /* How many random base-62 digits can currently be extracted from V.  */
> +  int vdigits = 0;
> +
> +  /* Least unfair value for V.  If V is less than this, V can generate
> +     BASE_62_DIGITS digits fairly.  Otherwise it might be biased.  */
> +  random_value const unfair_min
> +    = RANDOM_VALUE_MAX - RANDOM_VALUE_MAX % BASE_62_POWER;
> +
>    len = strlen (tmpl);
> -  if (len < 6 + suffixlen || memcmp (&tmpl[len - 6 - suffixlen], "XXXXXX", 6))
> +  if (len < x_suffix_len + suffixlen
> +      || strspn (&tmpl[len - x_suffix_len - suffixlen], "X") < x_suffix_len)
>      {
>        __set_errno (EINVAL);
>        return -1;
>      }
>  
>    /* This is where the Xs start.  */
> -  XXXXXX = &tmpl[len - 6 - suffixlen];
> +  XXXXXX = &tmpl[len - x_suffix_len - suffixlen];
>  
> -  uint64_t pid = (uint64_t) __getpid () << 32;
>    for (count = 0; count < attempts; ++count)
>      {
> -      uint64_t v;
> -      /* Get some more or less random data.  */
> -      RANDOM_BITS (v);
> -      v ^= pid;
> -
> -      /* 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 lstat fails because the name does not exist.
> -	     Note the continue to bypass the common logic at the bottom
> -	     of the loop.  */
> -	  if (__lstat64 (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 ();
> -	}
> -
> +      for (size_t i = 0; i < x_suffix_len; i++)
> +        {
> +          if (vdigits == 0)
> +            {
> +              do
> +                v = random_bits (v);
> +              while (unfair_min <= v);
> +
> +              vdigits = BASE_62_DIGITS;
> +            }
> +
> +          XXXXXX[i] = letters[v % 62];
> +          v /= 62;
> +          vdigits--;
> +        }
> +
> +      fd = tryfunc (tmpl, args);
>        if (fd >= 0)
> -	{
> -	  __set_errno (save_errno);
> -	  return fd;
> -	}
> +        {
> +          __set_errno (save_errno);
> +          return fd;
> +        }
>        else if (errno != EEXIST)
> -	return -1;
> +        return -1;
>      }
>  
>    /* We got out of the loop because we ran out of combinations to try.  */
>    __set_errno (EEXIST);
>    return -1;
>  }
> +
> +int
> +__gen_tempname (char *tmpl, int suffixlen, int flags, int kind)
> +{
> +  return gen_tempname_len (tmpl, suffixlen, flags, kind, 6);
> +}
> +
> +#if !_LIBC
> +int
> +try_tempname (char *tmpl, int suffixlen, void *args,
> +              int (*tryfunc) (char *, void *))
> +{
> +  return try_tempname_len (tmpl, suffixlen, args, tryfunc, 6);
> +}
> +#endif
> 


More information about the Libc-alpha mailing list