From patchwork Tue Sep 29 12:55:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella X-Patchwork-Id: 40544 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id B866F3851C06; Tue, 29 Sep 2020 12:55:56 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B866F3851C06 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1601384156; bh=JPA8fA5SsfAoLpkry7UKJ/wcoFGwakUxsb78fKz82ZA=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=jLB1K+I49x0ebXB1K+wrp0YrlU1oZAOQtLJjNr/ET96tAkrqoLExMDJdnHhmCTPsm YaQ+igHuSGYfQrzZm8kqFecBVxpj7qbdMXaIFxQIxSrt0WFPsB9eLZU4i8YzELEE/o tI8KZEyC6AIUwLNcfYbAdkkRtSO8EkQb36Cc0GRY= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-qk1-x743.google.com (mail-qk1-x743.google.com [IPv6:2607:f8b0:4864:20::743]) by sourceware.org (Postfix) with ESMTPS id A319A3857C65 for ; Tue, 29 Sep 2020 12:55:52 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org A319A3857C65 Received: by mail-qk1-x743.google.com with SMTP id x201so3748515qkb.11 for ; Tue, 29 Sep 2020 05:55:52 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=JPA8fA5SsfAoLpkry7UKJ/wcoFGwakUxsb78fKz82ZA=; b=d8KkNylBIWjBu9Y9F1u9WNFAkAFE0rQtLjw6Hlr9hRlG7Rh9lpC+hNFqFgRtC4LYs5 j3i1r+npAOgzLigHUTqVy+GA/8L7XmEyxBgUUk3fi6ArFGi2hKedXQqgGA5dylJd6YPI KygvAW2r9dL6+S0jXSwUVlLrdskozx8LWlwFdbn8s4SMj8fzn3M5cxlAaKbkkWBcffgN xUs/LQBFfFBIGMnXxvMumxXhoIO6Zypd0Ehm5/LVuB7tFR0uaE3mPFkLzpoSUC5IPGS/ TXOdQwJPNtli+OgCiWjeNWa1/VVAPgGXWTHrSdOvlWtn+pZHohAjB77n748e7fmD7e2U cTlw== X-Gm-Message-State: AOAM531sG4EXkcN2hFHXWN9sb32+U7tvoMhXmReG6q13ucbM/yOnZyzq v8owsqC4qz2pjwCCxO6V1uIGKJrNHgnDgw== X-Google-Smtp-Source: ABdhPJzo9dD54VrZMonbBXwQHUqo9Wzbb85iELJ2MJUie6mdLJ4Cs6VEYE42HaL3XxMJxPcqsaTHWQ== X-Received: by 2002:a05:620a:716:: with SMTP id 22mr4279558qkc.356.1601384151068; Tue, 29 Sep 2020 05:55:51 -0700 (PDT) Received: from localhost.localdomain ([177.194.48.209]) by smtp.googlemail.com with ESMTPSA id l19sm5549658qtu.16.2020.09.29.05.55.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 29 Sep 2020 05:55:50 -0700 (PDT) To: libc-alpha@sourceware.org Subject: [PATCH 1/2] posix: Sync tempname with gnulib [BZ #26648] Date: Tue, 29 Sep 2020 09:55:45 -0300 Message-Id: <20200929125546.3413273-1-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.25.1 MIME-Version: 1.0 X-Spam-Status: No, score=-13.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Adhemerval Zanella via Libc-alpha From: Adhemerval Zanella Reply-To: Adhemerval Zanella Cc: Jakub Jelinek Errors-To: libc-alpha-bounces@sourceware.org Sender: "Libc-alpha" 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 @@ . */ #if !_LIBC -# include +# include # include "tempname.h" #endif @@ -24,9 +24,6 @@ #include #include -#ifndef __set_errno -# define __set_errno(Val) errno = (Val) -#endif #include #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 #include -#include +#include #include -#include - +#include #include +#include #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 -# 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