mirror of
https://codeberg.org/libreboot/lbmk.git
synced 2026-03-25 13:29:03 +02:00
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>
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user