[libc-commits] [libc] [libc] implement mkstemp (PR #199220)
Hardik Chona via libc-commits
libc-commits at lists.llvm.org
Sun May 24 10:30:23 PDT 2026
https://github.com/un-pixelated updated https://github.com/llvm/llvm-project/pull/199220
>From e2a374b52978c4b63a482a19646e23625c4a4bef Mon Sep 17 00:00:00 2001
From: Hardik Chona <iamhardikchona at gmail.com>
Date: Fri, 22 May 2026 14:12:32 +0530
Subject: [PATCH] [libc] implement mkstemp
[fix] stdc -> posix
Fix headers in CMakeLists.txt and clean up testcase file
Changed EXPECTs to ASSERTs
formatting changes
[libc] implement mkstemp
Added new line at the end of files
[libc] implement mkstemp
[fix] charset expanded
[refactor] one rand_byte at a time
[fix] added mkstemp to aarch64 entrypoint
[fix] added mkstemp to riscv entrypoints
[fix] add newline to eof
[fix] add strdup and signal_macros to mkstemp depends
[refactor] refactor tests
[format] formatting change
[fix] dangling pointer bug
[fix] unsigned value bug
[fix] use len
[libc] implement mkstemp
[refactor] simplify more than six X test
---
libc/config/linux/aarch64/entrypoints.txt | 1 +
libc/config/linux/riscv/entrypoints.txt | 1 +
libc/config/linux/x86_64/entrypoints.txt | 1 +
libc/include/stdlib.yaml | 6 +
libc/src/stdlib/CMakeLists.txt | 17 ++
libc/src/stdlib/mkstemp.cpp | 70 +++++++++
libc/src/stdlib/mkstemp.h | 20 +++
libc/test/src/stdlib/CMakeLists.txt | 20 +++
libc/test/src/stdlib/mkstemp_test.cpp | 182 ++++++++++++++++++++++
9 files changed, 318 insertions(+)
create mode 100644 libc/src/stdlib/mkstemp.cpp
create mode 100644 libc/src/stdlib/mkstemp.h
create mode 100644 libc/test/src/stdlib/mkstemp_test.cpp
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 9994a9294173d..40f856c3790d0 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -194,6 +194,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.stdlib.llabs
libc.src.stdlib.lldiv
libc.src.stdlib.memalignment
+ libc.src.stdlib.mkstemp
libc.src.stdlib.qsort
libc.src.stdlib.qsort_r
libc.src.stdlib.rand
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 2748b2b8e6a5d..1724cdcda4c87 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -196,6 +196,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.stdlib.llabs
libc.src.stdlib.lldiv
libc.src.stdlib.memalignment
+ libc.src.stdlib.mkstemp
libc.src.stdlib.qsort
libc.src.stdlib.qsort_r
libc.src.stdlib.rand
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 4b551ced82138..3f203f16195d4 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -198,6 +198,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.stdlib.llabs
libc.src.stdlib.lldiv
libc.src.stdlib.memalignment
+ libc.src.stdlib.mkstemp
libc.src.stdlib.qsort
libc.src.stdlib.qsort_r
libc.src.stdlib.rand
diff --git a/libc/include/stdlib.yaml b/libc/include/stdlib.yaml
index 4c958cd9d28ad..06b57f0968329 100644
--- a/libc/include/stdlib.yaml
+++ b/libc/include/stdlib.yaml
@@ -164,6 +164,12 @@ functions:
return_type: size_t
arguments:
- type: const void *
+ - name: mkstemp
+ standards:
+ - posix
+ return_type: int
+ arguments:
+ - type: char *
- name: posix_memalign
standards:
- posix
diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt
index e79353eb2a581..15fa9457b26c0 100644
--- a/libc/src/stdlib/CMakeLists.txt
+++ b/libc/src/stdlib/CMakeLists.txt
@@ -397,6 +397,23 @@ add_entrypoint_object(
libc.hdr.types.size_t
)
+add_entrypoint_object(
+ mkstemp
+ SRCS
+ mkstemp.cpp
+ HDRS
+ mkstemp.h
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.fcntl_macros
+ libc.src.__support.OSUtil.linux.syscall_wrappers.getrandom
+ libc.src.__support.OSUtil.linux.syscall_wrappers.open
+ libc.src.__support.common
+ libc.src.__support.libc_errno
+ libc.src.__support.macros.config
+ libc.src.__support.macros.null_check
+)
+
add_entrypoint_object(
mbtowc
SRCS
diff --git a/libc/src/stdlib/mkstemp.cpp b/libc/src/stdlib/mkstemp.cpp
new file mode 100644
index 0000000000000..7c7cdec4970f7
--- /dev/null
+++ b/libc/src/stdlib/mkstemp.cpp
@@ -0,0 +1,70 @@
+//===-- Implementation of mkstemp -----------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/stdlib/mkstemp.h"
+#include "hdr/errno_macros.h"
+#include "hdr/fcntl_macros.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/getrandom.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/open.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, mkstemp, (char *tmpl)) {
+ LIBC_CRASH_ON_NULLPTR(tmpl);
+
+ cpp::string_view str_view(tmpl);
+ size_t count = 0;
+ size_t len = str_view.size();
+
+ for (size_t i = len; i > 0; i--) {
+ if (str_view[i - 1] != 'X')
+ break;
+ count++;
+ }
+
+ if (count < 6) {
+ libc_errno = EINVAL;
+ return -1;
+ }
+
+ char *suffix = tmpl + len - count;
+
+ const char charset[] = "QWERTYUIOPASDFGHJKLZXCVBNM"
+ "qwertyuiopasdfghjklzxcvbnm"
+ "1234567890"
+ "._-";
+
+ while (true) {
+ for (size_t i = 0; i < count; i++) {
+ uint8_t rand_byte;
+ auto ret = linux_syscalls::getrandom(&rand_byte, 1, 0);
+ if (!ret.has_value()) {
+ libc_errno = ret.error();
+ return -1;
+ }
+ suffix[i] = charset[rand_byte % (26 + 26 + 10 + 3)];
+ }
+
+ auto fd = linux_syscalls::open(tmpl, O_RDWR | O_CREAT | O_EXCL, 0600);
+ if (!fd.has_value()) {
+ if (fd.error() == EEXIST)
+ continue;
+ libc_errno = fd.error();
+ return -1;
+ }
+
+ return fd.value();
+ }
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdlib/mkstemp.h b/libc/src/stdlib/mkstemp.h
new file mode 100644
index 0000000000000..294f23d9d290a
--- /dev/null
+++ b/libc/src/stdlib/mkstemp.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for mkstemp -----------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDLIB_MKSTEMP_H
+#define LLVM_LIBC_SRC_STDLIB_MKSTEMP_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int mkstemp(char *tmpl);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDLIB_MKSTEMP_H
diff --git a/libc/test/src/stdlib/CMakeLists.txt b/libc/test/src/stdlib/CMakeLists.txt
index a7b7c9269fcee..697aa13d8ff0a 100644
--- a/libc/test/src/stdlib/CMakeLists.txt
+++ b/libc/test/src/stdlib/CMakeLists.txt
@@ -388,6 +388,26 @@ add_libc_test(
libc.src.stdlib.memalignment
)
+add_libc_test(
+ mkstemp_test
+ SUITE
+ libc-stdlib-tests
+ SRCS
+ mkstemp_test.cpp
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.fcntl_macros
+ libc.hdr.signal_macros
+ libc.src.stdlib.mkstemp
+ libc.src.string.strdup
+ libc.src.unistd.access
+ libc.src.unistd.close
+ libc.src.unistd.read
+ libc.src.unistd.unlink
+ libc.src.unistd.write
+ libc.test.UnitTest.ErrnoCheckingTest
+)
+
add_libc_test(
mbtowc_test
SUITE
diff --git a/libc/test/src/stdlib/mkstemp_test.cpp b/libc/test/src/stdlib/mkstemp_test.cpp
new file mode 100644
index 0000000000000..e7edfc3f98815
--- /dev/null
+++ b/libc/test/src/stdlib/mkstemp_test.cpp
@@ -0,0 +1,182 @@
+//===-- Unittests for mkstemp ---------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "hdr/fcntl_macros.h"
+#include "hdr/signal_macros.h"
+#include "src/stdlib/mkstemp.h"
+#include "src/string/strdup.h"
+#include "src/unistd/access.h"
+#include "src/unistd/close.h"
+#include "src/unistd/read.h"
+#include "src/unistd/unlink.h"
+#include "src/unistd/write.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcMkstempTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcMkstempTest, ValidTemplate) {
+ char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX"));
+ int fd = LIBC_NAMESPACE::mkstemp(tmpl);
+ ASSERT_GE(fd, 0);
+ LIBC_NAMESPACE::close(fd);
+ LIBC_NAMESPACE::unlink(tmpl);
+ ::free(tmpl);
+}
+
+TEST_F(LlvmLibcMkstempTest, TemplateModifiedInPlace) {
+ char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX"));
+ size_t len = 0;
+ while (tmpl[len] != '\0')
+ len++;
+ size_t count = 0;
+ for (size_t i = len; i > 0 && tmpl[i - 1] == 'X'; i--)
+ count++;
+ int fd = LIBC_NAMESPACE::mkstemp(tmpl);
+ ASSERT_GE(fd, 0);
+ bool modified = false;
+ for (size_t i = len - count; i < len; i++)
+ if (tmpl[i] != 'X') {
+ modified = true;
+ break;
+ }
+ EXPECT_TRUE(modified);
+ LIBC_NAMESPACE::close(fd);
+ LIBC_NAMESPACE::unlink(tmpl);
+ ::free(tmpl);
+}
+
+TEST_F(LlvmLibcMkstempTest, FileExists) {
+ char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX"));
+ int fd = LIBC_NAMESPACE::mkstemp(tmpl);
+ ASSERT_GE(fd, 0);
+ EXPECT_EQ(LIBC_NAMESPACE::access(tmpl, F_OK), 0);
+ LIBC_NAMESPACE::close(fd);
+ LIBC_NAMESPACE::unlink(tmpl);
+ ::free(tmpl);
+}
+
+TEST_F(LlvmLibcMkstempTest, FdIsWritable) {
+ char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX"));
+ int fd = LIBC_NAMESPACE::mkstemp(tmpl);
+ ASSERT_GE(fd, 0);
+ const char msg[] = "hello";
+ EXPECT_EQ(LIBC_NAMESPACE::write(fd, msg, 5), static_cast<ssize_t>(5));
+ LIBC_NAMESPACE::close(fd);
+ LIBC_NAMESPACE::unlink(tmpl);
+ ::free(tmpl);
+}
+
+TEST_F(LlvmLibcMkstempTest, Uniqueness) {
+ char *tmpl1 = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX"));
+ char *tmpl2 = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX"));
+ int fd1 = LIBC_NAMESPACE::mkstemp(tmpl1);
+ int fd2 = LIBC_NAMESPACE::mkstemp(tmpl2);
+ ASSERT_GE(fd1, 0);
+ ASSERT_GE(fd2, 0);
+ bool different = false;
+ for (size_t i = 0; tmpl1[i] != '\0'; i++)
+ if (tmpl1[i] != tmpl2[i]) {
+ different = true;
+ break;
+ }
+ EXPECT_TRUE(different);
+ LIBC_NAMESPACE::close(fd1);
+ LIBC_NAMESPACE::close(fd2);
+ LIBC_NAMESPACE::unlink(tmpl1);
+ LIBC_NAMESPACE::unlink(tmpl2);
+ ::free(tmpl1);
+ ::free(tmpl2);
+}
+
+TEST_F(LlvmLibcMkstempTest, FileIsEmpty) {
+ char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX"));
+ int fd = LIBC_NAMESPACE::mkstemp(tmpl);
+ ASSERT_GE(fd, 0);
+ // read should return 0 on empty file
+ char buf[1];
+ EXPECT_EQ(LIBC_NAMESPACE::read(fd, buf, 1), static_cast<ssize_t>(0));
+ LIBC_NAMESPACE::close(fd);
+ LIBC_NAMESPACE::unlink(tmpl);
+ ::free(tmpl);
+}
+
+TEST_F(LlvmLibcMkstempTest, SixXsNoPrefix) {
+ char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("XXXXXX"));
+ int fd = LIBC_NAMESPACE::mkstemp(tmpl);
+ ASSERT_GE(fd, 0);
+ LIBC_NAMESPACE::close(fd);
+ LIBC_NAMESPACE::unlink(tmpl);
+ ::free(tmpl);
+}
+
+TEST_F(LlvmLibcMkstempTest, MoreThanSixXs) {
+ char *tmpl =
+ LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXXXXXX"));
+ size_t len = 0;
+ while (tmpl[len] != '\0')
+ len++;
+ size_t count = 0;
+ for (size_t i = len; i > 0 && tmpl[i - 1] == 'X'; i--)
+ count++;
+ int fd = LIBC_NAMESPACE::mkstemp(tmpl);
+ ASSERT_GE(fd, 0);
+ bool modified = false;
+ for (size_t i = len - count; i < len; i++)
+ if (tmpl[i] != 'X') {
+ modified = true;
+ break;
+ }
+ EXPECT_TRUE(modified);
+ EXPECT_EQ(LIBC_NAMESPACE::access(tmpl, F_OK), 0);
+ LIBC_NAMESPACE::close(fd);
+ LIBC_NAMESPACE::unlink(tmpl);
+ ::free(tmpl);
+}
+
+#if defined(LIBC_ADD_NULL_CHECKS)
+TEST_F(LlvmLibcMkstempTest, NullPointer) {
+ EXPECT_DEATH([] { LIBC_NAMESPACE::mkstemp(nullptr); }, WITH_SIGNAL(-1));
+}
+#endif
+
+TEST_F(LlvmLibcMkstempTest, TemplateTooShort) {
+ char tmpl[] = "XXXXX";
+ int fd = LIBC_NAMESPACE::mkstemp(tmpl);
+ EXPECT_EQ(fd, -1);
+ ASSERT_ERRNO_EQ(EINVAL);
+}
+
+TEST_F(LlvmLibcMkstempTest, DoesNotEndInXs) {
+ char tmpl[] = "tmp_XXXXXY";
+ int fd = LIBC_NAMESPACE::mkstemp(tmpl);
+ EXPECT_EQ(fd, -1);
+ ASSERT_ERRNO_EQ(EINVAL);
+}
+
+TEST_F(LlvmLibcMkstempTest, XsInMiddleNotEnd) {
+ char tmpl[] = "XXXXXXtmp";
+ int fd = LIBC_NAMESPACE::mkstemp(tmpl);
+ EXPECT_EQ(fd, -1);
+ ASSERT_ERRNO_EQ(EINVAL);
+}
+
+TEST_F(LlvmLibcMkstempTest, FiveXsAtEnd) {
+ char tmpl[] = "tmp_XXXXX";
+ int fd = LIBC_NAMESPACE::mkstemp(tmpl);
+ EXPECT_EQ(fd, -1);
+ ASSERT_ERRNO_EQ(EINVAL);
+}
+
+TEST_F(LlvmLibcMkstempTest, EmptyString) {
+ char tmpl[] = "";
+ int fd = LIBC_NAMESPACE::mkstemp(tmpl);
+ EXPECT_EQ(fd, -1);
+ ASSERT_ERRNO_EQ(EINVAL);
+}
More information about the libc-commits
mailing list