summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorLeah Rowe <leah@libreboot.org>2026-03-24 19:44:22 +0000
committerLeah Rowe <leah@libreboot.org>2026-03-24 19:57:12 +0000
commit88ff5f7380c3382092217442dcc962fdca59be3a (patch)
tree8ada43324e767f6bd12b14da877079d3d1cb7c1a /util
parentbf5a3df79692fa757d77b9f1280242053541ee6a (diff)
util/mkhtemp: O_TMPFILE fast path on linux
linux itself provides much of the hardening we need, and avoids the need for some of our tests. use this on linux (fall back to openat still, on e.g. bsd) Signed-off-by: Leah Rowe <leah@libreboot.org>
Diffstat (limited to 'util')
-rw-r--r--util/libreboot-utils/include/common.h8
-rw-r--r--util/libreboot-utils/lib/mkhtemp.c108
2 files changed, 115 insertions, 1 deletions
diff --git a/util/libreboot-utils/include/common.h b/util/libreboot-utils/include/common.h
index b78b0c9c..620e95b9 100644
--- a/util/libreboot-utils/include/common.h
+++ b/util/libreboot-utils/include/common.h
@@ -513,6 +513,14 @@ int mkhtemp_try_create(int dirfd,
int *fd,
struct stat *st,
int type);
+int
+mkhtemp_tmpfile_linux(int dirfd,
+ struct stat *st_dir_initial,
+ char *fname_copy,
+ char *p,
+ size_t xc,
+ int *fd,
+ struct stat *st);
int mkhtemp(int *fd, struct stat *st,
char *template, int dirfd, const char *fname,
struct stat *st_dir_initial, int type);
diff --git a/util/libreboot-utils/lib/mkhtemp.c b/util/libreboot-utils/lib/mkhtemp.c
index dff05793..419bb0b3 100644
--- a/util/libreboot-utils/lib/mkhtemp.c
+++ b/util/libreboot-utils/lib/mkhtemp.c
@@ -19,10 +19,16 @@
#include <string.h>
#include <unistd.h>
-/* for openat2: */
+/* for openat2 / fast path: */
#ifdef __linux__
#include <linux/openat2.h>
#include <sys/syscall.h>
+#ifndef O_TMPFILE
+#define O_TMPFILE 020000000
+#endif
+#ifndef AT_EMPTY_PATH
+#define AT_EMPTY_PATH 0x1000
+#endif
#endif
#include "../include/common.h"
@@ -655,6 +661,18 @@ mkhtemp_try_create(int dirfd,
goto err;
if (type == MKHTEMP_FILE) {
+#ifdef __linux__
+ /* try O_TMPFILE fast path */
+ if (mkhtemp_tmpfile_linux(dirfd,
+ st_dir_initial, fname_copy,
+ p, xc, fd, st) == 0) {
+
+ errno = saved_errno;
+ rval = 1;
+ goto out;
+ }
+#endif
+
*fd = openat2p(dirfd, fname_copy,
O_RDWR | O_CREAT | O_EXCL |
O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, 0600);
@@ -746,6 +764,94 @@ out:
return rval;
}
+/* linux has its own special hardening
+ available specifically for tmpfiles,
+ which eliminates many race conditions.
+
+ we still use openat() on bsd, which is
+ still ok with our other mitigations
+ */
+#ifdef __linux__
+int
+mkhtemp_tmpfile_linux(int dirfd,
+ struct stat *st_dir_initial,
+ char *fname_copy,
+ char *p,
+ size_t xc,
+ int *fd,
+ struct stat *st)
+{
+ int saved_errno = errno;
+ int tmpfd = -1;
+ size_t retries;
+ int linked = 0;
+
+ if (fd == NULL || st == NULL ||
+ fname_copy == NULL || p == NULL ||
+ st_dir_initial == NULL) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ /* create unnamed tmpfile */
+ tmpfd = openat(dirfd, ".",
+ O_TMPFILE | O_RDWR | O_CLOEXEC, 0600);
+
+ if (tmpfd < 0)
+ return -1;
+
+ if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0)
+ goto err;
+
+ for (retries = 0; retries < MKHTEMP_RETRY_MAX; retries++) {
+
+ if (mkhtemp_fill_random(p, xc) < 0)
+ goto err;
+
+ if (fd_verify_dir_identity(dirfd,
+ st_dir_initial) < 0)
+ goto err;
+
+ if (linkat(tmpfd, "",
+ dirfd, fname_copy,
+ AT_EMPTY_PATH) == 0) {
+
+ linked = 1; /* file created */
+
+ if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0)
+ goto err;
+
+ /* success */
+ *fd = tmpfd;
+
+ if (fstat(*fd, st) < 0)
+ goto err;
+
+ if (secure_file(fd, st, st,
+ O_APPEND, 1, 1, 0600) < 0)
+ goto err;
+
+ errno = saved_errno;
+ return 0;
+ }
+
+ if (errno != EEXIST)
+ goto err;
+
+ /* retry on collision */
+ }
+
+ errno = EEXIST;
+
+err:
+ if (linked)
+ (void) unlinkat(dirfd, fname_copy, 0);
+
+ close_no_err(&tmpfd);
+ return -1;
+}
+#endif
+
int
mkhtemp_fill_random(char *p, size_t xc)
{