libreboot-utils: stricter errno handling

where possible, try not to clobber sys errno. override
it only when relatively safe.

also: when a syscall succeeds, it may set errno. this
is rare, but permitted (nothing specified against it
in specs, and the specs say that errno is undefined
on success).

i'm not libc, but i'm wrapping around it, so i need
to be careful in how i handle the errno value.

also:

i removed the requirement for directories to be
executable, in mkhtemp.c, because this isn't required
and will only break certain setups.

in world_writeable and sticky, i made the checks stricter:
the faccessat check was being skipped on some paths, so
i've closed that loophole now.

i also generally cleaned up some code, as part of the errno
handling refactoring, where it made sense to do so, plus a
few other bits of code cleanup.

Signed-off-by: Leah Rowe <leah@libreboot.org>
This commit is contained in:
Leah Rowe
2026-03-31 15:43:43 +01:00
parent c0fd88155a
commit d2abde5303
11 changed files with 371 additions and 450 deletions
+23 -5
View File
@@ -26,6 +26,24 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
/* dangerously cool macros:
*/
#define SUCCESS(x) ((x) >= 0)
/* syscalls can set errno even on success; this
* is rare, but permitted. in various functions, we
* reset errno on success, to what the caller had,
* but we must still honour what was returned.
*
* lib/file.c is littered with examples
*/
#define reset_caller_errno(return_value) \
do { \
if (SUCCESS(return_value) && (!errno)) \
errno = saved_errno; \
} while (0)
#define items(x) (sizeof((x)) / sizeof((x)[0])) #define items(x) (sizeof((x)) / sizeof((x)[0]))
/* system prototypes /* system prototypes
@@ -464,7 +482,7 @@ ssize_t rw_gbe_file_exact(int fd, unsigned char *mem, size_t nrw,
int fsync_dir(const char *path); int fsync_dir(const char *path);
ssize_t rw_file_exact(int fd, unsigned char *mem, size_t len, ssize_t rw_file_exact(int fd, unsigned char *mem, size_t len,
off_t off, int rw_type, size_t max_retries, int off_reset); off_t off, int rw_type, size_t max_retries, int off_reset);
ssize_t prw(int fd, void *mem, size_t nrw, ssize_t rw(int fd, void *mem, size_t nrw,
off_t off, int rw_type); off_t off, int rw_type);
int io_args(int fd, void *mem, size_t nrw, int io_args(int fd, void *mem, size_t nrw,
off_t off, int rw_type); off_t off, int rw_type);
@@ -489,7 +507,7 @@ int rw_retry(int saved_errno, ssize_t rval);
*/ */
void usage(void); void usage(void);
int set_errno(int saved_errno, int fallback); int with_fallback_errno(int fallback);
void err_exit(int nvm_errval, const char *msg, ...); void err_exit(int nvm_errval, const char *msg, ...);
func_t errhook(func_t ptr); /* hook function for cleanup on err */ func_t errhook(func_t ptr); /* hook function for cleanup on err */
const char *lbgetprogname(void); const char *lbgetprogname(void);
@@ -506,7 +524,7 @@ int new_tmpdir(int *fd, char **path, char *tmpdir,
int new_tmp_common(int *fd, char **path, int type, int new_tmp_common(int *fd, char **path, int type,
char *tmpdir, const char *template); char *tmpdir, const char *template);
int mkhtemp_try_create(int dirfd, int mkhtemp_try_create(int dirfd,
struct stat *st_dir_initial, struct stat *st_dir_first,
char *fname_copy, char *fname_copy,
char *p, char *p,
size_t xc, size_t xc,
@@ -515,7 +533,7 @@ int mkhtemp_try_create(int dirfd,
int type); int type);
int int
mkhtemp_tmpfile_linux(int dirfd, mkhtemp_tmpfile_linux(int dirfd,
struct stat *st_dir_initial, struct stat *st_dir_first,
char *fname_copy, char *fname_copy,
char *p, char *p,
size_t xc, size_t xc,
@@ -523,7 +541,7 @@ mkhtemp_tmpfile_linux(int dirfd,
struct stat *st); struct stat *st);
int mkhtemp(int *fd, struct stat *st, int mkhtemp(int *fd, struct stat *st,
char *template, int dirfd, const char *fname, char *template, int dirfd, const char *fname,
struct stat *st_dir_initial, int type); struct stat *st_dir_first, int type);
int world_writeable_and_sticky(const char *s, int world_writeable_and_sticky(const char *s,
int sticky_allowed, int always_sticky); int sticky_allowed, int always_sticky);
int same_dir(const char *a, const char *b); int same_dir(const char *a, const char *b);
+1 -5
View File
@@ -15,9 +15,6 @@
#include "../include/common.h" #include "../include/common.h"
/* Guard against regressions by maintainers (command table)
*/
void void
sanitize_command_list(void) sanitize_command_list(void)
{ {
@@ -496,8 +493,7 @@ cat_buf(unsigned char *b)
err_exit(errno, "null pointer in cat command"); err_exit(errno, "null pointer in cat command");
if (rw_file_exact(STDOUT_FILENO, b, if (rw_file_exact(STDOUT_FILENO, b,
GBE_PART_SIZE, 0, IO_WRITE, MAX_ZERO_RW_RETRY, OFF_ERR) GBE_PART_SIZE, 0, IO_WRITE, MAX_ZERO_RW_RETRY, OFF_ERR) < 0)
< 0)
err_exit(errno, "stdout: cat"); err_exit(errno, "stdout: cat");
} }
void void
+133 -155
View File
@@ -44,51 +44,31 @@ same_file(int fd, struct stat *st_old,
{ {
struct stat st; struct stat st;
int saved_errno = errno; int saved_errno = errno;
int rval = 0;
errno = 0;
/* TODO: null/-1 checks if (if_err(st_old == NULL, EFAULT) ||
* like this can be if_err(fd < 0, EBADF) ||
* generalised (rval = fstat(fd, &st)) < 0 ||
*/ (rval = fd_verify_regular(fd, st_old, &st)) < 0 ||
if (st_old == NULL) { if_err(check_size && st.st_size != st_old->st_size, ESTALE))
errno = EFAULT; return with_fallback_errno(ESTALE);
goto err_same_file;
}
if (fd < 0) {
errno = EBADF;
goto err_same_file;
}
if (fstat(fd, &st) == -1) reset_caller_errno(rval);
goto err_same_file;
if (fd_verify_regular(fd, st_old, &st) < 0)
goto err_same_file;
if (check_size &&
st.st_size != st_old->st_size)
goto err_same_file;
errno = saved_errno;
return 0; return 0;
err_same_file:
return set_errno(saved_errno, ESTALE);
} }
int int
fsync_dir(const char *path) fsync_dir(const char *path)
{ {
int saved_errno = errno; int saved_errno = errno;
size_t pathlen = 0; size_t pathlen = 0;
char *dirbuf = NULL; char *dirbuf = NULL;
int dirfd = -1; int dirfd = -1;
char *slash = NULL; char *slash = NULL;
struct stat st = {0}; struct stat st = {0};
int rval = 0;
int close_errno; errno = 0;
if (if_err(slen(path, PATH_MAX, &pathlen) == 0, EINVAL)) if (if_err(slen(path, PATH_MAX, &pathlen) == 0, EINVAL))
goto err_fsync_dir; goto err_fsync_dir;
@@ -119,26 +99,23 @@ fsync_dir(const char *path)
); );
if (if_err_sys(dirfd < 0) || if (if_err_sys(dirfd < 0) ||
if_err_sys(fstat(dirfd, &st) < 0) || if_err_sys((rval = fstat(dirfd, &st)) < 0) ||
if_err(!S_ISDIR(st.st_mode), ENOTDIR) if_err(!S_ISDIR(st.st_mode), ENOTDIR)
|| ||
if_err_sys(fsync_on_eintr(dirfd) == -1)) if_err_sys((rval = fsync_on_eintr(dirfd)) == -1))
goto err_fsync_dir; goto err_fsync_dir;
close_on_eintr(&dirfd); close_on_eintr(&dirfd);
free_and_set_null(&dirbuf); free_and_set_null(&dirbuf);
errno = saved_errno; reset_caller_errno(rval);
return 0; return 0;
err_fsync_dir: err_fsync_dir:
free_and_set_null(&dirbuf); free_and_set_null(&dirbuf);
close_on_eintr(&dirfd); close_on_eintr(&dirfd);
return set_errno(saved_errno, EIO); return with_fallback_errno(EIO);
} }
/* rw_file_exact() - Read perfectly or die /* rw_file_exact() - Read perfectly or die
@@ -162,34 +139,23 @@ ssize_t
rw_file_exact(int fd, unsigned char *mem, size_t nrw, rw_file_exact(int fd, unsigned char *mem, size_t nrw,
off_t off, int rw_type, size_t max_retries, int off_reset) off_t off, int rw_type, size_t max_retries, int off_reset)
{ {
ssize_t rval; int saved_errno = errno;
ssize_t rc; ssize_t rval = 0;
ssize_t rc = 0;
size_t nrw_cur; size_t nrw_cur;
off_t off_cur; off_t off_cur;
void *mem_cur; void *mem_cur;
size_t retries_on_zero = 0;
size_t retries_on_zero;
int saved_errno = errno;
errno = 0; errno = 0;
rval = 0;
rc = 0;
retries_on_zero = 0;
if (io_args(fd, mem, nrw, off, rw_type) == -1) if (io_args(fd, mem, nrw, off, rw_type) == -1)
goto err_rw_file_exact; goto err_rw_file_exact;
while (1) { while (1) {
/* Prevent theoretical overflow */ /* Prevent theoretical overflow */
if (rval >= 0 && (size_t)rval > (nrw - rc)) { if (if_err(rval >= 0 && (size_t)rval > (nrw - rc), EOVERFLOW))
errno = EOVERFLOW;
goto err_rw_file_exact; goto err_rw_file_exact;
}
rc += rval; rc += rval;
if ((size_t)rc >= nrw) if ((size_t)rc >= nrw)
@@ -198,50 +164,36 @@ rw_file_exact(int fd, unsigned char *mem, size_t nrw,
mem_cur = (void *)(mem + (size_t)rc); mem_cur = (void *)(mem + (size_t)rc);
nrw_cur = (size_t)(nrw - (size_t)rc); nrw_cur = (size_t)(nrw - (size_t)rc);
if (off < 0) { if (if_err(off < 0, EOVERFLOW))
errno = EOVERFLOW;
goto err_rw_file_exact; goto err_rw_file_exact;
}
off_cur = off + (off_t)rc; off_cur = off + (off_t)rc;
rval = prw(fd, mem_cur, nrw_cur, off_cur, if ((rval = rw(fd, mem_cur, nrw_cur, off_cur, rw_type)) < 0)
rw_type);
if (rval < 0)
goto err_rw_file_exact; goto err_rw_file_exact;
if (rval == 0) { if (rval == 0) {
if (retries_on_zero++ < max_retries) if (retries_on_zero++ < max_retries)
continue; continue;
errno = EIO;
goto err_rw_file_exact; goto err_rw_file_exact;
} }
retries_on_zero = 0; retries_on_zero = 0;
} }
if ((size_t)rc != nrw) { if (if_err((size_t)rc != nrw, EIO) ||
(rval = rw_over_nrw(rc, nrw)) < 0)
errno = EIO;
goto err_rw_file_exact;
}
rval = rw_over_nrw(rc, nrw);
if (rval < 0)
goto err_rw_file_exact; goto err_rw_file_exact;
errno = saved_errno; reset_caller_errno(rval);
return rval; return rval;
err_rw_file_exact: err_rw_file_exact:
return with_fallback_errno(EIO);
return set_errno(saved_errno, EIO);
} }
/* prw() - portable read-write with more /* rw() - read-write but with more
* safety checks than barebones libc * safety checks than barebones libc
* *
* A fallback is provided for regular read/write. * A fallback is provided for regular read/write.
@@ -250,35 +202,40 @@ err_rw_file_exact:
*/ */
ssize_t ssize_t
prw(int fd, void *mem, size_t nrw, rw(int fd, void *mem, size_t nrw,
off_t off, int rw_type) off_t off, int rw_type)
{ {
ssize_t rval; ssize_t rval = 0;
ssize_t r; ssize_t r = -1;
struct stat st; struct stat st;
int saved_errno = errno; int saved_errno = errno;
errno = 0; errno = 0;
if (io_args(fd, mem, nrw, off, rw_type) == -1) if (io_args(fd, mem, nrw, off, rw_type) == -1)
return set_errno(saved_errno, EIO); return with_fallback_errno(EINVAL);
r = -1; switch (rw_type) {
case IO_WRITE:
if (rw_type == IO_WRITE)
r = write_on_eintr(fd, mem, nrw); r = write_on_eintr(fd, mem, nrw);
else if (rw_type == IO_READ) break;
case IO_READ:
r = read_on_eintr(fd, mem, nrw); r = read_on_eintr(fd, mem, nrw);
else if (rw_type == IO_PWRITE) break;
case IO_PWRITE:
r = pwrite_on_eintr(fd, mem, nrw, off); r = pwrite_on_eintr(fd, mem, nrw, off);
else if (rw_type == IO_PREAD) break;
case IO_PREAD:
r = pread_on_eintr(fd, mem, nrw, off); r = pread_on_eintr(fd, mem, nrw, off);
break;
default:
errno = EINVAL;
break;
}
if ((rval = rw_over_nrw(r, nrw)) < 0) if ((rval = rw_over_nrw(r, nrw)) < 0)
return set_errno(saved_errno, EIO); return with_fallback_errno(EIO);
if (rval >= 0 && !errno)
errno = saved_errno;
reset_caller_errno(rval);
return rval; return rval;
} }
@@ -287,6 +244,7 @@ io_args(int fd, void *mem, size_t nrw,
off_t off, int rw_type) off_t off, int rw_type)
{ {
int saved_errno = errno; int saved_errno = errno;
errno = 0;
if (if_err(mem == NULL, EFAULT) || if (if_err(mem == NULL, EFAULT) ||
if_err(fd < 0, EBADF) || if_err(fd < 0, EBADF) ||
@@ -297,29 +255,31 @@ io_args(int fd, void *mem, size_t nrw,
if_err(rw_type > IO_PWRITE, EINVAL)) if_err(rw_type > IO_PWRITE, EINVAL))
goto err_io_args; goto err_io_args;
errno = saved_errno; reset_caller_errno(0);
return 0; return 0;
err_io_args: err_io_args:
return set_errno(saved_errno, EINVAL); return with_fallback_errno(EINVAL);
} }
int int
check_file(int fd, struct stat *st) check_file(int fd, struct stat *st)
{ {
int saved_errno = errno; int saved_errno = errno;
int rval = 0;
errno = 0;
if (if_err(fd < 0, EBADF) || if (if_err(fd < 0, EBADF) ||
if_err(st == NULL, EFAULT) || if_err(st == NULL, EFAULT) ||
if_err(fstat(fd, st) == -1, 0) || ((rval = fstat(fd, st)) == -1) ||
if_err(!S_ISREG(st->st_mode), EBADF)) if_err(!S_ISREG(st->st_mode), EBADF))
goto err_is_file; goto err_is_file;
errno = saved_errno; reset_caller_errno(rval);
return 0; return 0;
err_is_file: err_is_file:
return set_errno(saved_errno, EINVAL); return with_fallback_errno(EINVAL);
} }
/* POSIX can say whatever it wants. /* POSIX can say whatever it wants.
@@ -328,15 +288,12 @@ err_is_file:
ssize_t ssize_t
rw_over_nrw(ssize_t r, size_t nrw) rw_over_nrw(ssize_t r, size_t nrw)
{ {
int saved_errno = errno; if (if_err(!nrw, EIO) ||
(r == -1) ||
if (if_err(!nrw, 0) ||
if_err(r == -1, 0) ||
if_err((size_t)r > SSIZE_MAX, ERANGE) || if_err((size_t)r > SSIZE_MAX, ERANGE) ||
if_err((size_t)r > nrw, ERANGE)) if_err((size_t)r > nrw, ERANGE))
return set_errno(saved_errno, EIO); return with_fallback_errno(EIO);
errno = saved_errno;
return r; return r;
} }
@@ -347,10 +304,8 @@ if_err(int condition, int errval)
{ {
if (!condition) if (!condition)
return 0; return 0;
if (errval) if (errval)
errno = errval; errno = errval;
return 1; return 1;
} }
int int
@@ -431,6 +386,7 @@ fs_resolve_at(int dirfd, const char *path, int flags)
int saved_errno = errno; int saved_errno = errno;
int r; int r;
int is_last; int is_last;
errno = 0;
if (dirfd < 0 || path == NULL || *path == '\0') { if (dirfd < 0 || path == NULL || *path == '\0') {
errno = EINVAL; errno = EINVAL;
@@ -461,7 +417,7 @@ fs_resolve_at(int dirfd, const char *path, int flags)
nextfd = -1; nextfd = -1;
} }
errno = saved_errno; reset_caller_errno(0);
return curfd; return curfd;
err: err:
@@ -475,7 +431,7 @@ err:
close_on_eintr(&curfd); close_on_eintr(&curfd);
errno = saved_errno; errno = saved_errno;
return -1; return with_fallback_errno(EIO);
} }
/* NOTE: /* NOTE:
@@ -515,41 +471,36 @@ fs_next_component(const char **p,
name[len] = '\0'; name[len] = '\0';
/* reject . and .. */ /* reject . and .. */
if ((name[0] == '.' && name[1] == '\0') || if (if_err((name[0] == '.' && name[1] == '\0') ||
(name[0] == '.' && name[1] == '.' && name[2] == '\0')) { (name[0] == '.' && name[1] == '.' && name[2] == '\0'), EPERM))
errno = EPERM; goto err;
return -1;
}
*p = s + len; *p = s + len;
return 1; return 1;
err:
return with_fallback_errno(EPERM);
} }
int int
fs_open_component(int dirfd, const char *name, fs_open_component(int dirfd, const char *name,
int flags, int is_last) int flags, int is_last)
{ {
int saved_errno = errno;
int fd; int fd;
struct stat st; struct stat st;
errno = 0;
fd = openat_on_eintr(dirfd, name, fd = openat_on_eintr(dirfd, name,
(is_last ? flags : (O_RDONLY | O_DIRECTORY)) | (is_last ? flags : (O_RDONLY | O_DIRECTORY)) |
O_NOFOLLOW | O_CLOEXEC, (flags & O_CREAT) ? 0600 : 0); O_NOFOLLOW | O_CLOEXEC, (flags & O_CREAT) ? 0600 : 0);
if (!is_last) { if (!is_last &&
(if_err(fd < 0, EBADF) ||
if (if_err(fd < 0, EBADF) || if_err_sys(fstat(fd, &st) < 0) ||
if_err_sys(fstat(fd, &st) < 0)) if_err(!S_ISDIR(st.st_mode), ENOTDIR)))
return -1; return with_fallback_errno(EIO);
if (!S_ISDIR(st.st_mode)) {
close_on_eintr(&fd);
errno = ENOTDIR;
return -1;
}
}
reset_caller_errno(fd);
return fd; return fd;
} }
@@ -558,13 +509,14 @@ fs_dirname_basename(const char *path,
char **dir, char **base, char **dir, char **base,
int allow_relative) int allow_relative)
{ {
int saved_errno = errno;
char *buf = NULL; char *buf = NULL;
char *slash; char *slash;
size_t len; size_t len;
int rval; errno = 0;
if (if_err(path == NULL || dir == NULL || base == NULL, EFAULT)) if (if_err(path == NULL || dir == NULL || base == NULL, EFAULT))
return -1; goto err;
slen(path, PATH_MAX, &len); slen(path, PATH_MAX, &len);
memcpy(smalloc(&buf, len + 1), memcpy(smalloc(&buf, len + 1),
@@ -591,12 +543,14 @@ fs_dirname_basename(const char *path,
sdup(".", PATH_MAX, dir); sdup(".", PATH_MAX, dir);
*base = buf; *base = buf;
} else { } else {
errno = EINVAL;
free_and_set_null(&buf); free_and_set_null(&buf);
return -1; goto err;
} }
reset_caller_errno(0);
return 0; return 0;
err:
return with_fallback_errno(EINVAL);
} }
/* TODO: why does this abort, but others /* TODO: why does this abort, but others
@@ -608,7 +562,8 @@ open_file_on_eintr(const char *path,
struct stat *st) struct stat *st)
{ {
int saved_errno = errno; int saved_errno = errno;
int rval; int rval = 0;
errno = 0;
if (path == NULL) if (path == NULL)
err_exit(EINVAL, "open_file_on_eintr: null path"); err_exit(EINVAL, "open_file_on_eintr: null path");
@@ -626,8 +581,17 @@ open_file_on_eintr(const char *path,
err_exit(errno, err_exit(errno,
"%s: open_file_on_eintr: could not close", path); "%s: open_file_on_eintr: could not close", path);
reset_caller_errno(rval);
*fd = rval; *fd = rval;
/* we don't care about edge case behaviour here,
even if the next operation sets errno on success,
because the open() call is our main concern.
however, we also must preserve the new errno,
assuming it changed above under the same edge case */
saved_errno = errno;
if (st != NULL) { if (st != NULL) {
if (fstat(*fd, st) < 0) if (fstat(*fd, st) < 0)
err_exit(errno, "%s: stat", path); err_exit(errno, "%s: stat", path);
@@ -639,7 +603,7 @@ open_file_on_eintr(const char *path,
if (lseek_on_eintr(*fd, 0, SEEK_CUR, 1, 1) == (off_t)-1) if (lseek_on_eintr(*fd, 0, SEEK_CUR, 1, 1) == (off_t)-1)
err_exit(errno, "%s: file not seekable", path); err_exit(errno, "%s: file not seekable", path);
errno = saved_errno; errno = saved_errno; /* see previous comment */
} }
@@ -657,20 +621,24 @@ openat_on_eintr(int dirfd, const char *path,
RESOLVE_NO_MAGICLINKS RESOLVE_NO_MAGICLINKS
}; };
int saved_errno = errno; int saved_errno = errno;
long rval; long rval = 0;
errno = 0;
if (if_err(dirfd < 0, EBADF) || if (if_err(dirfd < 0, EBADF) ||
if_err(path == NULL, EFAULT)) if_err(path == NULL, EFAULT))
return set_errno(saved_errno, EIO); goto err;
errno = 0; errno = 0;
while (sys_retry(saved_errno, while (sys_retry(saved_errno,
rval = syscall(SYS_openat2, dirfd, path, &how, sizeof(how)))); rval = syscall(SYS_openat2, dirfd, path, &how, sizeof(how))));
if (rval == -1) /* avoid long->int UB for -1 */ if (rval == -1) /* avoid long->int UB for -1 */
return -1; goto err;
reset_caller_errno(rval);
return (int)rval; return (int)rval;
err:
return with_fallback_errno(EIO); /* -1 */
} }
#else /* regular openat on non-linux e.g. openbsd */ #else /* regular openat on non-linux e.g. openbsd */
int int
@@ -678,16 +646,17 @@ openat_on_eintr(int dirfd, const char *path,
int flags, mode_t mode) int flags, mode_t mode)
{ {
int saved_errno = errno; int saved_errno = errno;
int rval; int rval = 0;
errno = 0;
if (if_err(dirfd < 0, EBADF) || if (if_err(dirfd < 0, EBADF) ||
if_err(path == NULL, EFAULT)) if_err(path == NULL, EFAULT))
return set_errno(saved_errno, EIO); return with_fallback_errno(EIO);
errno = 0;
while (fs_retry(saved_errno, while (fs_retry(saved_errno,
rval = openat(dirfd, path, flags, mode))); rval = openat(dirfd, path, flags, mode)));
reset_caller_errno(rval);
return rval; return rval;
} }
#endif #endif
@@ -697,12 +666,13 @@ lseek_on_eintr(int fd, off_t off, int whence,
int loop_eagain, int loop_eintr) int loop_eagain, int loop_eintr)
{ {
int saved_errno = errno; int saved_errno = errno;
off_t rval; off_t rval = 0;
errno = 0; errno = 0;
while (off_retry(saved_errno, while (off_retry(saved_errno,
rval = lseek(fd, off, whence))); rval = lseek(fd, off, whence)));
reset_caller_errno(rval);
return rval; return rval;
} }
@@ -711,16 +681,17 @@ mkdirat_on_eintr(int dirfd,
const char *path, mode_t mode) const char *path, mode_t mode)
{ {
int saved_errno = errno; int saved_errno = errno;
int rval; int rval = 0;
errno = 0;
if (if_err(dirfd < 0, EBADF) || if (if_err(dirfd < 0, EBADF) ||
if_err(path == NULL, EFAULT)) if_err(path == NULL, EFAULT))
return set_errno(saved_errno, EIO); return with_fallback_errno(EIO);
errno = 0;
while (fs_retry(saved_errno, while (fs_retry(saved_errno,
rval = mkdirat(dirfd, path, mode))); rval = mkdirat(dirfd, path, mode)));
reset_caller_errno(rval);
return rval; return rval;
} }
@@ -729,17 +700,18 @@ read_on_eintr(int fd,
void *buf, size_t count) void *buf, size_t count)
{ {
int saved_errno = errno; int saved_errno = errno;
ssize_t rval; ssize_t rval = 0;
errno = 0;
if (if_err(buf == NULL, EFAULT) || if (if_err(buf == NULL, EFAULT) ||
if_err(fd < 0, EBADF) || if_err(fd < 0, EBADF) ||
if_err(count == 0, EINVAL)) if_err(count == 0, EINVAL))
return set_errno(saved_errno, EIO); return with_fallback_errno(EIO);
errno = 0;
while (rw_retry(saved_errno, while (rw_retry(saved_errno,
rval = read(fd, buf, count))); rval = read(fd, buf, count)));
reset_caller_errno(rval);
return rval; return rval;
} }
@@ -749,18 +721,19 @@ pread_on_eintr(int fd,
off_t off) off_t off)
{ {
int saved_errno = errno; int saved_errno = errno;
ssize_t rval; ssize_t rval = 0;
errno = 0;
if (if_err(buf == NULL, EFAULT) || if (if_err(buf == NULL, EFAULT) ||
if_err(fd < 0, EBADF) || if_err(fd < 0, EBADF) ||
if_err(off < 0, EFAULT) || if_err(off < 0, EFAULT) ||
if_err(count == 0, EINVAL)) if_err(count == 0, EINVAL))
return set_errno(saved_errno, EIO); return with_fallback_errno(EIO);
errno = 0;
while (rw_retry(saved_errno, while (rw_retry(saved_errno,
rval = pread(fd, buf, count, off))); rval = pread(fd, buf, count, off)));
reset_caller_errno(rval);
return rval; return rval;
} }
@@ -769,17 +742,18 @@ write_on_eintr(int fd,
void *buf, size_t count) void *buf, size_t count)
{ {
int saved_errno = errno; int saved_errno = errno;
ssize_t rval; ssize_t rval = 0;
errno = 0;
if (if_err(buf == NULL, EFAULT) || if (if_err(buf == NULL, EFAULT) ||
if_err(fd < 0, EBADF) || if_err(fd < 0, EBADF) ||
if_err(count == 0, EINVAL)) if_err(count == 0, EINVAL))
return set_errno(saved_errno, EIO); return with_fallback_errno(EIO);
errno = 0;
while (rw_retry(saved_errno, while (rw_retry(saved_errno,
rval = write(fd, buf, count))); rval = write(fd, buf, count)));
reset_caller_errno(rval);
return rval; return rval;
} }
@@ -789,18 +763,19 @@ pwrite_on_eintr(int fd,
off_t off) off_t off)
{ {
int saved_errno = errno; int saved_errno = errno;
ssize_t rval; ssize_t rval = 0;
errno = 0;
if (if_err(buf == NULL, EFAULT) || if (if_err(buf == NULL, EFAULT) ||
if_err(fd < 0, EBADF) || if_err(fd < 0, EBADF) ||
if_err(off < 0, EFAULT) || if_err(off < 0, EFAULT) ||
if_err(count == 0, EINVAL)) if_err(count == 0, EINVAL))
return set_errno(saved_errno, EIO); return with_fallback_errno(EIO);
errno = 0;
while (rw_retry(saved_errno, while (rw_retry(saved_errno,
rval = pwrite(fd, buf, count, off))); rval = pwrite(fd, buf, count, off)));
reset_caller_errno(rval);
return rval; return rval;
} }
@@ -808,15 +783,16 @@ int
fsync_on_eintr(int fd) fsync_on_eintr(int fd)
{ {
int saved_errno = errno; int saved_errno = errno;
int rval; int rval = 0;
errno = 0;
if (if_err(fd < 0, EBADF)) if (if_err(fd < 0, EBADF))
return set_errno(saved_errno, EIO); return with_fallback_errno(EIO);
errno = 0;
while (fs_retry(saved_errno, while (fs_retry(saved_errno,
rval = fsync(fd))); rval = fsync(fd)));
reset_caller_errno(rval);
return rval; return rval;
} }
@@ -824,7 +800,7 @@ void
close_on_eintr(int *fd) close_on_eintr(int *fd)
{ {
int saved_errno = errno; int saved_errno = errno;
int rval; int rval = 0;
if (fd == NULL) if (fd == NULL)
err_exit(EINVAL, "close_on_eintr: null pointer"); err_exit(EINVAL, "close_on_eintr: null pointer");
@@ -839,6 +815,8 @@ close_on_eintr(int *fd)
err_exit(errno, "close_on_eintr: could not close"); err_exit(errno, "close_on_eintr: could not close");
*fd = -1; *fd = -1;
reset_caller_errno(rval);
} }
/* unified eintr looping. /* unified eintr looping.
+3 -4
View File
@@ -470,9 +470,9 @@ ret_gbe_mv:
if (rval >= 0) if (rval >= 0)
goto out; goto out;
return set_errno(saved_errno, EIO); return with_fallback_errno(EIO);
out: out:
errno = saved_errno; reset_caller_errno(rval);
return rval; return rval;
} }
@@ -572,6 +572,5 @@ rw_gbe_file_exact(int fd, unsigned char *mem, size_t nrw,
return rw_over_nrw(r, nrw); return rw_over_nrw(r, nrw);
err_rw_gbe_file_exact: err_rw_gbe_file_exact:
errno = EIO; return with_fallback_errno(EIO);
return -1;
} }
+160 -240
View File
@@ -67,20 +67,15 @@ new_tmp_common(int *fd, char **path, int type,
int dirfd = -1; int dirfd = -1;
const char *fname = NULL; const char *fname = NULL;
struct stat st_dir_initial; struct stat st_dir_first;
char *fail_dir = NULL; char *fail_dir = NULL;
if (path == NULL || fd == NULL) { errno = 0;
errno = EFAULT;
goto err;
}
/* don't mess with someone elses file */ if (if_err(path == NULL || fd == NULL, EFAULT) ||
if (*fd >= 0) { if_err(*fd >= 0, EEXIST)) /* don't touch someone else's file */
errno = EEXIST;
goto err; goto err;
}
/* regarding **path: /* regarding **path:
* the pointer (to the pointer) * the pointer (to the pointer)
@@ -111,12 +106,7 @@ new_tmp_common(int *fd, char **path, int type,
tmpdir = env_tmpdir(0, &fail_dir, tmpdir); tmpdir = env_tmpdir(0, &fail_dir, tmpdir);
#endif #endif
} }
if (tmpdir == NULL) if (if_err(tmpdir ==NULL || *tmpdir == '\0' || *tmpdir != '/', EINVAL))
goto err;
if (*tmpdir == '\0')
goto err;
if (*tmpdir != '/')
goto err; goto err;
if (template != NULL) if (template != NULL)
@@ -138,11 +128,11 @@ new_tmp_common(int *fd, char **path, int type,
if (dirfd < 0) if (dirfd < 0)
goto err; goto err;
if (fstat(dirfd, &st_dir_initial) < 0) if (fstat(dirfd, &st_dir_first) < 0)
goto err; goto err;
*fd = mkhtemp(fd, &st, dest, dirfd, *fd = mkhtemp(fd, &st, dest, dirfd,
fname, &st_dir_initial, type); fname, &st_dir_first, type);
if (*fd < 0) if (*fd < 0)
goto err; goto err;
@@ -151,15 +141,10 @@ new_tmp_common(int *fd, char **path, int type,
errno = saved_errno; errno = saved_errno;
*path = dest; *path = dest;
reset_caller_errno(0);
return 0; return 0;
err: err:
if (errno != saved_errno)
saved_errno = errno;
else
saved_errno = errno = EIO;
free_and_set_null(&dest); free_and_set_null(&dest);
close_on_eintr(&dirfd); close_on_eintr(&dirfd);
@@ -173,7 +158,7 @@ err:
*path = fail_dir; *path = fail_dir;
errno = saved_errno; errno = saved_errno;
return -1; return with_fallback_errno(EIO);
} }
@@ -184,13 +169,17 @@ char *
env_tmpdir(int bypass_all_sticky_checks, char **tmpdir, env_tmpdir(int bypass_all_sticky_checks, char **tmpdir,
char *override_tmpdir) char *override_tmpdir)
{ {
char *t; char *t = NULL;
int allow_noworld_unsticky; int allow_noworld_unsticky;
int saved_errno = errno; int saved_errno = errno;
static const char tmp[] = "/tmp"; static const char tmp[] = "/tmp";
static const char vartmp[] = "/var/tmp"; static const char vartmp[] = "/var/tmp";
char *rval = NULL;
errno = 0;
/* tmpdir is a user override, if set */ /* tmpdir is a user override, if set */
if (override_tmpdir == NULL) if (override_tmpdir == NULL)
t = getenv("TMPDIR"); t = getenv("TMPDIR");
@@ -200,48 +189,38 @@ env_tmpdir(int bypass_all_sticky_checks, char **tmpdir,
if (t != NULL && *t != '\0') { if (t != NULL && *t != '\0') {
if (tmpdir_policy(t, if (tmpdir_policy(t,
&allow_noworld_unsticky) < 0) { &allow_noworld_unsticky) < 0)
if (tmpdir != NULL) goto err;
*tmpdir = t;
return NULL; /* errno already set */
}
if (!world_writeable_and_sticky(t, if (!world_writeable_and_sticky(t,
allow_noworld_unsticky, allow_noworld_unsticky,
bypass_all_sticky_checks)) { bypass_all_sticky_checks))
if (tmpdir != NULL) goto err;
*tmpdir = t;
return NULL;
}
errno = saved_errno; rval = t;
return t; goto out;
} }
allow_noworld_unsticky = 0; allow_noworld_unsticky = 0;
if (world_writeable_and_sticky(tmp, if (world_writeable_and_sticky(tmp, allow_noworld_unsticky,
allow_noworld_unsticky, bypass_all_sticky_checks))
bypass_all_sticky_checks)) { rval = (char *)tmp;
else if (world_writeable_and_sticky(vartmp,
if (tmpdir != NULL) allow_noworld_unsticky, bypass_all_sticky_checks))
*tmpdir = (char *)tmp; rval = (char *)vartmp;
else
errno = saved_errno; goto err;
return (char *)tmp;
}
if (world_writeable_and_sticky(vartmp,
allow_noworld_unsticky,
bypass_all_sticky_checks)) {
if (tmpdir != NULL)
*tmpdir = (char *)vartmp;
errno = saved_errno;
return (char *)vartmp;
}
out:
reset_caller_errno(0);
if (tmpdir != NULL)
*tmpdir = rval;
return rval;
err:
if (tmpdir != NULL && t != NULL)
*tmpdir = t;
(void) with_fallback_errno(EPERM);
return NULL; return NULL;
} }
@@ -251,13 +230,11 @@ tmpdir_policy(const char *path,
{ {
int saved_errno = errno; int saved_errno = errno;
int r; int r;
errno = 0;
if (path == NULL || if (if_err(path == NULL ||
allow_noworld_unsticky == NULL) { allow_noworld_unsticky == NULL, EFAULT))
goto err_tmpdir_policy;
errno = EFAULT;
return -1;
}
*allow_noworld_unsticky = 1; *allow_noworld_unsticky = 1;
@@ -273,11 +250,11 @@ tmpdir_policy(const char *path,
if (r > 0) if (r > 0)
*allow_noworld_unsticky = 0; *allow_noworld_unsticky = 0;
errno = saved_errno; reset_caller_errno(0);
return 0; return 0;
err_tmpdir_policy: err_tmpdir_policy:
return set_errno(saved_errno, EIO); return with_fallback_errno(EPERM);
} }
int int
@@ -290,63 +267,48 @@ same_dir(const char *a, const char *b)
struct stat st_b; struct stat st_b;
int saved_errno = errno; int saved_errno = errno;
int rval_scmp; int rval = 0; /* LOGICAL error, 0, if 0 is returned */
errno = 0;
/* optimisation: if both dirs /* optimisation: if both dirs
are the same, we don't need are the same, we don't need
to check anything. sehr schnell! to check anything. sehr schnell!
*/ */
/* bonus: scmp checks null for us */ /* bonus: scmp checks null for us */
if (!scmp(a, b, PATH_MAX, &rval_scmp)) if (!scmp(a, b, PATH_MAX, &rval))
goto success_same_dir; goto success_same_dir;
else
rval = 0; /* reset */
fd_a = fs_open(a, O_RDONLY | O_DIRECTORY | O_NOFOLLOW); if ((fd_a = fs_open(a, O_RDONLY | O_DIRECTORY | O_NOFOLLOW)) < 0 ||
if (fd_a < 0) (fd_b = fs_open(b, O_RDONLY | O_DIRECTORY | O_NOFOLLOW)) < 0 ||
goto err_same_dir; fstat(fd_a, &st_a) < 0 ||
fstat(fd_b, &st_b) < 0)
fd_b = fs_open(b, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
if (fd_b < 0)
goto err_same_dir;
if (fstat(fd_a, &st_a) < 0)
goto err_same_dir;
if (fstat(fd_b, &st_b) < 0)
goto err_same_dir; goto err_same_dir;
if (st_a.st_dev == st_b.st_dev && if (st_a.st_dev == st_b.st_dev &&
st_a.st_ino == st_b.st_ino) { st_a.st_ino == st_b.st_ino) {
close_on_eintr(&fd_a);
close_on_eintr(&fd_b);
success_same_dir: success_same_dir:
rval = 1; /* SUCCESS */
/* SUCCESS
*/
errno = saved_errno;
return 1;
} }
close_on_eintr(&fd_a); close_on_eintr(&fd_a);
close_on_eintr(&fd_b); close_on_eintr(&fd_b);
/* FAILURE (logical) /* we reset caller errno regardless
* of success, so long as it's not
* a syscall error
*/ */
reset_caller_errno(0);
errno = saved_errno; return rval;
return 0;
err_same_dir: err_same_dir:
/* FAILURE (probably syscall) - returns -1
/* FAILURE (probably syscall)
*/ */
close_on_eintr(&fd_a); close_on_eintr(&fd_a);
close_on_eintr(&fd_b); close_on_eintr(&fd_b);
return set_errno(saved_errno, EIO); return with_fallback_errno(EIO); /* -1 */
} }
/* bypass_all_sticky_checks: if set, /* bypass_all_sticky_checks: if set,
@@ -367,81 +329,45 @@ world_writeable_and_sticky(
int dirfd = -1; int dirfd = -1;
int saved_errno = errno; int saved_errno = errno;
errno = 0;
if (s == NULL || *s == '\0') { if (if_err(s == NULL || *s == '\0', EINVAL) ||
errno = EINVAL; (dirfd = fs_open(s, O_RDONLY | O_DIRECTORY)) < 0 ||
fstat(dirfd, &st) < 0 ||
if_err(!S_ISDIR(st.st_mode), ENOTDIR))
goto sticky_hell; goto sticky_hell;
}
/* mitigate symlink attacks*
*/
dirfd = fs_open(s, O_RDONLY | O_DIRECTORY);
if (dirfd < 0)
goto sticky_hell;
if (fstat(dirfd, &st) < 0)
goto sticky_hell;
if (!S_ISDIR(st.st_mode)) {
errno = ENOTDIR;
goto sticky_hell;
}
/* all of these checks are probably
* redundant (execution rights)
if (!(st.st_mode & S_IXUSR) ||
!(st.st_mode & S_IXGRP) ||
!(st.st_mode & S_IXOTH)) {
*/
/* just require it for *you*, for now */
if (!(st.st_mode & S_IXUSR)) {
errno = EACCES;
goto sticky_hell;
}
/* *normal-**ish mode (libc): /* *normal-**ish mode (libc):
*/ */
if (bypass_all_sticky_checks) if (bypass_all_sticky_checks)
goto sticky_heaven; /* normal == no security */ goto sticky_heaven; /* normal == no security */
/* extremely not-libc mode: /* extremely not-libc mode:
* only require stickiness on world-writeable dirs:
*/ */
if (st.st_mode & S_IWOTH) { /* world writeable */ if (st.st_mode & S_IWOTH) { /* world writeable */
/* if world-writeable, only if (if_err(!(st.st_mode & S_ISVTX), EPERM))
* allow sticky files goto sticky_hell; /* not sticky */
*/
if (st.st_mode & S_ISVTX)
goto sticky_heaven; /* sticky */
errno = EPERM; goto sticky_heaven; /* sticky! */
goto sticky_hell; /* not sticky */ } else if (allow_noworld_unsticky) {
goto sticky_heaven; /* sticky visa */
} else {
goto sticky_hell; /* visa denied */
} }
/* for good measure */
if (faccessat(dirfd, ".", X_OK, AT_EACCESS) < 0)
goto sticky_hell;
/* non-world-writeable, so
* stickiness is do-not-care
*/
if (allow_noworld_unsticky)
goto sticky_heaven; /* sticky! */
goto sticky_hell; /* heaven visa denied */
sticky_heaven: sticky_heaven:
close_on_eintr(&dirfd); if (faccessat(dirfd, ".", X_OK, AT_EACCESS) < 0)
errno = saved_errno; goto sticky_hell; /* down you go! */
close_on_eintr(&dirfd);
reset_caller_errno(0);
return 1; return 1;
sticky_hell: sticky_hell:
close_on_eintr(&dirfd); close_on_eintr(&dirfd);
(void) with_fallback_errno(EPERM);
(void) set_errno(saved_errno, EPERM);
return 0; return 0;
} }
@@ -503,7 +429,7 @@ mkhtemp(int *fd,
char *template, char *template,
int dirfd, int dirfd,
const char *fname, const char *fname,
struct stat *st_dir_initial, struct stat *st_dir_first,
int type) int type)
{ {
size_t template_len = 0; size_t template_len = 0;
@@ -521,11 +447,13 @@ mkhtemp(int *fd,
int r; int r;
char *end; char *end;
errno = 0;
if (if_err(fd == NULL || template == NULL || fname == NULL || if (if_err(fd == NULL || template == NULL || fname == NULL ||
st_dir_initial == NULL, EFAULT) || st_dir_first == NULL, EFAULT) ||
if_err(*fd >= 0, EEXIST) || if_err(*fd >= 0, EEXIST) ||
if_err(dirfd < 0, EBADF)) if_err(dirfd < 0, EBADF))
return -1; goto err;
/* count X */ /* count X */
for (end = template + slen(template, PATH_MAX, &template_len); for (end = template + slen(template, PATH_MAX, &template_len);
@@ -533,15 +461,15 @@ mkhtemp(int *fd,
fname_len = slen(fname, PATH_MAX, &fname_len); fname_len = slen(fname, PATH_MAX, &fname_len);
if (if_err(strrchr(fname, '/') != NULL, EINVAL)) if (if_err(strrchr(fname, '/') != NULL, EINVAL))
return -1; goto err;
if (if_err(xc < 3 || xc > template_len, EINVAL) || if (if_err(xc < 3 || xc > template_len, EINVAL) ||
if_err(fname_len > template_len, EOVERFLOW)) if_err(fname_len > template_len, EOVERFLOW))
return -1; goto err;
if (if_err(vcmp(fname, template + template_len - fname_len, if (if_err(vcmp(fname, template + template_len - fname_len,
fname_len) != 0, EINVAL)) fname_len) != 0, EINVAL))
return -1; goto err;
/* fname_copy = templatestr region only; p points to trailing XXXXXX */ /* fname_copy = templatestr region only; p points to trailing XXXXXX */
memcpy(smalloc(&fname_copy, fname_len + 1), memcpy(smalloc(&fname_copy, fname_len + 1),
@@ -552,7 +480,7 @@ mkhtemp(int *fd,
for (retries = 0; retries < MKHTEMP_RETRY_MAX; retries++) { for (retries = 0; retries < MKHTEMP_RETRY_MAX; retries++) {
r = mkhtemp_try_create(dirfd, r = mkhtemp_try_create(dirfd,
st_dir_initial, fname_copy, st_dir_first, fname_copy,
p, xc, fd, st, type); p, xc, fd, st, type);
if (r == 0) if (r == 0)
@@ -569,19 +497,22 @@ mkhtemp(int *fd,
} }
errno = EEXIST; errno = EEXIST;
err: err:
close_on_eintr(fd); close_on_eintr(fd);
free_and_set_null(&fname_copy);
return with_fallback_errno(EIO);
success: success:
free_and_set_null(&fname_copy); free_and_set_null(&fname_copy);
return (*fd >= 0) ? *fd : -1; reset_caller_errno(0);
return *fd;
} }
int int
mkhtemp_try_create(int dirfd, mkhtemp_try_create(int dirfd,
struct stat *st_dir_initial, struct stat *st_dir_first,
char *fname_copy, char *fname_copy,
char *p, char *p,
size_t xc, size_t xc,
@@ -597,8 +528,10 @@ mkhtemp_try_create(int dirfd,
int file_created = 0; int file_created = 0;
int dir_created = 0; int dir_created = 0;
errno = 0;
if (if_err(fd == NULL || st == NULL || p ==NULL || fname_copy ==NULL || if (if_err(fd == NULL || st == NULL || p ==NULL || fname_copy ==NULL ||
st_dir_initial == NULL, EFAULT) || st_dir_first == NULL, EFAULT) ||
if_err(*fd >= 0, EEXIST)) if_err(*fd >= 0, EEXIST))
goto err; goto err;
@@ -608,14 +541,14 @@ mkhtemp_try_create(int dirfd,
memcpy(p, rstr = rchars(xc), xc); memcpy(p, rstr = rchars(xc), xc);
free_and_set_null(&rstr); free_and_set_null(&rstr);
if (if_err_sys(fd_verify_dir_identity(dirfd, st_dir_initial) < 0)) if (if_err_sys(fd_verify_dir_identity(dirfd, st_dir_first) < 0))
goto err; goto err;
if (type == MKHTEMP_FILE) { if (type == MKHTEMP_FILE) {
#ifdef __linux__ #ifdef __linux__
/* try O_TMPFILE fast path */ /* try O_TMPFILE fast path */
if (mkhtemp_tmpfile_linux(dirfd, if (mkhtemp_tmpfile_linux(dirfd,
st_dir_initial, fname_copy, st_dir_first, fname_copy,
p, xc, fd, st) == 0) { p, xc, fd, st) == 0) {
errno = saved_errno; errno = saved_errno;
@@ -645,7 +578,7 @@ mkhtemp_try_create(int dirfd,
dir_created = 1; dir_created = 1;
/* do it again (mitigate directory race) */ /* do it again (mitigate directory race) */
if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0) if (fd_verify_dir_identity(dirfd, st_dir_first) < 0)
goto err; goto err;
if ((*fd = openat_on_eintr(dirfd, fname_copy, if ((*fd = openat_on_eintr(dirfd, fname_copy,
@@ -657,7 +590,7 @@ mkhtemp_try_create(int dirfd,
goto err; goto err;
/* NOTE: pointless to check nlink here (only just opened) */ /* NOTE: pointless to check nlink here (only just opened) */
if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0) if (fd_verify_dir_identity(dirfd, st_dir_first) < 0)
goto err; goto err;
} }
@@ -680,7 +613,7 @@ mkhtemp_try_create(int dirfd,
if (type == MKHTEMP_FILE) { if (type == MKHTEMP_FILE) {
if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0) if (fd_verify_dir_identity(dirfd, st_dir_first) < 0)
goto err; goto err;
if (secure_file(fd, st, &st_open, if (secure_file(fd, st, &st_open,
@@ -689,7 +622,7 @@ mkhtemp_try_create(int dirfd,
} else { /* dir: MKHTEMP_DIR */ } else { /* dir: MKHTEMP_DIR */
if (fd_verify_identity(*fd, &st_open, st_dir_initial) < 0) if (fd_verify_identity(*fd, &st_open, st_dir_first) < 0)
goto err; goto err;
if (if_err(!S_ISDIR(st_open.st_mode), ENOTDIR) || if (if_err(!S_ISDIR(st_open.st_mode), ENOTDIR) ||
@@ -698,10 +631,11 @@ mkhtemp_try_create(int dirfd,
goto err; goto err;
} }
errno = saved_errno;
rval = 1; rval = 1;
goto out;
out:
reset_caller_errno(0);
return rval;
err: err:
close_on_eintr(fd); close_on_eintr(fd);
@@ -710,9 +644,7 @@ err:
if (dir_created) if (dir_created)
(void) unlinkat(dirfd, fname_copy, AT_REMOVEDIR); (void) unlinkat(dirfd, fname_copy, AT_REMOVEDIR);
rval = -1; return with_fallback_errno(EPERM);
out:
return rval;
} }
/* linux has its own special hardening /* linux has its own special hardening
@@ -725,7 +657,7 @@ out:
#ifdef __linux__ #ifdef __linux__
int int
mkhtemp_tmpfile_linux(int dirfd, mkhtemp_tmpfile_linux(int dirfd,
struct stat *st_dir_initial, struct stat *st_dir_first,
char *fname_copy, char *fname_copy,
char *p, char *p,
size_t xc, size_t xc,
@@ -737,22 +669,21 @@ mkhtemp_tmpfile_linux(int dirfd,
size_t retries; size_t retries;
int linked = 0; int linked = 0;
char *rstr = NULL; char *rstr = NULL;
errno = 0;
if (fd == NULL || st == NULL || if (if_err(fd == NULL || st == NULL ||
fname_copy == NULL || p == NULL || fname_copy == NULL || p == NULL ||
st_dir_initial == NULL) { st_dir_first == NULL, EFAULT))
errno = EFAULT; goto err;
return -1;
}
/* create unnamed tmpfile */ /* create unnamed tmpfile */
tmpfd = openat(dirfd, ".", tmpfd = openat(dirfd, ".",
O_TMPFILE | O_RDWR | O_CLOEXEC, 0600); O_TMPFILE | O_RDWR | O_CLOEXEC, 0600);
if (tmpfd < 0) if (tmpfd < 0)
return -1; goto err;
if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0) if (fd_verify_dir_identity(dirfd, st_dir_first) < 0)
goto err; goto err;
for (retries = 0; retries < MKHTEMP_RETRY_MAX; retries++) { for (retries = 0; retries < MKHTEMP_RETRY_MAX; retries++) {
@@ -761,30 +692,19 @@ mkhtemp_tmpfile_linux(int dirfd,
free_and_set_null(&rstr); free_and_set_null(&rstr);
if (fd_verify_dir_identity(dirfd, if (fd_verify_dir_identity(dirfd,
st_dir_initial) < 0) st_dir_first) < 0)
goto err; goto err;
if (linkat(tmpfd, "", if (linkat(tmpfd, "", dirfd, fname_copy, AT_EMPTY_PATH) == 0) {
dirfd, fname_copy,
AT_EMPTY_PATH) == 0) {
linked = 1; /* file created */ linked = 1; /* file created */
if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0) if (fd_verify_dir_identity(dirfd, st_dir_first) < 0 ||
fstat(*fd = tmpfd, st) < 0 ||
secure_file(fd, st, st, O_APPEND, 1, 1, 0600) < 0)
goto err; goto err;
/* success */ goto out;
*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) if (errno != EEXIST)
@@ -794,13 +714,15 @@ mkhtemp_tmpfile_linux(int dirfd,
} }
errno = EEXIST; errno = EEXIST;
err: err:
if (linked) if (linked)
(void) unlinkat(dirfd, fname_copy, 0); (void) unlinkat(dirfd, fname_copy, 0);
close_on_eintr(&tmpfd); close_on_eintr(&tmpfd);
return -1; return with_fallback_errno(EIO);
out:
reset_caller_errno(0);
return 0;
} }
#endif #endif
@@ -821,6 +743,7 @@ int secure_file(int *fd,
int flags; int flags;
struct stat st_now; struct stat st_now;
int saved_errno = errno; int saved_errno = errno;
errno = 0;
if (if_err(fd == NULL || st == NULL, EFAULT) || if (if_err(fd == NULL || st == NULL, EFAULT) ||
if_err(*fd < 0, EBADF) || if_err(*fd < 0, EBADF) ||
@@ -862,23 +785,28 @@ int secure_file(int *fd,
if (fchmod(*fd, mode) == -1) if (fchmod(*fd, mode) == -1)
goto err_demons; goto err_demons;
errno = saved_errno; reset_caller_errno(0);
return 0; return 0;
err_demons: err_demons:
return set_errno(saved_errno, EIO); return with_fallback_errno(EIO);
} }
int int
fd_verify_regular(int fd, fd_verify_regular(int fd,
const struct stat *expected, const struct stat *expected,
struct stat *out) struct stat *out)
{if ( {
if_err_sys(fd_verify_identity(fd, expected, out) < 0) || int saved_errno = errno;
if_err(!S_ISREG(out->st_mode), EBADF) errno = 0;
) return -1;
else if (if_err_sys(fd_verify_identity(fd, expected, out) < 0) ||
if_err(!S_ISREG(out->st_mode), EBADF)) {
return with_fallback_errno(EIO);
} else {
reset_caller_errno(0);
return 0; /* regular file */ return 0; /* regular file */
}
} }
int int
@@ -888,17 +816,18 @@ fd_verify_identity(int fd,
{ {
struct stat st_now; struct stat st_now;
int saved_errno = errno; int saved_errno = errno;
errno = 0;
if( if_err(fd < 0 || expected == NULL, EFAULT) || if( if_err(fd < 0 || expected == NULL, EFAULT) ||
if_err_sys(fstat(fd, &st_now)) || if_err_sys(fstat(fd, &st_now)) ||
if_err(st_now.st_dev != expected->st_dev || if_err(st_now.st_dev != expected->st_dev ||
st_now.st_ino != expected->st_ino, ESTALE)) st_now.st_ino != expected->st_ino, ESTALE))
return -1; return with_fallback_errno(EIO);
if (out != NULL) if (out != NULL)
*out = st_now; *out = st_now;
errno = saved_errno; reset_caller_errno(0);
return 0; return 0;
} }
@@ -908,45 +837,35 @@ fd_verify_dir_identity(int fd,
{ {
struct stat st_now; struct stat st_now;
int saved_errno = errno; int saved_errno = errno;
errno = 0;
if (if_err(fd < 0 || expected == NULL, EFAULT) || if (if_err(fd < 0 || expected == NULL, EFAULT) ||
if_err_sys(fstat(fd, &st_now) < 0)) if_err_sys(fstat(fd, &st_now) < 0) ||
return -1; if_err(st_now.st_dev != expected->st_dev, ESTALE) ||
if_err(st_now.st_ino != expected->st_ino, ESTALE) ||
if_err(!S_ISDIR(st_now.st_mode), ENOTDIR))
goto err;
if (st_now.st_dev != expected->st_dev || reset_caller_errno(0);
st_now.st_ino != expected->st_ino) {
errno = ESTALE;
return -1;
}
if (!S_ISDIR(st_now.st_mode)) {
errno = ENOTDIR;
return -1;
}
errno = saved_errno;
return 0; return 0;
err:
return with_fallback_errno(EIO);
} }
int int
is_owner(struct stat *st) is_owner(struct stat *st)
{ {
if (st == NULL) { int saved_errno = errno;
errno = 0;
errno = EFAULT; if (if_err(st == NULL, EFAULT) ||
return -1; if_err(st->st_uid != geteuid() /* someone else's file */
} #if defined(ALLOW_ROOT_OVERRIDE) && ((ALLOW_ROOT_OVERRIDE) > 0)
&& geteuid() != 0 /* override for root */
#if ALLOW_ROOT_OVERRIDE #endif
if (st->st_uid != geteuid() && /* someone else's file */ , EPERM)) return with_fallback_errno(EIO);
geteuid() != 0) { /* override for root */
#else
if (st->st_uid != geteuid()) { /* someone else's file */
#endif /* and no root override */
errno = EPERM;
return -1;
}
reset_caller_errno(0);
return 0; return 0;
} }
@@ -955,6 +874,7 @@ lock_file(int fd, int flags)
{ {
struct flock fl; struct flock fl;
int saved_errno = errno; int saved_errno = errno;
errno = 0;
if (if_err(fd < 0, EBADF) || if (if_err(fd < 0, EBADF) ||
if_err(flags < 0, EINVAL)) if_err(flags < 0, EINVAL))
@@ -972,9 +892,9 @@ lock_file(int fd, int flags)
if (fcntl(fd, F_SETLK, &fl) == -1) if (fcntl(fd, F_SETLK, &fl) == -1)
goto err_lock_file; goto err_lock_file;
saved_errno = errno; reset_caller_errno(0);
return 0; return 0;
err_lock_file: err_lock_file:
return set_errno(saved_errno, EIO); return with_fallback_errno(EIO);
} }
+2 -4
View File
@@ -28,8 +28,6 @@
unsigned short unsigned short
hextonum(char ch_s) hextonum(char ch_s)
{ {
int saved_errno = errno;
unsigned char ch; unsigned char ch;
size_t rval; size_t rval;
@@ -43,8 +41,8 @@ hextonum(char ch_s)
if ((unsigned int)(ch - 'a') <= 5) if ((unsigned int)(ch - 'a') <= 5)
return ch - 'a' + 10; return ch - 'a' + 10;
if (ch == '?' || ch == 'x') if (ch == '?' || ch == 'x') /* random */
return rsize(16); /* <-- with rejection sampling! */ return (short)rsize(16); /* <-- with rejection sampling! */
return 16; return 16;
} }
+5 -6
View File
@@ -127,6 +127,7 @@ void
rset(void *buf, size_t n) rset(void *buf, size_t n)
{ {
int saved_errno = errno; int saved_errno = errno;
errno = 0;
if (if_err(buf == NULL, EFAULT)) if (if_err(buf == NULL, EFAULT))
goto err; goto err;
@@ -140,7 +141,6 @@ rset(void *buf, size_t n)
((USE_URANDOM) > 0)) ((USE_URANDOM) > 0))
arc4random_buf(buf, n); arc4random_buf(buf, n);
goto out;
#else #else
size_t off = 0; size_t off = 0;
@@ -166,7 +166,7 @@ retry_rand:
#endif #endif
if (rc < 0) if (rc < 0)
goto err; goto err; /* syscall fehler */
if (rc == 0) if (rc == 0)
goto err; /* prevent infinite loop on fatal err */ goto err; /* prevent infinite loop on fatal err */
@@ -180,16 +180,15 @@ retry_rand:
#endif #endif
#endif #endif
out: reset_caller_errno(0);
errno = saved_errno;
return; return;
err: err:
#if defined(USE_URANDOM) && \ #if defined(USE_URANDOM) && \
((USE_URANDOM) > 0) ((USE_URANDOM) > 0)
close_on_eintr(&fd); close_on_eintr(&fd);
#endif #endif
err_exit(ECANCELED, (void) with_fallback_errno(ECANCELED);
"Randomisation failure, possibly unsupported in your kernel"); err_exit(errno, "Randomisierungsfehler");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
#endif #endif
+35 -19
View File
@@ -50,6 +50,7 @@ pagesize(void)
{ {
static long rval = 0; static long rval = 0;
static int set = 0; static int set = 0;
int saved_errno = 0;
if (!set) { if (!set) {
if ((rval = sysconf(_SC_PAGESIZE)) < 0) if ((rval = sysconf(_SC_PAGESIZE)) < 0)
@@ -57,6 +58,7 @@ pagesize(void)
set = 1; set = 1;
} }
reset_caller_errno(0);
return rval; return rval;
} }
@@ -92,7 +94,9 @@ smalloc(char **buf, size_t size)
void * void *
vmalloc(void **buf, size_t size) vmalloc(void **buf, size_t size)
{ {
int saved_errno = errno;
void *rval = NULL; void *rval = NULL;
errno = 0;
if (size >= SIZE_MAX - 1) if (size >= SIZE_MAX - 1)
err_exit(EOVERFLOW, "integer overflow in vmalloc"); err_exit(EOVERFLOW, "integer overflow in vmalloc");
@@ -116,6 +120,7 @@ vmalloc(void **buf, size_t size)
if ((rval = malloc(size)) == NULL) if ((rval = malloc(size)) == NULL)
err_exit(errno, "malloc fail in vmalloc"); err_exit(errno, "malloc fail in vmalloc");
reset_caller_errno(0);
return *buf = rval; return *buf = rval;
} }
@@ -131,6 +136,7 @@ scmp(const char *a,
size_t wa; size_t wa;
size_t wb; size_t wb;
int saved_errno = errno; int saved_errno = errno;
errno = 0;
if (if_err(a == NULL || b == NULL || rval == NULL, EFAULT)) if (if_err(a == NULL || b == NULL || rval == NULL, EFAULT))
goto err; goto err;
@@ -171,14 +177,14 @@ scmp(const char *a,
goto out; goto out;
err: err:
(void) set_errno(saved_errno, EFAULT); (void) with_fallback_errno(EFAULT);
if (rval != NULL) if (rval != NULL)
*rval = -1; *rval = -1;
err_exit(errno, "scmp"); err_exit(errno, "scmp");
return -1; return -1;
out: out:
errno = saved_errno; reset_caller_errno(0);
return *rval; return *rval;
} }
@@ -215,13 +221,14 @@ slen(const char *s,
size_t i = 0; size_t i = 0;
size_t w; size_t w;
size_t j; size_t j;
errno = 0;
if (if_err(s == NULL || rval == NULL, EFAULT)) if (if_err(s == NULL || rval == NULL, EFAULT))
goto err; goto err;
for ( ; ((uintptr_t)(s + i) % sizeof(size_t)) != 0; i++) { for ( ; ((uintptr_t)(s + i) % sizeof(size_t)) != 0; i++) {
if (i >= maxlen) if (if_err(i >= maxlen, EOVERFLOW))
goto err; goto err;
if (s[i] == '\0') { if (s[i] == '\0') {
*rval = i; *rval = i;
@@ -252,14 +259,14 @@ slen(const char *s,
} }
err: err:
(void) set_errno(saved_errno, EFAULT); (void) with_fallback_errno(EFAULT);
if (rval != NULL) if (rval != NULL)
*rval = 0; *rval = 0;
err_exit(errno, "slen"); /* abort */ err_exit(errno, "slen"); /* abort */
return 0; /* gcc15 is happy */ return 0; /* gcc15 is happy */
out: out:
errno = saved_errno; reset_caller_errno(0);
return *rval; return *rval;
} }
@@ -273,6 +280,7 @@ sdup(const char *s,
size_t i = 0; size_t i = 0;
char *out = NULL; char *out = NULL;
int saved_errno = errno; int saved_errno = errno;
errno = 0;
if (if_err(dest == NULL || *dest != NULL || s == NULL, EFAULT)) if (if_err(dest == NULL || *dest != NULL || s == NULL, EFAULT))
goto err; goto err;
@@ -326,12 +334,12 @@ err:
if (dest != NULL) if (dest != NULL)
*dest = NULL; *dest = NULL;
(void) set_errno(saved_errno, EFAULT); (void) with_fallback_errno(EFAULT);
err_exit(errno, "sdup"); err_exit(errno, "sdup");
return NULL; return NULL;
out: out:
errno = saved_errno; reset_caller_errno(0);
return *dest; return *dest;
} }
@@ -345,6 +353,7 @@ scatn(ssize_t sc, const char **sv,
char *rcur = NULL; char *rcur = NULL;
char *rtmp = NULL; char *rtmp = NULL;
size_t i; size_t i;
errno = 0;
if (if_err(sc < 2, EINVAL) || if (if_err(sc < 2, EINVAL) ||
if_err(sv == NULL, EFAULT) || if_err(sv == NULL, EFAULT) ||
@@ -368,7 +377,7 @@ scatn(ssize_t sc, const char **sv,
rtmp = NULL; rtmp = NULL;
} }
errno = saved_errno; reset_caller_errno(0);
*rval = final; *rval = final;
return *rval; return *rval;
err: err:
@@ -376,7 +385,7 @@ err:
free_and_set_null(&rtmp); free_and_set_null(&rtmp);
free_and_set_null(&final); free_and_set_null(&final);
(void) set_errno(saved_errno, EFAULT); (void) with_fallback_errno(EFAULT);
err_exit(errno, "scatn"); err_exit(errno, "scatn");
return NULL; return NULL;
@@ -391,6 +400,7 @@ scat(const char *s1, const char *s2,
size_t size2; size_t size2;
char *rval = NULL; char *rval = NULL;
int saved_errno = errno; int saved_errno = errno;
errno = 0;
if (if_err(dest == NULL || *dest != NULL, EFAULT)) if (if_err(dest == NULL || *dest != NULL, EFAULT))
goto err; goto err;
@@ -408,11 +418,11 @@ scat(const char *s1, const char *s2,
memcpy(rval + size1, s2, size2); memcpy(rval + size1, s2, size2);
*(rval + size1 + size2) = '\0'; *(rval + size1 + size2) = '\0';
reset_caller_errno(0);
*dest = rval; *dest = rval;
errno = saved_errno;
return *dest; return *dest;
err: err:
(void) set_errno(saved_errno, EINVAL); (void) with_fallback_errno(EINVAL);
if (dest != NULL) if (dest != NULL)
*dest = NULL; *dest = NULL;
err_exit(errno, "scat"); err_exit(errno, "scat");
@@ -431,6 +441,7 @@ dcat(const char *s, size_t n,
char *rval1 = NULL; char *rval1 = NULL;
char *rval2 = NULL; char *rval2 = NULL;
int saved_errno = errno; int saved_errno = errno;
errno = 0;
if (if_err(dest1 == NULL || dest2 == NULL, EFAULT)) if (if_err(dest1 == NULL || dest2 == NULL, EFAULT))
goto err; goto err;
@@ -450,7 +461,7 @@ dcat(const char *s, size_t n,
*dest1 = rval1; *dest1 = rval1;
*dest2 = rval2; *dest2 = rval2;
errno = saved_errno; reset_caller_errno(0);
return; return;
err: err:
@@ -459,7 +470,7 @@ err:
free_and_set_null(&rval1); free_and_set_null(&rval1);
free_and_set_null(&rval2); free_and_set_null(&rval2);
(void) set_errno(saved_errno, EINVAL); (void) with_fallback_errno(EINVAL);
err_exit(errno, "dcat"); err_exit(errno, "dcat");
} }
@@ -477,6 +488,7 @@ vcmp(const void *s1, const void *s2, size_t n)
const unsigned char *x; const unsigned char *x;
const unsigned char *y; const unsigned char *y;
errno = 0;
if (if_err(s1 == NULL || s2 == NULL, EFAULT)) if (if_err(s1 == NULL || s2 == NULL, EFAULT))
err_exit(EFAULT, "vcmp: null input"); err_exit(EFAULT, "vcmp: null input");
@@ -497,7 +509,7 @@ vcmp(const void *s1, const void *s2, size_t n)
if (x[i] != y[i]) if (x[i] != y[i])
return (int)x[i] - (int)y[i]; return (int)x[i] - (int)y[i];
errno = saved_errno; reset_caller_errno(0);
return 0; return 0;
} }
@@ -507,9 +519,9 @@ vcmp(const void *s1, const void *s2, size_t n)
* under error condition. * under error condition.
*/ */
int int
set_errno(int saved_errno, int fallback) with_fallback_errno(int fallback)
{ {
if (errno == saved_errno) if (!errno)
errno = fallback; errno = fallback;
return -1; return -1;
} }
@@ -525,7 +537,8 @@ err_exit(int nvm_errval, const char *msg, ...)
func_t err_cleanup = errhook(NULL); func_t err_cleanup = errhook(NULL);
err_cleanup(); err_cleanup();
errno = saved_errno; reset_caller_errno(0);
saved_errno = errno;
if (!errno) if (!errno)
saved_errno = errno = ECANCELED; saved_errno = errno = ECANCELED;
@@ -536,6 +549,7 @@ err_exit(int nvm_errval, const char *msg, ...)
vfprintf(stderr, msg, args); vfprintf(stderr, msg, args);
va_end(args); va_end(args);
errno = saved_errno;
fprintf(stderr, ": %s\n", strerror(errno)); fprintf(stderr, ": %s\n", strerror(errno));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@@ -615,11 +629,12 @@ xpledgex(const char *promises, const char *execpromises)
{ {
int saved_errno = errno; int saved_errno = errno;
(void) promises, (void) execpromises, (void) saved_errno; (void) promises, (void) execpromises, (void) saved_errno;
errno = 0;
#ifdef __OpenBSD__ #ifdef __OpenBSD__
if (pledge(promises, execpromises) == -1) if (pledge(promises, execpromises) == -1)
err_exit(errno, "pledge"); err_exit(errno, "pledge");
#endif #endif
errno = saved_errno; reset_caller_errno(0);
return 0; return 0;
} }
int int
@@ -627,10 +642,11 @@ xunveilx(const char *path, const char *permissions)
{ {
int saved_errno = errno; int saved_errno = errno;
(void) path, (void) permissions, (void) saved_errno; (void) path, (void) permissions, (void) saved_errno;
errno = 0;
#ifdef __OpenBSD__ #ifdef __OpenBSD__
if (pledge(promises, execpromises) == -1) if (pledge(promises, execpromises) == -1)
err_exit(errno, "pledge"); err_exit(errno, "pledge");
#endif #endif
errno = saved_errno; reset_caller_errno(0);
return 0; return 0;
} }
-1
View File
@@ -37,7 +37,6 @@ main(int argc, char **argv)
fprintf(stderr, "\n%s\n", same ? "You win!" : "You lose!"); fprintf(stderr, "\n%s\n", same ? "You win!" : "You lose!");
printf("%lu\n", PATH_MAX);
return same ? EXIT_SUCCESS : EXIT_FAILURE; return same ? EXIT_SUCCESS : EXIT_FAILURE;
} }
+6 -8
View File
@@ -1,11 +1,13 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT ( >:3 )
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org> * Copyright (c) 2026 Leah Rowe <leah@libreboot.org> /| |\
* * / \
* Hardened mktemp (mkhtemp!) * Hardened mktemp (mkhtemp!)
* *
* WORK IN PROGRESS (proof of concept), or, v0.0000001 * WORK IN PROGRESS (proof of concept), or, v0.0000001
* DO NOT PUT THIS IN YOUR LINUX DISTRO YET. * DO NOT PUT THIS IN YOUR LINUX DISTRO YET.
* *
* In other words: for reference only -- PATCHES WELCOME!
*
* I will remove this notice when the code is mature, and * I will remove this notice when the code is mature, and
* probably contact several of your projects myself. * probably contact several of your projects myself.
* *
@@ -139,8 +141,4 @@ static void
exit_cleanup(void) exit_cleanup(void)
{ {
return; return;
}/* }
( >:3 )
/| |\
/ \ */
+3 -3
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT ( >:3 )
* Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org> * Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org> /| |\
* * / \
* This tool lets you modify Intel GbE NVM (Gigabit Ethernet * This tool lets you modify Intel GbE NVM (Gigabit Ethernet
* Non-Volatile Memory) images, e.g. change the MAC address. * Non-Volatile Memory) images, e.g. change the MAC address.
* These images configure your Intel Gigabit Ethernet adapter. * These images configure your Intel Gigabit Ethernet adapter.