[libc-commits] [libc] [libc] Add tmpnam implementation (PR #204901)
Jeff Bailey via libc-commits
libc-commits at lists.llvm.org
Sat Aug 1 00:24:29 PDT 2026
================
@@ -0,0 +1,96 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Declaration of tmpnam, a POSIX function that generate a string that is a
+/// valid pathname that does not name an existing file.
+/// See:
+/// https://pubs.opengroup.org/onlinepubs/9799919799/functions/tmpnam.html
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/stdio/tmpnam.h"
+#include "hdr/errno_macros.h"
+#include "hdr/stdio_macros.h"
+#include "hdr/unistd_macros.h"
+#include "src/__support/CPP/atomic.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/access.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/getrandom.h"
+#include "src/__support/macros/config.h"
+#include "src/string/memory_utils/inline_memcpy.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+static char tmpbuf[L_tmpnam];
+static cpp::Atomic<size_t> tmpnam_budget = TMP_MAX;
+
+// Partially thread-safe:
+// - When null is handed it is not thread-safe.
+// - We do some work to ensure that cases where we need to use tmpnam_budget
+// that we lock around it.
+LLVM_LIBC_FUNCTION(char *, tmpnam, (char *s)) {
+ if (s == nullptr)
+ s = tmpbuf;
+
+ // here if the s is null then use tmpbuf and if sizeof
+ // POSIX portable filename character set, sorted by ASCII value.
+ // See
+ // https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap03.html#tag_03_265
+ const char charset[] = "-.0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "_"
+ "abcdefghijklmnopqrstuvwxyz";
+
+ // We want to construct: P_tmpdir / <14 random chars> \0
+ // P_tmpdir is "/tmp" (length 4).
+ // Total length must be L_tmpnam (20).
+ // /tmp/ is 5 chars.
+ // Random suffix is 14 chars.
+ // Null terminator is 1 char.
+ // Total: 5 + 14 + 1 = 20.
+ constexpr cpp::string_view PREFIX = P_tmpdir "/";
+ static_assert(PREFIX.size() + 14 + 1 == L_tmpnam, "L_tmpnam mismatch");
+ inline_memcpy(s, PREFIX.data(), PREFIX.size());
+ constexpr size_t PREFIX_SIZE = PREFIX.size();
+ constexpr size_t SUFFIX_SIZE = 14;
+
+ bool is_unique = false;
+ while (!is_unique) {
+ size_t curr_budget = tmpnam_budget.load(cpp::MemoryOrder::RELAXED);
+
+ do {
+ if (curr_budget == 0)
+ break;
+ } while (
+ !tmpnam_budget.compare_exchange_strong(curr_budget, curr_budget - 1));
+
+ if (curr_budget == 0)
+ break;
+
+ uint8_t rand_bytes[L_tmpnam];
+ auto ret = linux_syscalls::getrandom(rand_bytes, SUFFIX_SIZE, 0);
+ if (!ret.has_value()) {
+ /* return nullptr when getrandom fails but consume tmpnam budget */
+ return nullptr;
+ }
+
+ for (size_t i = 0; i < SUFFIX_SIZE; i++) {
----------------
kaladron wrote:
This loop has a slight modulo bias. sizeof(charset) - 1 is 65. Since 256 = 3 × 65 + 61, taking rand_bytes[i] % 65 introduces a slight modulo bias (the first 61 characters are slightly more likely to be chosen than the last 4).
But also while we're looking at it - this is currently 65 characters. This means that the compiler will have to operate less efficiently. I'd suggest reducing one character in exchange for performance:
```c++
constexpr char CHARSET[] = "0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"_.";
constexpr size_t CHARSET_SIZE = sizeof(CHARSET) - 1; // 64 (excluding '\0')
static_assert((CHARSET_SIZE & (CHARSET_SIZE - 1)) == 0,
"CHARSET_SIZE must be a power of 2");
constexpr size_t MASK = CHARSET_SIZE - 1; // 63
char *suffix = s + PREFIX_SIZE;
for (size_t i = 0; i < SUFFIX_SIZE; ++i)
suffix[i] = CHARSET[rand_bytes[i] & MASK];
```
https://github.com/llvm/llvm-project/pull/204901
More information about the libc-commits
mailing list