Compare commits

...

63 Commits

Author SHA1 Message Date
Leah Rowe
d5a08963c3 libreboot-utils: add dragonflybsd to arc4random
it supports arc4random since forever

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 12:00:18 +00:00
Leah Rowe
e9cefe81b0 libreboot-utils: usleep 100 on rand failure
it's extremely unlikely that a 2nd call would
also fail. this is fine.

it mitigates DoS attacks (entropy exhaustion)

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 11:37:55 +00:00
Leah Rowe
6db9514c95 libreboot-utils: tidy up the rand code
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 11:28:44 +00:00
Leah Rowe
49cc239884 util/mkhtemp: allow zero as a rand value
yes, zero is a valid response.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 11:13:56 +00:00
Leah Rowe
7bff5712b4 util/libreboot-utils: simplified rand
only use the getrandom syscall on linux,
or arc4random.

the /dev/urandom fallback is removed, and
we use the syscall; failure is almost certainly
unlikely, but if it fails, we abort. this
provides therefore the same guarantee as
bsd arc4random, since it will never return
under fault conditions. it will only ever
return success, or abort.

nobody should be using /dev/urandom in 2026.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 11:08:24 +00:00
Leah Rowe
a6da25ad0b libreboot-utils: remove 1989 rand
added as an academic exercise, but pointless
in the year 2026.

or even the year 1989.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 10:38:37 +00:00
Leah Rowe
f7f1856969 libreboot-utils: move rand to own file
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 10:34:37 +00:00
Leah Rowe
e1ff02f323 util/libreboot-utils: added more string functions
also reset pointer values

because i can

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 07:51:51 +00:00
Leah Rowe
5f93b7faec lib/mkhtemp: fix bad string comparison
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 04:58:23 +00:00
Leah Rowe
7efdc40cab lib/mkhtemp: rename suffix to template
suffix is a gnu term, for its mktemp version.
it goes after the template.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 00:44:13 +00:00
Leah Rowe
1228f7e0e5 util/mkhtemp: require at least 3 X
the GNU one requires 3. we should be compatible
with them. i'm going to work on the compatibility
mode - this is phase one!

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 00:30:39 +00:00
Leah Rowe
f06db344ad mkhtemp: fix err()
calling it indirectly was out of the question.

must call it directly.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-25 00:03:31 +00:00
Leah Rowe
3ddd7a0d36 util/mkhtemp: add -q option (silence errors)
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 23:31:51 +00:00
Leah Rowe
2cee58188c util/nvmutil: make tmp files dotfiles
so that they are hidden. yes.

mkhtemp can take any template now

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 22:44:43 +00:00
Leah Rowe
1ed2ca6b69 util/libreboot-utils: rename err() to b0rk()
it behaves a bit differently than err(), so it's
not good to confuse readers

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 22:44:27 +00:00
Leah Rowe
4fc4946f3c util/nvmutil: fix name size in fs_resolve_at
it's capped at 256 bytes

we need it configurable and in sync with
other limits in the code.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 22:21:22 +00:00
Leah Rowe
f8d9c51a36 util/mkhtemp: template support on util
just add a template like yo uwould on other mktemp.
it works perfectly now.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 22:19:40 +00:00
Leah Rowe
b8a045ef86 util/mkhtemp: allow relative path with -p
but only -p

not inside the library. that way, we retain
security. symlinks resolved with use of -p;
a warning will be added about this to the
manpage, when written.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 21:31:33 +00:00
Leah Rowe
715723c7ce mkhtemp: harden tmpdir access control
faccessat used this way respects uid/gid,
handles ACLs (where used), and matches whatt
many real security tools might do.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 20:18:15 +00:00
Leah Rowe
b16bb6c445 util/mkhtemp: loosen execution restriction
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 20:15:14 +00:00
Leah Rowe
75f03ea696 util/mkhtemp: add directory override (-p) option.
-p dir

to override TMPDIR

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 20:14:47 +00:00
Leah Rowe
88ff5f7380 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>
2026-03-24 19:57:12 +00:00
Leah Rowe
bf5a3df796 mkhtemp: fix bad comparison
pointers are null, not zero.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 19:57:07 +00:00
Leah Rowe
3522a23587 util/nvmutil: use renameat for atomic write
not rename(). use renameat()

this re-uses the logic added for mkhtemp.

this will later enable more stringent
integrity checks, though we already verify
the integrity of a file after writing it
back, and renameat is always tied to the
descriptor, so it's fine.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 19:19:33 +00:00
Leah Rowe
217ad55bed improve the error message
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 18:56:51 +00:00
Leah Rowe
c1befbcd3e util/nvmutil: never do cross-filesystem moves
make a local TMPDIR instead, where gbe.bin is.
this avoids the EXDEV errno, so we don't have
to handle it, and it's just better performant
for everyone.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 18:46:36 +00:00
Leah Rowe
6593e76c6a TODO: exdev handling still broken on nvmutil
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 17:33:03 +00:00
Leah Rowe
e9c5da1a25 util/nvmutil: use new fs_open functions for gbe
this unifies nvmutil's file handling with the
handling used by mkhtemp. a special function
has been written for this. this allows greater
flexibility since we can more easily check the
integrity of a file at inode/dev level; this
complements nvmutil's existing content-based
verification.

(this also fixes nvmutil, so that gbe files can
be changed again. mkhtemp broke it while i was
writing it, but now everything works again)

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 17:18:47 +00:00
Leah Rowe
56ab5a18fe mkhtemp: show progname on error
i have my own getprogname implementation,
because not every libc is good enough to
include one.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 16:35:40 +00:00
Leah Rowe
9de01208b0 comment
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 09:59:49 +00:00
Leah Rowe
cce396a1ac libreboot-utils: general code cleanup
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 09:48:34 +00:00
Leah Rowe
e7ede0c755 mkhtemp: unified non-error close handling
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 07:06:39 +00:00
Leah Rowe
616099edad util/nvmutil: make it compile again
i was reorganising the state machine (singleton)
used for data, and part of what i wanted lead
to mkhtemp being written.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 04:43:52 +00:00
Leah Rowe
61ee661b88 nvmutil/state: remove unnecessary check
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 04:29:31 +00:00
Leah Rowe
8b8a5635c3 add comment about mkhtemp calling convention
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 03:15:19 +00:00
Leah Rowe
2c21a04741 util/mkhtemp: show path on error accessing it
a bit naughty the way i do it, but it works. without
this, the message gets clobbered by EINVAL due to
a bad call to vprintf in the err function.

in this way, we ensure that there is a path, and
thus the errno does not get clobbered. i also
removed the EPERM setting in the env_tmpdir
function, which also clobbered errno.

with this fix, if TMPDIR is set but invalid,
it should now show the error reliably.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 02:41:53 +00:00
Leah Rowe
a5eed39a76 lib/mkhtemp.c: use standard suffix
it may seem counterintuitive that a shorter suffix is
better, but i think we should ideally look just like
what is made my any other tool, and other mktemp
tools generate:

tmp.XXXXXXXXXX

this is the default, but of course it could be changed.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 01:51:59 +00:00
Leah Rowe
f2544d094b util/mkhtemp: new utility (hardened mktemp)
part of the same code library as nvmutil.

as part of this, i renamed util/nvmutil
to util/libreboot-utils/ because it is
now a multi-utility codebase.

this is more efficient, since i also wish
to use mkhtemp (function) in nvmutil.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-24 01:25:53 +00:00
Leah Rowe
afcd535816 mkhtemp: split library into its own file
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-23 23:31:00 +00:00
Leah Rowe
a9165d2503 mkhtemp: fail if TMPDIR is SET, but set wrong
right now we defer to fallbacks otherwise, which
is wrong.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-23 10:51:02 +00:00
Leah Rowe
ba80191b78 mkhtemp: PROPER fd leak/overwrite fix
now this code should be stable. no leaks.

yes. hardened mkhtemp. oh yeah mate.

now all i need is a main() and a getopt
loop, and pledge, unveil, and blackjack,
and something dubious of a titilating
nature.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-23 09:14:41 +00:00
Leah Rowe
8261553818 util/nvmutil: remove bad check in world stick func
yeah, of course we don't bloody own /tmp

duh

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-23 08:57:04 +00:00
Leah Rowe
7c414b2d84 mkhtemp: fix bad close
the fd in fs_resolve_at is subsequently used

note that in practise, this is not a real fix:
the best fix is to cache all descriptors and
free them at the end, once resolution is done.

not a real fix, because now fd leaks,
but it's dealt with on program close.

not a util yet. just just stubbing this in
main to test various features.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-23 08:48:34 +00:00
Leah Rowe
024862a152 mkhtemp: fix bad check
where the path is quite short and the number of X
is quite big compared to the rest of it, this
check will actually cause a false overflow
error. the maths are correct, just not the error

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-23 08:31:41 +00:00
Leah Rowe
c1c1aee695 mkhtemp: fix initialisied fd
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-23 08:21:18 +00:00
Leah Rowe
f18f995c42 WIP remove xdev from openat2p
causes error on cross mount links
e.g. /tmp tmpfs

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-23 06:42:57 +00:00
Leah Rowe
186d612ac7 WIP: "finished"
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-23 05:32:34 +00:00
Leah Rowe
1f7d4c72f4 remove dead code WIP
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-23 04:59:29 +00:00
Leah Rowe
c87e425442 WIP dir support (also demons)
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-23 04:43:47 +00:00
Leah Rowe
1684f475cd WIP identity check unification
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-22 23:43:03 +00:00
Leah Rowe
cf4be895f9 WIP verify after lock
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-22 23:28:40 +00:00
Leah Rowe
79376a900a WIP cleanup
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-22 23:27:09 +00:00
Leah Rowe
ca92e7831a cleanup WIP
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-22 21:54:04 +00:00
Leah Rowe
cc7b0a4381 WIP cleanup: split mkhtemp
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-22 20:38:01 +00:00
Leah Rowe
df99f345dc WIP cleanup
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-22 20:06:10 +00:00
Leah Rowe
a505cb261a WIP: remove local mode in mkhtemp + cleanup
bloat

unveil can get pledged

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-22 20:01:11 +00:00
Leah Rowe
b8ababa2be WIP: split out rand fill on mkhtemp
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-22 19:27:37 +00:00
Leah Rowe
535ab29deb WIP: file system sandboxing
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-22 19:13:43 +00:00
Leah Rowe
5818ec11bf openat2 WIP
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-22 17:47:05 +00:00
Leah Rowe
c766c819b3 WIP: fs_resolve_at
yes. mkhtemp is ccoming along nicely

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-22 16:50:01 +00:00
Leah Rowe
7694b307b8 WIP: always use openat
why would i write a secure mktemp to be used
on linux from 1999?????

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-22 16:32:52 +00:00
Leah Rowe
825d520575 WIP: pathless resolution
Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-22 15:45:57 +00:00
Leah Rowe
6838db4647 WIP: hardened mktemp
i'm pretty much nearly there. still no dir support,
only files.

i won't keep amending now - will do more, then
squash later.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2026-03-22 13:50:44 +00:00
23 changed files with 3553 additions and 1852 deletions

View File

@@ -6,7 +6,7 @@
cbcfgsdir="config/coreboot"
tmpromdel="$XBMK_CACHE/DO_NOT_FLASH"
nvmutil="util/nvmutil/nvmutil"
nvmutil="util/libreboot-utils/nvmutil"
ifdtool="elf/coreboot/default/ifdtool"
checkvars="CONFIG_GBE_BIN_PATH"
@@ -197,8 +197,8 @@ modify_mac()
x_ cp "${CONFIG_GBE_BIN_PATH##*../}" "$xbtmp/gbe"
if [ -n "$new_mac" ] && [ "$new_mac" != "restore" ]; then
x_ make -C util/nvmutil clean
x_ make -C util/nvmutil
x_ make -C util/libreboot-utils clean
x_ make -C util/libreboot-utils
x_ "$nvmutil" "$xbtmp/gbe" setmac "$new_mac"
fi

View File

@@ -1,5 +1,6 @@
/nvm
/nvmutil
/mkhtemp
*.bin
*.o
*.d

View File

@@ -24,8 +24,9 @@ STRICT = $(WARN) -std=c90 -pedantic -Werror
HELLFLAGS = $(STRICT) -Weverything
PROG = nvmutil
PROGMKH = mkhtemp
OBJS = \
OBJS_NVMUTIL = \
obj/nvmutil.o \
obj/lib/state.o \
obj/lib/file.o \
@@ -35,19 +36,33 @@ OBJS = \
obj/lib/num.o \
obj/lib/io.o \
obj/lib/checksum.o \
obj/lib/word.o
obj/lib/word.o \
obj/lib/mkhtemp.o \
obj/lib/rand.o
OBJS_MKHTEMP = \
obj/mkhtemp.o \
obj/lib/file.o \
obj/lib/string.o \
obj/lib/num.o \
obj/lib/mkhtemp.o \
obj/lib/rand.o
# default mode
CFLAGS_MODE = $(PORTABLE)
CC_MODE = $(CC)
all: $(PROG)
all: $(PROG) $(PROGMKH)
$(PROG): $(OBJS)
$(CC_MODE) $(OBJS) -o $(PROG) $(LDFLAGS)
$(PROG): $(OBJS_NVMUTIL)
$(CC_MODE) $(OBJS_NVMUTIL) -o $(PROG) $(LDFLAGS)
$(PROGMKH): $(OBJS_MKHTEMP)
$(CC_MODE) $(OBJS_MKHTEMP) -o $(PROGMKH) $(LDFLAGS)
# ensure obj directory exists
$(OBJS): obj
$(OBJS_NVMUTIL): obj
$(OBJS_MKHTEMP): obj
obj:
mkdir obj || true
@@ -58,6 +73,9 @@ obj:
obj/nvmutil.o: nvmutil.c
$(CC_MODE) $(CFLAGS_MODE) -c nvmutil.c -o obj/nvmutil.o
obj/mkhtemp.o: mkhtemp.c
$(CC_MODE) $(CFLAGS_MODE) -c mkhtemp.c -o obj/mkhtemp.o
# library/helper objects
obj/lib/state.o: lib/state.c
@@ -87,18 +105,27 @@ obj/lib/checksum.o: lib/checksum.c
obj/lib/word.o: lib/word.c
$(CC_MODE) $(CFLAGS_MODE) -c lib/word.c -o obj/lib/word.o
obj/lib/mkhtemp.o: lib/mkhtemp.c
$(CC_MODE) $(CFLAGS_MODE) -c lib/mkhtemp.c -o obj/lib/mkhtemp.o
obj/lib/rand.o: lib/rand.c
$(CC_MODE) $(CFLAGS_MODE) -c lib/rand.c -o obj/lib/rand.o
# install
install: $(PROG)
install: $(PROG) $(PROGMKH)
$(INSTALL) -d $(DESTDIR)$(PREFIX)/bin
$(INSTALL) $(PROG) $(DESTDIR)$(PREFIX)/bin/$(PROG)
chmod 755 $(DESTDIR)$(PREFIX)/bin/$(PROG)
$(INSTALL) $(PROGMKH) $(DESTDIR)$(PREFIX)/bin/$(PROGMKH)
chmod 755 $(DESTDIR)$(PREFIX)/bin/$(PROGMKH)
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/$(PROG)
rm -f $(DESTDIR)$(PREFIX)/bin/$(PROGMKH)
clean:
rm -f $(PROG) $(OBJS)
rm -f $(PROG) $(PROGMKH) $(OBJS_NVMUTIL) $(OBJS_MKHTEMP)
distclean: clean

View File

@@ -1,31 +1,25 @@
/* SPDX-License-Identifier: MIT
* Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
TODO: this file should be split, into headers for each
C source file specifically. it was originally just
for nvmutil, until i added mkhtemp to the mix
*/
#ifndef COMMON_H
#define COMMON_H
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <errno.h>
/* for linux getrandom
*/
#if defined(__linux__)
#include <errno.h>
#if defined(__has_include)
#if __has_include(<sys/random.h>)
#include <sys/random.h>
#define HAVE_GETRANDOM 1
#endif
#endif
#if !defined(HAVE_GETRANDOM)
#include <sys/syscall.h>
#if defined(SYS_getrandom)
#define HAVE_GETRANDOM_SYSCALL 1
#endif
#endif
#endif
#define items(x) (sizeof((x)) / sizeof((x)[0]))
@@ -35,11 +29,27 @@
int fchmod(int fd, mode_t mode);
/* analog of SSIZE_MAX
#define MKHTEMP_RETRY_MAX 512
#define MKHTEMP_SPIN_THRESHOLD 32
#define MKHTEMP_FILE 0
#define MKHTEMP_DIR 1
/* if 1: on operations that
* check ownership, always
* permit root to access even
* if not the file/dir owner
*/
#ifndef ALLOW_ROOT_OVERRIDE
#define ALLOW_ROOT_OVERRIDE 0
#endif
/*
*/
#ifndef X_LONG_MAX
#define X_LONG_MAX ((long)(~((long)1 << (sizeof(long)*CHAR_BIT-1))))
#ifndef SSIZE_MAX
#define SSIZE_MAX ((ssize_t)(~((ssize_t)1 << (sizeof(ssize_t)*CHAR_BIT-1))))
#endif
@@ -72,8 +82,8 @@ int fchmod(int fd, mode_t mode);
#define MAX_ZERO_RW_RETRY 5
#endif
#ifndef HAVE_REAL_PREAD_PWRITE
#define HAVE_REAL_PREAD_PWRITE 0
#ifndef REAL_POS_IO
#define REAL_POS_IO 0
#endif
#ifndef LOOP_EAGAIN
@@ -95,6 +105,10 @@ int fchmod(int fd, mode_t mode);
#define EXIT_SUCCESS 0
#endif
#ifndef O_NOCTTY
#define O_NOCTTY 0
#endif
#ifndef O_ACCMODE
#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
#endif
@@ -208,14 +222,14 @@ int fchmod(int fd, mode_t mode);
*/
struct commands {
unsigned long chk;
size_t chk;
char *str;
void (*run)(void);
int argc;
unsigned char arg_part;
unsigned char chksum_read;
unsigned char chksum_write;
unsigned long rw_size; /* within the 4KB GbE part */
size_t rw_size; /* within the 4KB GbE part */
int flags; /* e.g. O_RDWR or O_RDONLY */
};
@@ -254,7 +268,7 @@ struct xfile {
off_t gbe_file_size;
off_t gbe_tmp_size;
unsigned long part;
size_t part;
unsigned char part_modified[2];
unsigned char part_valid[2];
@@ -262,6 +276,11 @@ struct xfile {
unsigned char bufcmp[GBE_BUF_SIZE]; /* compare gbe/tmp/reads */
unsigned char pad[GBE_WORK_SIZE]; /* the file that wouldn't die */
/* we later rename in-place, using old fd. renameat() */
int dirfd;
char *base;
char *tmpbase;
};
/* Command table, MAC address, files
@@ -278,7 +297,7 @@ struct xstate {
char *argv0;
unsigned long i; /* index to cmd[] for current command */
size_t i; /* index to cmd[] for current command */
int no_cmd;
/* Cat commands set this.
@@ -286,28 +305,39 @@ struct xstate {
int cat;
};
struct filesystem {
int rootfd;
};
struct xstate *xstatus(int argc, char *argv[]);
struct xstate *xstart(int argc, char *argv[]);
struct xstate *xstatus(void);
/* Sanitize command tables.
*/
void sanitize_command_list(void);
void sanitize_command_index(unsigned long c);
void sanitize_command_index(size_t c);
/* Argument handling (user input)
*/
void set_cmd(int argc, char *argv[]);
void set_cmd_args(int argc, char *argv[]);
unsigned long conv_argv_part_num(const char *part_str);
int xstrxcmp(const char *a, const char *b, unsigned long maxlen);
size_t conv_argv_part_num(const char *part_str);
/* Prep files for reading
*/
void open_gbe_file(void);
int fd_verify_regular(int fd,
const struct stat *expected,
struct stat *out);
int fd_verify_identity(int fd,
const struct stat *expected,
struct stat *out);
int fd_verify_dir_identity(int fd,
const struct stat *expected);
int is_owner(struct stat *st);
int lock_file(int fd, int flags);
int same_file(int fd, struct stat *st_old, int check_size);
void xopen(int *fd, const char *path, int flags, struct stat *st);
@@ -318,45 +348,50 @@ void xopen(int *fd, const char *path, int flags, struct stat *st);
void copy_gbe(void);
void read_file(void);
void read_checksums(void);
int good_checksum(unsigned long partnum);
int good_checksum(size_t partnum);
/* validate commands
*/
void check_command_num(unsigned long c);
unsigned char valid_command(unsigned long c);
void check_command_num(size_t c);
unsigned char valid_command(size_t c);
/* Helper functions for command: setmac
*/
void cmd_helper_setmac(void);
void parse_mac_string(void);
unsigned long xstrxlen(const char *scmp, unsigned long maxlen);
void set_mac_byte(unsigned long mac_byte_pos);
void set_mac_nib(unsigned long mac_str_pos,
unsigned long mac_byte_pos, unsigned long mac_nib_pos);
void set_mac_byte(size_t mac_byte_pos);
void set_mac_nib(size_t mac_str_pos,
size_t mac_byte_pos, size_t mac_nib_pos);
void write_mac_part(size_t partnum);
/* string functions
*/
int slen(const char *scmp, size_t maxlen,
size_t *rval);
int scmp(const char *a, const char *b,
size_t maxlen, int *rval);
int sdup(const char *s,
size_t n, char **dest);
int scat(const char *s1, const char *s2,
size_t n, char **dest);
int dcat(const char *s, size_t n,
size_t off, char **dest1,
char **dest2);
/* numerical functions
*/
unsigned short hextonum(char ch_s);
unsigned long rlong(void);
#if !(defined(FALLBACK_RAND_1989) && \
((FALLBACK_RAND_1989) > 0))
#if defined(__linux__)
#if defined(HAVE_GETRANDOM) || \
defined(HAVE_GETRANDOM_SYSCALL)
int fallback_rand_getrandom(void *buf, size_t len);
#endif
#endif
#else
unsigned long fallback_rand_1989(void);
unsigned long entropy_jitter(void);
#endif
void write_mac_part(unsigned long partnum);
size_t rlong(void);
/* Helper functions for command: dump
*/
void cmd_helper_dump(void);
void print_mac_from_nvm(unsigned long partnum);
void hexdump(unsigned long partnum);
void print_mac_from_nvm(size_t partnum);
void hexdump(size_t partnum);
/* Helper functions for command: swap
*/
@@ -375,7 +410,7 @@ void cmd_helper_copy(void);
void cmd_helper_cat(void);
void cmd_helper_cat16(void);
void cmd_helper_cat128(void);
void cat(unsigned long nff);
void cat(size_t nff);
void cat_buf(unsigned char *b);
/* Command verification/control
@@ -388,51 +423,51 @@ void cmd_helper_err(void);
*/
void write_gbe_file(void);
void set_checksum(unsigned long part);
unsigned short calculated_checksum(unsigned long p);
void set_checksum(size_t part);
unsigned short calculated_checksum(size_t p);
/* NVM read/write
*/
unsigned short nvm_word(unsigned long pos16, unsigned long part);
void set_nvm_word(unsigned long pos16,
unsigned long part, unsigned short val16);
void set_part_modified(unsigned long p);
void check_nvm_bound(unsigned long pos16, unsigned long part);
void check_bin(unsigned long a, const char *a_name);
unsigned short nvm_word(size_t pos16, size_t part);
void set_nvm_word(size_t pos16,
size_t part, unsigned short val16);
void set_part_modified(size_t p);
void check_nvm_bound(size_t pos16, size_t part);
void check_bin(size_t a, const char *a_name);
/* GbE file read/write
*/
void rw_gbe_file_part(unsigned long p, int rw_type,
void rw_gbe_file_part(size_t p, int rw_type,
const char *rw_type_str);
void write_to_gbe_bin(void);
int gbe_mv(void);
void check_written_part(unsigned long p);
void check_written_part(size_t p);
void report_io_err_rw(void);
unsigned char *gbe_mem_offset(unsigned long part, const char *f_op);
off_t gbe_file_offset(unsigned long part, const char *f_op);
off_t gbe_x_offset(unsigned long part, const char *f_op,
unsigned char *gbe_mem_offset(size_t part, const char *f_op);
off_t gbe_file_offset(size_t part, const char *f_op);
off_t gbe_x_offset(size_t part, const char *f_op,
const char *d_type, off_t nsize, off_t ncmp);
long rw_gbe_file_exact(int fd, unsigned char *mem, unsigned long nrw,
ssize_t rw_gbe_file_exact(int fd, unsigned char *mem, size_t nrw,
off_t off, int rw_type);
/* Generic read/write
*/
int fsync_dir(const char *path);
long rw_file_exact(int fd, unsigned char *mem, unsigned long len,
ssize_t rw_file_exact(int fd, unsigned char *mem, size_t len,
off_t off, int rw_type, int loop_eagain, int loop_eintr,
unsigned long max_retries, int off_reset);
long prw(int fd, void *mem, unsigned long nrw,
size_t max_retries, int off_reset);
ssize_t prw(int fd, void *mem, size_t nrw,
off_t off, int rw_type, int loop_eagain, int loop_eintr,
int off_reset);
int io_args(int fd, void *mem, unsigned long nrw,
int io_args(int fd, void *mem, size_t nrw,
off_t off, int rw_type);
int check_file(int fd, struct stat *st);
long rw_over_nrw(long r, unsigned long nrw);
#if !defined(HAVE_REAL_PREAD_PWRITE) || \
HAVE_REAL_PREAD_PWRITE < 1
ssize_t rw_over_nrw(ssize_t r, size_t nrw);
#if !defined(REAL_POS_IO) || \
REAL_POS_IO < 1
off_t lseek_on_eintr(int fd, off_t off,
int whence, int loop_eagain, int loop_eintr);
#endif
@@ -442,18 +477,79 @@ int try_err(int loop_err, int errval);
*/
void usage(void);
void err(int nvm_errval, const char *msg, ...);
void err_no_cleanup(int stfu, int nvm_errval, const char *msg, ...);
void b0rk(int nvm_errval, const char *msg, ...);
int exit_cleanup(void);
const char *getnvmprogname(void);
/* Portable libc functions
void err_mkhtemp(int stfu, int errval, const char *msg, ...);
/* libc hardening
*/
char *new_tmpfile(int *fd, int local, const char *path);
int mkstemp_n(char *template);
char *get_tmpdir(void);
int new_tmpfile(int *fd, char **path, char *tmpdir,
const char *template);
int new_tmpdir(int *fd, char **path, char *tmpdir,
const char *template);
int new_tmp_common(int *fd, char **path, int type,
char *tmpdir, const char *template);
int mkhtemp_try_create(int dirfd,
struct stat *st_dir_initial,
char *fname_copy,
char *p,
size_t xc,
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);
int mkhtemp_fill_random(char *p, size_t xc);
int world_writeable_and_sticky(const char *s,
int sticky_allowed, int always_sticky);
int same_dir(const char *a, const char *b);
int tmpdir_policy(const char *path,
int *allow_noworld_unsticky);
char *env_tmpdir(int always_sticky, char **tmpdir,
char *override_tmpdir);
int secure_file(int *fd,
struct stat *st,
struct stat *expected,
int bad_flags,
int check_seek,
int do_lock,
mode_t mode);
int close_on_eintr(int fd);
int fsync_on_eintr(int fd);
int fs_rename_at(int olddirfd, const char *old,
int newdirfd, const char *new);
int fs_open(const char *path, int flags);
void close_no_err(int *fd);
void free_if_null(char **buf);
int close_warn(int *fd, char *s);
struct filesystem *rootfs(void);
int fs_resolve_at(int dirfd, const char *path, int flags);
int fs_next_component(const char **p,
char *name, size_t namesz);
int fs_open_component(int dirfd, const char *name,
int flags, int is_last);
int fs_dirname_basename(const char *path,
char **dir, char **base, int allow_relative);
int openat2p(int dirfd, const char *path,
int flags, mode_t mode);
int mkdirat_on_eintr(int dirfd,
const char *pathname, mode_t mode);
int if_err(int condition, int errval);
int if_err_sys(int condition);
char *lbgetprogname(char *argv0);
/* asserts */
@@ -467,16 +563,16 @@ typedef char static_assert_unsigned_short_is_2[
typedef char static_assert_short_is_2[(sizeof(short) >= 2) ? 1 : -1];
typedef char static_assert_unsigned_int_is_4[
(sizeof(unsigned int) >= 4) ? 1 : -1];
typedef char static_assert_unsigned_long_is_4[
(sizeof(unsigned long) >= 4) ? 1 : -1];
typedef char static_assert_long_ulong[
(sizeof(unsigned long) == sizeof(long)) ? 1 : -1];
typedef char static_assert_unsigned_ssize_t_is_4[
(sizeof(size_t) >= 4) ? 1 : -1];
typedef char static_assert_ssize_t_ussize_t[
(sizeof(size_t) == sizeof(ssize_t)) ? 1 : -1];
typedef char static_assert_int_ge_32[(sizeof(int) >= 4) ? 1 : -1];
typedef char static_assert_twos_complement[
((-1 & 3) == 3) ? 1 : -1
];
typedef char assert_unsigned_long_ptr[
(sizeof(unsigned long) >= sizeof(void *)) ? 1 : -1
typedef char assert_unsigned_ssize_t_ptr[
(sizeof(size_t) >= sizeof(void *)) ? 1 : -1
];
/*

View File

@@ -17,12 +17,12 @@
void
read_checksums(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct commands *cmd = &x->cmd[x->i];
struct xfile *f = &x->f;
unsigned long _p;
unsigned long _skip_part;
size_t _p;
size_t _skip_part;
unsigned char _num_invalid;
unsigned char _max_invalid;
@@ -59,16 +59,16 @@ read_checksums(void)
if (_num_invalid >= _max_invalid) {
if (_max_invalid == 1)
err(ECANCELED, "%s: part %lu has a bad checksum",
f->fname, (unsigned long)f->part);
b0rk(ECANCELED, "%s: part %lu has a bad checksum",
f->fname, (size_t)f->part);
err(ECANCELED, "%s: No valid checksum found in file",
b0rk(ECANCELED, "%s: No valid checksum found in file",
f->fname);
}
}
int
good_checksum(unsigned long partnum)
good_checksum(size_t partnum)
{
unsigned short expected_checksum;
unsigned short actual_checksum;
@@ -87,16 +87,16 @@ good_checksum(unsigned long partnum)
}
void
set_checksum(unsigned long p)
set_checksum(size_t p)
{
check_bin(p, "part number");
set_nvm_word(NVM_CHECKSUM_WORD, p, calculated_checksum(p));
}
unsigned short
calculated_checksum(unsigned long p)
calculated_checksum(size_t p)
{
unsigned long c;
size_t c;
unsigned int val16;
val16 = 0;

View File

@@ -21,10 +21,10 @@
void
sanitize_command_list(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
unsigned long c;
unsigned long num_commands;
size_t c;
size_t num_commands;
num_commands = items(x->cmd);
@@ -33,37 +33,41 @@ sanitize_command_list(void)
}
void
sanitize_command_index(unsigned long c)
sanitize_command_index(size_t c)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct commands *cmd = &x->cmd[c];
int _flag;
unsigned long gbe_rw_size;
size_t gbe_rw_size;
size_t rval;
check_command_num(c);
if (cmd->argc < 3)
err(EINVAL, "cmd index %lu: argc below 3, %d",
(unsigned long)c, cmd->argc);
b0rk(EINVAL, "cmd index %lu: argc below 3, %d",
(size_t)c, cmd->argc);
if (cmd->str == NULL)
err(EINVAL, "cmd index %lu: NULL str",
(unsigned long)c);
b0rk(EINVAL, "cmd index %lu: NULL str",
(size_t)c);
if (*cmd->str == '\0')
err(EINVAL, "cmd index %lu: empty str",
(unsigned long)c);
b0rk(EINVAL, "cmd index %lu: empty str",
(size_t)c);
if (xstrxlen(cmd->str, MAX_CMD_LEN + 1) >
MAX_CMD_LEN) {
err(EINVAL, "cmd index %lu: str too long: %s",
(unsigned long)c, cmd->str);
if (slen(cmd->str, MAX_CMD_LEN +1, &rval) < 0)
b0rk(errno, "Could not get command length");
if (rval > MAX_CMD_LEN) {
b0rk(EINVAL, "cmd index %lu: str too long: %s",
(size_t)c, cmd->str);
}
if (cmd->run == NULL)
err(EINVAL, "cmd index %lu: cmd ptr null",
(unsigned long)c);
b0rk(EINVAL, "cmd index %lu: cmd ptr null",
(size_t)c);
check_bin(cmd->arg_part, "cmd.arg_part");
check_bin(cmd->chksum_read, "cmd.chksum_read");
@@ -76,36 +80,40 @@ sanitize_command_index(unsigned long c)
case NVM_SIZE:
break;
default:
err(EINVAL, "Unsupported rw_size: %lu",
(unsigned long)gbe_rw_size);
b0rk(EINVAL, "Unsupported rw_size: %lu",
(size_t)gbe_rw_size);
}
if (gbe_rw_size > GBE_PART_SIZE)
err(EINVAL, "rw_size larger than GbE part: %lu",
(unsigned long)gbe_rw_size);
b0rk(EINVAL, "rw_size larger than GbE part: %lu",
(size_t)gbe_rw_size);
_flag = (cmd->flags & O_ACCMODE);
if (_flag != O_RDONLY &&
_flag != O_RDWR)
err(EINVAL, "invalid cmd.flags setting");
b0rk(EINVAL, "invalid cmd.flags setting");
}
void
set_cmd(int argc, char *argv[])
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
const char *cmd;
unsigned long c;
int rval;
size_t c;
for (c = 0; c < items(x->cmd); c++) {
cmd = x->cmd[c].str;
/* not the right command */
if (xstrxcmp(argv[2], cmd, MAX_CMD_LEN) != 0)
continue;
if (scmp(argv[2], cmd, MAX_CMD_LEN, &rval) < 0)
err_no_cleanup(0, EINVAL,
"could not compare command strings");
if (rval != 0)
continue; /* not the right command */
/* valid command found */
if (argc >= x->cmd[c].argc) {
@@ -115,18 +123,19 @@ set_cmd(int argc, char *argv[])
return;
}
err(EINVAL,
err_no_cleanup(0, EINVAL,
"Too few args on command '%s'", cmd);
}
x->no_cmd = 1;
}
void
set_cmd_args(int argc, char *argv[])
{
struct xstate *x = xstatus(0, NULL);
unsigned long i = x->i;
struct xstate *x = xstatus();
size_t i = x->i;
struct commands *cmd = &x->cmd[i];
struct xfile *f = &x->f;
@@ -139,11 +148,11 @@ set_cmd_args(int argc, char *argv[])
/* Maintainer bug
*/
if (cmd->arg_part && argc < 4)
err(EINVAL,
b0rk(EINVAL,
"arg_part set for command that needs argc4");
if (cmd->arg_part && i == CMD_SETMAC)
err(EINVAL,
b0rk(EINVAL,
"arg_part set on CMD_SETMAC");
if (i == CMD_SETMAC) {
@@ -159,35 +168,35 @@ set_cmd_args(int argc, char *argv[])
}
}
unsigned long
size_t
conv_argv_part_num(const char *part_str)
{
unsigned char ch;
if (part_str[0] == '\0' || part_str[1] != '\0')
err(EINVAL, "Partnum string '%s' wrong length", part_str);
b0rk(EINVAL, "Partnum string '%s' wrong length", part_str);
/* char signedness is implementation-defined
*/
ch = (unsigned char)part_str[0];
if (ch < '0' || ch > '1')
err(EINVAL, "Bad part number (%c)", ch);
b0rk(EINVAL, "Bad part number (%c)", ch);
return (unsigned long)(ch - '0');
return (size_t)(ch - '0');
}
void
check_command_num(unsigned long c)
check_command_num(size_t c)
{
if (!valid_command(c))
err(EINVAL, "Invalid run_cmd arg: %lu",
(unsigned long)c);
b0rk(EINVAL, "Invalid run_cmd arg: %lu",
(size_t)c);
}
unsigned char
valid_command(unsigned long c)
valid_command(size_t c)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct commands *cmd;
if (c >= items(x->cmd))
@@ -196,7 +205,7 @@ valid_command(unsigned long c)
cmd = &x->cmd[c];
if (c != cmd->chk)
err(EINVAL,
b0rk(EINVAL,
"Invalid cmd chk value (%lu) vs arg: %lu",
cmd->chk, c);
@@ -206,10 +215,10 @@ valid_command(unsigned long c)
void
cmd_helper_setmac(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct macaddr *mac = &x->mac;
unsigned long partnum;
size_t partnum;
check_cmd(cmd_helper_setmac, "setmac");
@@ -223,13 +232,18 @@ cmd_helper_setmac(void)
void
parse_mac_string(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct macaddr *mac = &x->mac;
unsigned long mac_byte;
size_t mac_byte;
if (xstrxlen(x->mac.str, 18) != 17)
err(EINVAL, "MAC address is the wrong length");
size_t rval;
if (slen(x->mac.str, 18, &rval) < 0)
b0rk(EINVAL, "Could not determine MAC length");
if (rval != 17)
b0rk(EINVAL, "MAC address is the wrong length");
memset(mac->mac_buf, 0, sizeof(mac->mac_buf));
@@ -237,28 +251,28 @@ parse_mac_string(void)
set_mac_byte(mac_byte);
if ((mac->mac_buf[0] | mac->mac_buf[1] | mac->mac_buf[2]) == 0)
err(EINVAL, "Must not specify all-zeroes MAC address");
b0rk(EINVAL, "Must not specify all-zeroes MAC address");
if (mac->mac_buf[0] & 1)
err(EINVAL, "Must not specify multicast MAC address");
b0rk(EINVAL, "Must not specify multicast MAC address");
}
void
set_mac_byte(unsigned long mac_byte_pos)
set_mac_byte(size_t mac_byte_pos)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct macaddr *mac = &x->mac;
char separator;
unsigned long mac_str_pos;
unsigned long mac_nib_pos;
size_t mac_str_pos;
size_t mac_nib_pos;
mac_str_pos = mac_byte_pos * 3;
if (mac_str_pos < 15) {
if ((separator = mac->str[mac_str_pos + 2]) != ':')
err(EINVAL, "Invalid MAC address separator '%c'",
b0rk(EINVAL, "Invalid MAC address separator '%c'",
separator);
}
@@ -267,10 +281,10 @@ set_mac_byte(unsigned long mac_byte_pos)
}
void
set_mac_nib(unsigned long mac_str_pos,
unsigned long mac_byte_pos, unsigned long mac_nib_pos)
set_mac_nib(size_t mac_str_pos,
size_t mac_byte_pos, size_t mac_nib_pos)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct macaddr *mac = &x->mac;
char mac_ch;
@@ -278,9 +292,13 @@ set_mac_nib(unsigned long mac_str_pos,
mac_ch = mac->str[mac_str_pos + mac_nib_pos];
if ((hex_num = hextonum(mac_ch)) > 15)
err(EINVAL, "Invalid character '%c'",
mac->str[mac_str_pos + mac_nib_pos]);
if ((hex_num = hextonum(mac_ch)) > 15) {
if (hex_num >= 17)
b0rk(EIO, "Randomisation failure");
else
b0rk(EINVAL, "Invalid character '%c'",
mac->str[mac_str_pos + mac_nib_pos]);
}
/* If random, ensure that local/unicast bits are set.
*/
@@ -298,13 +316,13 @@ set_mac_nib(unsigned long mac_str_pos,
}
void
write_mac_part(unsigned long partnum)
write_mac_part(size_t partnum)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct xfile *f = &x->f;
struct macaddr *mac = &x->mac;
unsigned long w;
size_t w;
check_bin(partnum, "part number");
if (!f->part_valid[partnum])
@@ -314,17 +332,17 @@ write_mac_part(unsigned long partnum)
set_nvm_word(w, partnum, mac->mac_buf[w]);
printf("Wrote MAC address to part %lu: ",
(unsigned long)partnum);
(size_t)partnum);
print_mac_from_nvm(partnum);
}
void
cmd_helper_dump(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct xfile *f = &x->f;
unsigned long p;
size_t p;
check_cmd(cmd_helper_dump, "dump");
@@ -338,12 +356,12 @@ cmd_helper_dump(void)
fprintf(stderr,
"BAD checksum %04x in part %lu (expected %04x)\n",
nvm_word(NVM_CHECKSUM_WORD, p),
(unsigned long)p,
(size_t)p,
calculated_checksum(p));
}
printf("MAC (part %lu): ",
(unsigned long)p);
(size_t)p);
print_mac_from_nvm(p);
@@ -352,9 +370,9 @@ cmd_helper_dump(void)
}
void
print_mac_from_nvm(unsigned long partnum)
print_mac_from_nvm(size_t partnum)
{
unsigned long c;
size_t c;
unsigned short val16;
for (c = 0; c < 3; c++) {
@@ -373,16 +391,16 @@ print_mac_from_nvm(unsigned long partnum)
}
void
hexdump(unsigned long partnum)
hexdump(size_t partnum)
{
unsigned long c;
unsigned long row;
size_t c;
size_t row;
unsigned short val16;
for (row = 0; row < 8; row++) {
printf("%08lx ",
(unsigned long)((unsigned long)row << 4));
(size_t)((size_t)row << 4));
for (c = 0; c < 8; c++) {
@@ -404,24 +422,24 @@ hexdump(unsigned long partnum)
void
cmd_helper_swap(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct xfile *f = &x->f;
check_cmd(cmd_helper_swap, "swap");
memcpy(
f->buf + (unsigned long)GBE_WORK_SIZE,
f->buf + (size_t)GBE_WORK_SIZE,
f->buf,
GBE_PART_SIZE);
memcpy(
f->buf,
f->buf + (unsigned long)GBE_PART_SIZE,
f->buf + (size_t)GBE_PART_SIZE,
GBE_PART_SIZE);
memcpy(
f->buf + (unsigned long)GBE_PART_SIZE,
f->buf + (unsigned long)GBE_WORK_SIZE,
f->buf + (size_t)GBE_PART_SIZE,
f->buf + (size_t)GBE_WORK_SIZE,
GBE_PART_SIZE);
set_part_modified(0);
@@ -431,14 +449,14 @@ cmd_helper_swap(void)
void
cmd_helper_copy(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct xfile *f = &x->f;
check_cmd(cmd_helper_copy, "copy");
memcpy(
f->buf + (unsigned long)((f->part ^ 1) * GBE_PART_SIZE),
f->buf + (unsigned long)(f->part * GBE_PART_SIZE),
f->buf + (size_t)((f->part ^ 1) * GBE_PART_SIZE),
f->buf + (size_t)(f->part * GBE_PART_SIZE),
GBE_PART_SIZE);
set_part_modified(f->part ^ 1);
@@ -447,7 +465,7 @@ cmd_helper_copy(void)
void
cmd_helper_cat(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
check_cmd(cmd_helper_cat, "cat");
@@ -458,7 +476,7 @@ cmd_helper_cat(void)
void
cmd_helper_cat16(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
check_cmd(cmd_helper_cat16, "cat16");
@@ -469,7 +487,7 @@ cmd_helper_cat16(void)
void
cmd_helper_cat128(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
check_cmd(cmd_helper_cat128, "cat128");
@@ -478,20 +496,20 @@ cmd_helper_cat128(void)
}
void
cat(unsigned long nff)
cat(size_t nff)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct xfile *f = &x->f;
unsigned long p;
unsigned long ff;
size_t p;
size_t ff;
p = 0;
ff = 0;
if ((unsigned long)x->cat != nff) {
if ((size_t)x->cat != nff) {
err(ECANCELED, "erroneous call to cat");
b0rk(ECANCELED, "erroneous call to cat");
}
fflush(NULL);
@@ -501,7 +519,7 @@ cat(unsigned long nff)
for (p = 0; p < 2; p++) {
cat_buf(f->bufcmp +
(unsigned long)(p * (f->gbe_file_size >> 1)));
(size_t)(p * (f->gbe_file_size >> 1)));
for (ff = 0; ff < nff; ff++) {
@@ -514,22 +532,22 @@ void
cat_buf(unsigned char *b)
{
if (b == NULL)
err(errno, "null pointer in cat command");
b0rk(errno, "null pointer in cat command");
if (rw_file_exact(STDOUT_FILENO, b,
GBE_PART_SIZE, 0, IO_WRITE, LOOP_EAGAIN, LOOP_EINTR,
MAX_ZERO_RW_RETRY, OFF_ERR) < 0)
err(errno, "stdout: cat");
b0rk(errno, "stdout: cat");
}
void
check_cmd(void (*fn)(void),
const char *name)
{
struct xstate *x = xstatus(0, NULL);
unsigned long i = x->i;
struct xstate *x = xstatus();
size_t i = x->i;
if (x->cmd[i].run != fn)
err(ECANCELED, "Running %s, but cmd %s is set",
b0rk(ECANCELED, "Running %s, but cmd %s is set",
name, x->cmd[i].str);
/* prevent second command
@@ -541,6 +559,6 @@ check_cmd(void (*fn)(void),
void
cmd_helper_err(void)
{
err(ECANCELED,
b0rk(ECANCELED,
"Erroneously running command twice");
}

File diff suppressed because it is too large Load Diff

View File

@@ -21,7 +21,7 @@
void
open_gbe_file(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct commands *cmd = &x->cmd[x->i];
struct xfile *f = &x->f;
@@ -29,19 +29,19 @@ open_gbe_file(void)
xopen(&f->gbe_fd, f->fname,
cmd->flags | O_BINARY |
O_NOFOLLOW | O_CLOEXEC, &f->gbe_st);
O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, &f->gbe_st);
if (f->gbe_st.st_nlink > 1)
err(EINVAL,
b0rk(EINVAL,
"%s: warning: file has multiple (%lu) hard links\n",
f->fname, (unsigned long)f->gbe_st.st_nlink);
f->fname, (size_t)f->gbe_st.st_nlink);
if (f->gbe_st.st_nlink == 0)
err(EIO, "%s: file unlinked while open", f->fname);
b0rk(EIO, "%s: file unlinked while open", f->fname);
_flags = fcntl(f->gbe_fd, F_GETFL);
if (_flags == -1)
err(errno, "%s: fcntl(F_GETFL)", f->fname);
b0rk(errno, "%s: fcntl(F_GETFL)", f->fname);
/* O_APPEND allows POSIX write() to ignore
* the current write offset and write at EOF,
@@ -49,7 +49,7 @@ open_gbe_file(void)
*/
if (_flags & O_APPEND)
err(EIO, "%s: O_APPEND flag", f->fname);
b0rk(EIO, "%s: O_APPEND flag", f->fname);
f->gbe_file_size = f->gbe_st.st_size;
@@ -59,17 +59,17 @@ open_gbe_file(void)
case SIZE_128KB:
break;
default:
err(EINVAL, "File size must be 8KB, 16KB or 128KB");
b0rk(EINVAL, "File size must be 8KB, 16KB or 128KB");
}
if (lock_file(f->gbe_fd, cmd->flags) == -1)
err(errno, "%s: can't lock", f->fname);
b0rk(errno, "%s: can't lock", f->fname);
}
void
copy_gbe(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct xfile *f = &x->f;
read_file();
@@ -77,19 +77,19 @@ copy_gbe(void)
if (f->gbe_file_size == SIZE_8KB)
return;
memcpy(f->buf + (unsigned long)GBE_PART_SIZE,
f->buf + (unsigned long)(f->gbe_file_size >> 1),
(unsigned long)GBE_PART_SIZE);
memcpy(f->buf + (size_t)GBE_PART_SIZE,
f->buf + (size_t)(f->gbe_file_size >> 1),
(size_t)GBE_PART_SIZE);
}
void
read_file(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct xfile *f = &x->f;
struct stat _st;
long _r;
ssize_t _r;
/* read main file
*/
@@ -98,7 +98,7 @@ read_file(void)
MAX_ZERO_RW_RETRY, OFF_ERR);
if (_r < 0)
err(errno, "%s: read failed", f->fname);
b0rk(errno, "%s: read failed", f->fname);
/* copy to tmpfile
*/
@@ -107,55 +107,55 @@ read_file(void)
MAX_ZERO_RW_RETRY, OFF_ERR);
if (_r < 0)
err(errno, "%s: %s: copy failed",
b0rk(errno, "%s: %s: copy failed",
f->fname, f->tname);
/* file size comparison
*/
if (fstat(f->tmp_fd, &_st) == -1)
err(errno, "%s: stat", f->tname);
b0rk(errno, "%s: stat", f->tname);
f->gbe_tmp_size = _st.st_size;
if (f->gbe_tmp_size != f->gbe_file_size)
err(EIO, "%s: %s: not the same size",
b0rk(EIO, "%s: %s: not the same size",
f->fname, f->tname);
/* needs sync, for verification
*/
if (fsync_on_eintr(f->tmp_fd) == -1)
err(errno, "%s: fsync (tmpfile copy)", f->tname);
b0rk(errno, "%s: fsync (tmpfile copy)", f->tname);
_r = rw_file_exact(f->tmp_fd, f->bufcmp, f->gbe_file_size,
0, IO_PREAD, NO_LOOP_EAGAIN, LOOP_EINTR,
MAX_ZERO_RW_RETRY, OFF_ERR);
if (_r < 0)
err(errno, "%s: read failed (cmp)", f->tname);
b0rk(errno, "%s: read failed (cmp)", f->tname);
if (memcmp(f->buf, f->bufcmp, f->gbe_file_size) != 0)
err(errno, "%s: %s: read contents differ (pre-test)",
b0rk(errno, "%s: %s: read contents differ (pre-test)",
f->fname, f->tname);
}
void
write_gbe_file(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct commands *cmd = &x->cmd[x->i];
struct xfile *f = &x->f;
unsigned long p;
size_t p;
unsigned char update_checksum;
if ((cmd->flags & O_ACCMODE) == O_RDONLY)
return;
if (same_file(f->tmp_fd, &f->tmp_st, 0) < 0)
err(errno, "%s: file inode/device changed", f->tname);
b0rk(errno, "%s: file inode/device changed", f->tname);
if (same_file(f->gbe_fd, &f->gbe_st, 1) < 0)
err(errno, "%s: file has changed", f->fname);
b0rk(errno, "%s: file has changed", f->fname);
update_checksum = cmd->chksum_write;
@@ -171,25 +171,25 @@ write_gbe_file(void)
}
void
rw_gbe_file_part(unsigned long p, int rw_type,
rw_gbe_file_part(size_t p, int rw_type,
const char *rw_type_str)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct commands *cmd = &x->cmd[x->i];
struct xfile *f = &x->f;
long rval;
ssize_t rval;
off_t file_offset;
unsigned long gbe_rw_size;
size_t gbe_rw_size;
unsigned char *mem_offset;
gbe_rw_size = cmd->rw_size;
if (rw_type < IO_PREAD || rw_type > IO_PWRITE)
err(errno, "%s: %s: part %lu: invalid rw_type, %d",
f->fname, rw_type_str, (unsigned long)p, rw_type);
b0rk(errno, "%s: %s: part %lu: invalid rw_type, %d",
f->fname, rw_type_str, (size_t)p, rw_type);
mem_offset = gbe_mem_offset(p, rw_type_str);
file_offset = (off_t)gbe_file_offset(p, rw_type_str);
@@ -198,18 +198,18 @@ rw_gbe_file_part(unsigned long p, int rw_type,
gbe_rw_size, file_offset, rw_type);
if (rval == -1)
err(errno, "%s: %s: part %lu",
f->fname, rw_type_str, (unsigned long)p);
b0rk(errno, "%s: %s: part %lu",
f->fname, rw_type_str, (size_t)p);
if ((unsigned long)rval != gbe_rw_size)
err(EIO, "%s: partial %s: part %lu",
f->fname, rw_type_str, (unsigned long)p);
if ((size_t)rval != gbe_rw_size)
b0rk(EIO, "%s: partial %s: part %lu",
f->fname, rw_type_str, (size_t)p);
}
void
write_to_gbe_bin(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct commands *cmd = &x->cmd[x->i];
struct xfile *f = &x->f;
@@ -226,7 +226,7 @@ write_to_gbe_bin(void)
*/
if (fsync_on_eintr(f->tmp_fd) == -1)
err(errno, "%s: fsync (pre-verification)",
b0rk(errno, "%s: fsync (pre-verification)",
f->tname);
check_written_part(0);
@@ -235,19 +235,12 @@ write_to_gbe_bin(void)
report_io_err_rw();
if (f->io_err_gbe)
err(EIO, "%s: bad write", f->fname);
b0rk(EIO, "%s: bad write", f->fname);
saved_errno = errno;
if (close_on_eintr(f->tmp_fd) == -1) {
fprintf(stderr, "FAIL: %s: close\n", f->tname);
f->io_err_gbe_bin = 1;
}
if (close_on_eintr(f->gbe_fd) == -1) {
fprintf(stderr, "FAIL: %s: close\n", f->fname);
f->io_err_gbe_bin = 1;
}
f->io_err_gbe_bin |= -close_warn(&f->tmp_fd, f->tname);
f->io_err_gbe_bin |= -close_warn(&f->gbe_fd, f->fname);
errno = saved_errno;
@@ -273,11 +266,7 @@ write_to_gbe_bin(void)
/* removed by rename
*/
if (f->tname != NULL)
free(f->tname);
f->tname = NULL;
free_if_null(&f->tname);
}
}
@@ -292,15 +281,15 @@ write_to_gbe_bin(void)
}
void
check_written_part(unsigned long p)
check_written_part(size_t p)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct commands *cmd = &x->cmd[x->i];
struct xfile *f = &x->f;
long rval;
ssize_t rval;
unsigned long gbe_rw_size;
size_t gbe_rw_size;
off_t file_offset;
unsigned char *mem_offset;
@@ -318,17 +307,17 @@ check_written_part(unsigned long p)
memset(f->pad, 0xff, sizeof(f->pad));
if (same_file(f->tmp_fd, &f->tmp_st, 0) < 0)
err(errno, "%s: file inode/device changed", f->tname);
b0rk(errno, "%s: file inode/device changed", f->tname);
if (same_file(f->gbe_fd, &f->gbe_st, 1) < 0)
err(errno, "%s: file changed during write", f->fname);
b0rk(errno, "%s: file changed during write", f->fname);
rval = rw_gbe_file_exact(f->tmp_fd, f->pad,
gbe_rw_size, file_offset, IO_PREAD);
if (rval == -1)
f->rw_check_err_read[p] = f->io_err_gbe = 1;
else if ((unsigned long)rval != gbe_rw_size)
else if ((size_t)rval != gbe_rw_size)
f->rw_check_partial_read[p] = f->io_err_gbe = 1;
else if (memcmp(mem_offset, f->pad, gbe_rw_size) != 0)
f->rw_check_bad_part[p] = f->io_err_gbe = 1;
@@ -359,10 +348,10 @@ check_written_part(unsigned long p)
void
report_io_err_rw(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct xfile *f = &x->f;
unsigned long p;
size_t p;
if (!f->io_err_gbe)
return;
@@ -374,22 +363,22 @@ report_io_err_rw(void)
if (f->rw_check_err_read[p])
fprintf(stderr,
"%s: pread: p%lu (post-verification)\n",
f->fname, (unsigned long)p);
f->fname, (size_t)p);
if (f->rw_check_partial_read[p])
fprintf(stderr,
"%s: partial pread: p%lu (post-verification)\n",
f->fname, (unsigned long)p);
f->fname, (size_t)p);
if (f->rw_check_bad_part[p])
fprintf(stderr,
"%s: pwrite: corrupt write on p%lu\n",
f->fname, (unsigned long)p);
f->fname, (size_t)p);
if (f->rw_check_err_read[p] ||
f->rw_check_partial_read[p]) {
fprintf(stderr,
"%s: p%lu: skipped checksum verification "
"(because read failed)\n",
f->fname, (unsigned long)p);
f->fname, (size_t)p);
continue;
}
@@ -402,7 +391,7 @@ report_io_err_rw(void)
fprintf(stderr, "BAD");
fprintf(stderr, " checksum in p%lu on-disk.\n",
(unsigned long)p);
(size_t)p);
if (f->post_rw_checksum[p]) {
fprintf(stderr,
@@ -415,7 +404,7 @@ report_io_err_rw(void)
int
gbe_mv(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct xfile *f = &x->f;
int rval;
@@ -424,7 +413,15 @@ gbe_mv(void)
int tmp_gbe_bin_exists;
char *dest_tmp;
int dest_fd;
int dest_fd = -1;
char *dir = NULL;
char *base = NULL;
char *dest_name = NULL;
int dirfd = -1;
struct stat st_dir;
/* will be set 0 if it doesn't
*/
@@ -435,91 +432,36 @@ gbe_mv(void)
saved_errno = errno;
rval = rename(f->tname, f->fname);
if (rval > -1) {
/* rename on same filesystem
*/
rval = fs_rename_at(f->dirfd, f->tmpbase,
f->dirfd, f->base);
if (rval > -1)
tmp_gbe_bin_exists = 0;
if (fsync_dir(f->fname) < 0) {
f->io_err_gbe_bin = 1;
rval = -1;
}
goto ret_gbe_mv;
}
if (errno != EXDEV)
goto ret_gbe_mv;
/*
* OR, cross-filesystem rename:
*/
if ((rval = f->tmp_fd = open(f->tname,
O_RDONLY | O_BINARY)) == -1)
goto ret_gbe_mv;
/* create replacement temp in target directory
*/
dest_tmp = new_tmpfile(&dest_fd, 1, f->fname);
if (dest_tmp == NULL)
goto ret_gbe_mv;
/* copy data
*/
rval = rw_file_exact(f->tmp_fd, f->bufcmp,
f->gbe_file_size, 0, IO_PREAD,
NO_LOOP_EAGAIN, LOOP_EINTR,
MAX_ZERO_RW_RETRY, OFF_ERR);
if (rval < 0)
goto ret_gbe_mv;
rval = rw_file_exact(dest_fd, f->bufcmp,
f->gbe_file_size, 0, IO_PWRITE,
NO_LOOP_EAGAIN, LOOP_EINTR,
MAX_ZERO_RW_RETRY, OFF_ERR);
if (rval < 0)
goto ret_gbe_mv;
if (fsync_on_eintr(dest_fd) == -1)
goto ret_gbe_mv;
if (close_on_eintr(dest_fd) == -1)
goto ret_gbe_mv;
if (rename(dest_tmp, f->fname) == -1)
goto ret_gbe_mv;
if (fsync_dir(f->fname) < 0) {
f->io_err_gbe_bin = 1;
goto ret_gbe_mv;
}
free(dest_tmp);
dest_tmp = NULL;
ret_gbe_mv:
/* TODO: this whole section is bloat.
it can be generalised
*/
if (f->gbe_fd > -1) {
if (close_on_eintr(f->gbe_fd) < 0)
if (close_on_eintr(f->gbe_fd) < 0) {
f->gbe_fd = -1;
rval = -1;
}
f->gbe_fd = -1;
if (fsync_dir(f->fname) < 0) {
f->io_err_gbe_bin = 1;
rval = -1;
}
f->gbe_fd = -1;
}
if (f->tmp_fd > -1) {
if (close_on_eintr(f->tmp_fd) < 0)
if (close_on_eintr(f->tmp_fd) < 0) {
f->tmp_fd = -1;
rval = -1;
}
f->tmp_fd = -1;
}
@@ -552,9 +494,9 @@ ret_gbe_mv:
* and it is *also* used during file I/O.
*/
unsigned char *
gbe_mem_offset(unsigned long p, const char *f_op)
gbe_mem_offset(size_t p, const char *f_op)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct xfile *f = &x->f;
off_t gbe_off;
@@ -563,7 +505,7 @@ gbe_mem_offset(unsigned long p, const char *f_op)
GBE_PART_SIZE, GBE_WORK_SIZE);
return (unsigned char *)
(f->buf + (unsigned long)gbe_off);
(f->buf + (size_t)gbe_off);
}
/* I/O operations filtered here. These operations must
@@ -571,9 +513,9 @@ gbe_mem_offset(unsigned long p, const char *f_op)
* within the GbE file, and write 4KB of data.
*/
off_t
gbe_file_offset(unsigned long p, const char *f_op)
gbe_file_offset(size_t p, const char *f_op)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct xfile *f = &x->f;
off_t gbe_file_half_size;
@@ -585,10 +527,10 @@ gbe_file_offset(unsigned long p, const char *f_op)
}
off_t
gbe_x_offset(unsigned long p, const char *f_op, const char *d_type,
gbe_x_offset(size_t p, const char *f_op, const char *d_type,
off_t nsize, off_t ncmp)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct xfile *f = &x->f;
off_t off;
@@ -598,24 +540,24 @@ gbe_x_offset(unsigned long p, const char *f_op, const char *d_type,
off = ((off_t)p) * (off_t)nsize;
if (off > ncmp - GBE_PART_SIZE)
err(ECANCELED, "%s: GbE %s %s out of bounds",
b0rk(ECANCELED, "%s: GbE %s %s out of bounds",
f->fname, d_type, f_op);
if (off != 0 && off != ncmp >> 1)
err(ECANCELED, "%s: GbE %s %s at bad offset",
b0rk(ECANCELED, "%s: GbE %s %s at bad offset",
f->fname, d_type, f_op);
return off;
}
long
rw_gbe_file_exact(int fd, unsigned char *mem, unsigned long nrw,
ssize_t
rw_gbe_file_exact(int fd, unsigned char *mem, size_t nrw,
off_t off, int rw_type)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct xfile *f = &x->f;
long r;
ssize_t r;
if (io_args(fd, mem, nrw, off, rw_type) == -1)
return -1;
@@ -624,17 +566,17 @@ rw_gbe_file_exact(int fd, unsigned char *mem, unsigned long nrw,
if (mem < f->buf)
goto err_rw_gbe_file_exact;
if ((unsigned long)(mem - f->buf) >= GBE_WORK_SIZE)
if ((size_t)(mem - f->buf) >= GBE_WORK_SIZE)
goto err_rw_gbe_file_exact;
}
if (off < 0 || off >= f->gbe_file_size)
goto err_rw_gbe_file_exact;
if (nrw > (unsigned long)(f->gbe_file_size - off))
if (nrw > (size_t)(f->gbe_file_size - off))
goto err_rw_gbe_file_exact;
if (nrw > (unsigned long)GBE_PART_SIZE)
if (nrw > (size_t)GBE_PART_SIZE)
goto err_rw_gbe_file_exact;
r = rw_file_exact(fd, mem, nrw, off, rw_type,

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,119 @@
/* SPDX-License-Identifier: MIT
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
*
* Numerical functions.
* NOTE: randomness was moved to rand.c
*/
/*
TODO: properly handle errno in this file
*/
#ifdef __OpenBSD__
#include <sys/param.h>
#endif
#include <sys/types.h>
#include <errno.h>
#if !((defined(__OpenBSD__) && (OpenBSD) >= 201) || \
defined(__FreeBSD__) || \
defined(__NetBSD__) || defined(__APPLE__))
#include <fcntl.h> /* if not arc4random: /dev/urandom */
#endif
#include <limits.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include "../include/common.h"
/* TODO:
* make this and errno handling more
* flexible
in particular:
hextonum could be modified to
write into a buffer instead,
with the converted numbers,
of an arbitrary length
*/
unsigned short
hextonum(char ch_s)
{
int saved_errno = errno;
/* rlong() can return error,
but preserves errno if no
error. we need to detect
this because it handles
/dev/urandom sometimes
therefore, if it's zero
at start, we know if there
was an err at the end, by
return value zero, if errno
was set; this is technically
valid, since zero is also
a valid random number!
it's an edge case that i had
to fix. i'll rewrite the code
better later. for now, it
should be ok.
*/
errno = 0;
unsigned char ch;
size_t rval;
ch = (unsigned char)ch_s;
if ((unsigned int)(ch - '0') <= 9) {
rval = ch - '0';
goto hextonum_success;
}
ch |= 0x20;
if ((unsigned int)(ch - 'a') <= 5) {
rval = ch - 'a' + 10;
goto hextonum_success;
}
if (ch == '?' || ch == 'x') {
rval = rlong();
if (errno > 0)
goto err_hextonum;
goto hextonum_success;
}
goto err_hextonum;
hextonum_success:
errno = saved_errno;
return (unsigned short)rval & 0xf;
err_hextonum:
if (errno == saved_errno)
errno = EINVAL;
else
return 17; /* 17 indicates getrandom/urandom fail */
return 16; /* invalid character */
/* caller just checks >15. */
}
void
check_bin(size_t a, const char *a_name)
{
if (a > 1)
err_no_cleanup(0, EINVAL, "%s must be 0 or 1, but is %lu",
a_name, (size_t)a);
}

View File

@@ -0,0 +1,114 @@
/* SPDX-License-Identifier: MIT
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
*
* Random number generation
*/
#ifndef RAND_H
#define RAND_H
#ifdef __OpenBSD__
#include <sys/param.h>
#endif
#include <sys/types.h>
#include <errno.h>
#if !((defined(__OpenBSD__) && (OpenBSD) >= 201) || \
defined(__FreeBSD__) || \
defined(__NetBSD__) || defined(__APPLE__))
#include <fcntl.h> /* if not arc4random: /dev/urandom */
#endif
#include <limits.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "../include/common.h"
/* Random numbers
*/
/* when calling this: save errno
* first, then set errno to zero.
* on error, this function will
* set errno and possibly return
*
* rlong also preserves errno
* and leaves it unchanged on
* success, so if you do it
* right, you can detect error.
* this is because it uses
* /dev/urandom which can err.
* ditto getrandom (EINTR),
* theoretically.
*/
/* for the linux version: we use only the
* syscall, because we cannot trust /dev/urandom
* to be as robust, and some libc implementations
* may default to /dev/urandom under fault conditions.
*
* for general high reliability, we must abort on
* failure. in practise, it will likely never fail.
* the arc4random call on bsd never returns error.
*/
size_t
rlong(void)
{
size_t rval;
int saved_errno = errno;
errno = 0;
#if (defined(__OpenBSD__) || defined(__FreeBSD__) || \
defined(__NetBSD__) || defined(__APPLE__) || \
defined(__DragonFly__))
arc4random_buf(&rval, sizeof(size_t));
goto out;
#elif defined(__linux__)
size_t off = 0;
size_t len = sizeof(rval);
ssize_t rc;
retry_rand:
rc = (ssize_t)syscall(SYS_getrandom,
(char *)&rval + off, len - off, 0);
if (rc < 0) {
if (errno == EINTR || errno == EAGAIN) {
usleep(100);
goto retry_rand;
}
goto err; /* possibly unsupported by kernel */
}
if ((off += (size_t)rc) < len)
goto retry_rand;
goto out;
err:
/*
* getrandom can return with error, butt arc4random
* doesn't. generally, getrandom will be reliably,
* but we of course have to maintain parity with
* BSD. So a rand failure is to be interpreted as
* a major systems failure, and we act accordingly.
*/
err_no_cleanup(1, ECANCELED,
"Randomisation failure, possibly unsupported in your kernel.");
exit(EXIT_FAILURE);
#else
#error Unsupported operating system (possibly unsecure randomisation)
#endif
out:
errno = saved_errno;
return rval;
}
#endif

View File

@@ -23,11 +23,19 @@
#include "../include/common.h"
struct xstate *
xstatus(int argc, char *argv[])
xstart(int argc, char *argv[])
{
static int first_run = 1;
static char *dir = NULL;
static char *base = NULL;
char *realdir = NULL;
char *tmpdir = NULL;
char *tmpbase_local = NULL;
static struct xstate us = {
/* DO NOT MESS THIS UP, OR THERE WILL BE DEMONS */
{
/* be careful when modifying xstate. you
* must set everything precisely */
{
CMD_DUMP, "dump", cmd_helper_dump, ARGC_3,
ARG_NOPART,
@@ -86,92 +94,60 @@ xstatus(int argc, char *argv[])
};
static int first_run = 1;
if (!first_run)
return &us;
if (argc < 3)
err_no_cleanup(0, EINVAL, "xstart: Too few arguments");
if (argv == NULL)
err_no_cleanup(0, EINVAL, "xstart: NULL argv");
first_run = 0;
us.f.buf = us.f.real_buf;
first_run = 0;
us.argv0 = argv[0];
us.f.fname = argv[1];
if (argc > 1)
us.f.fname = argv[1];
us.f.tmp_fd = -1;
us.f.tname = NULL;
if (argc < 3)
usage();
if ((realdir = realpath(us.f.fname, NULL)) == NULL)
err_no_cleanup(0, errno, "xstart: can't get realpath of %s",
us.f.fname);
/* https://man.openbsd.org/pledge.2
https://man.openbsd.org/unveil.2 */
#if defined(__OpenBSD__) && defined(OpenBSD)
#if (OpenBSD) >= 604
if (pledge("stdio flock rpath wpath cpath unveil", NULL) == -1)
err(errno, "pledge plus unveil");
#elif (OpenBSD) >= 509
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
err(errno, "pledge");
#endif
#endif
if (fs_dirname_basename(realdir, &dir, &base, 0) < 0)
err_no_cleanup(0, errno, "xstart: don't know CWD of %s",
us.f.fname);
#ifndef S_ISREG
err(ECANCELED, "Can't determine file types (S_ISREG undefined)");
#endif
if ((us.f.base = strdup(base)) == NULL)
err_no_cleanup(0, errno, "strdup base");
#ifndef CHAR_BIT
err(ECANCELED, "Unknown char size");
#else
if (CHAR_BIT != 8)
err(EINVAL, "Unsupported char size");
#endif
us.f.dirfd = fs_open(dir,
O_RDONLY | O_DIRECTORY);
if (us.f.dirfd < 0)
err_no_cleanup(0, errno, "%s: open dir", dir);
if (new_tmpfile(&us.f.tmp_fd, &us.f.tname, dir, ".gbe.XXXXXXXXXX") < 0)
err_no_cleanup(0, errno, "%s", us.f.tname);
if (fs_dirname_basename(us.f.tname,
&tmpdir, &tmpbase_local, 0) < 0)
err_no_cleanup(0, errno, "tmp basename");
us.f.tmpbase = strdup(tmpbase_local);
if (us.f.tmpbase == NULL)
err_no_cleanup(0, errno, "strdup tmpbase");
free_if_null(&tmpdir);
#if defined(__OpenBSD__) && defined(OpenBSD) && \
(OpenBSD) >= 604
/* can only use local tmp on openbsd, due to unveil */
us.f.tname = new_tmpfile(&us.f.tmp_fd, 1, NULL);
#else
us.f.tname = new_tmpfile(&us.f.tmp_fd, 0, NULL);
#endif
if (us.f.tname == NULL)
err(errno, "Can't create tmpfile");
err_no_cleanup(0, errno, "x->f.tname null");
if (*us.f.tname == '\0')
err(errno, "tmp dir is an empty string");
err_no_cleanup(0, errno, "x->f.tname empty");
#if defined(__OpenBSD__) && defined(OpenBSD) && \
OpenBSD >= 604
if (unveil(us.f.tname, "rwc") == -1)
err(errno, "unveil rwc: %s", us.f.tname);
#endif
if (fstat(us.f.tmp_fd, &us.f.tmp_st) < 0)
err(errno, "%s: stat", us.f.tname);
sanitize_command_list();
/* parse user command */
set_cmd(argc, argv);
set_cmd_args(argc, argv);
#if defined(__OpenBSD__) && defined(OpenBSD) && \
(OpenBSD) >= 604
if ((us.cmd[i].flags & O_ACCMODE) == O_RDONLY) {
if (unveil(us.f.fname, "r") == -1)
err(errno, "%s: unveil r", us.f.fname);
} else {
if (unveil(us.f.fname, "rwc") == -1)
err(errno, "%s: unveil rw", us.f.fname);
}
if (unveil(us.f.tname, "rwc") == -1)
err(errno, "%s: unveil rwc", us.f.tname);
if (unveil(NULL, NULL) == -1)
err(errno, "unveil block (rw)");
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
err(errno, "pledge (kill unveil)");
#endif
open_gbe_file();
err_no_cleanup(0, errno, "%s: stat", us.f.tname);
memset(us.f.real_buf, 0, sizeof(us.f.real_buf));
memset(us.f.bufcmp, 0, sizeof(us.f.bufcmp));
@@ -179,16 +155,24 @@ xstatus(int argc, char *argv[])
/* for good measure */
memset(us.f.pad, 0, sizeof(us.f.pad));
copy_gbe();
read_checksums();
return &us;
}
void
err(int nvm_errval, const char *msg, ...)
struct xstate *
xstatus(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstart(0, NULL);
if (x == NULL)
err_no_cleanup(0, EACCES, "NULL pointer to xstate");
return x;
}
void
b0rk(int nvm_errval, const char *msg, ...)
{
struct xstate *x = xstatus();
va_list args;
@@ -211,35 +195,10 @@ err(int nvm_errval, const char *msg, ...)
exit(EXIT_FAILURE);
}
const char *
getnvmprogname(void)
{
struct xstate *x = xstatus(0, NULL);
const char *p;
static char fallback[] = "nvmutil";
char *rval = fallback;
if (x != NULL) {
if (x->argv0 == NULL || *x->argv0 == '\0')
return "";
rval = x->argv0;
}
p = strrchr(rval, '/');
if (p)
return p + 1;
else
return rval;
}
int
exit_cleanup(void)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct xfile *f;
int close_err;
@@ -251,23 +210,17 @@ exit_cleanup(void)
if (x != NULL) {
f = &x->f;
if (f->gbe_fd > -1) {
if (close_on_eintr(f->gbe_fd) == -1)
close_err = 1;
f->gbe_fd = -1;
}
close_no_err(&f->gbe_fd);
close_no_err(&f->tmp_fd);
close_no_err(&f->tmp_fd);
if (f->tmp_fd > -1) {
if (close_on_eintr(f->tmp_fd) == -1)
close_err = 1;
}
if (f->tname != NULL) {
if (f->tname != NULL)
if (unlink(f->tname) == -1)
close_err = 1;
}
f->tmp_fd = -1;
close_no_err(&f->dirfd);
free_if_null(&f->base);
free_if_null(&f->tmpbase);
}
if (saved_errno)

View File

@@ -0,0 +1,284 @@
/* SPDX-License-Identifier: MIT
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
*
* String functions
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <stdint.h>
#include "../include/common.h"
/* strict strcmp */
int
scmp(const char *a,
const char *b,
size_t maxlen,
int *rval)
{
size_t ch;
unsigned char ac;
unsigned char bc;
if (a == NULL ||
b == NULL ||
rval == NULL) {
errno = EFAULT;
goto err;
}
for (ch = 0; ch < maxlen; ch++) {
ac = (unsigned char)a[ch];
bc = (unsigned char)b[ch];
if (ac != bc) {
*rval = ac - bc;
return 0;
}
if (ac == '\0') {
*rval = 0;
return 0;
}
}
err:
errno = EFAULT;
if (rval != NULL)
*rval = -1;
return -1;
}
/* strict strlen */
int
slen(const char *s,
size_t maxlen,
size_t *rval)
{
size_t ch;
if (s == NULL ||
rval == NULL) {
errno = EFAULT;
goto err;
}
for (ch = 0;
ch < maxlen && s[ch] != '\0';
ch++);
if (ch == maxlen) {
/* unterminated */
errno = EFAULT;
goto err;
}
*rval = ch;
return 0;
err:
if (rval != NULL)
*rval = 0;
return -1;
}
/* strict strdup */
int
sdup(const char *s,
size_t n, char **dest)
{
size_t size;
char *rval;
if (dest == NULL ||
slen(s, n, &size) < 0 ||
if_err(size == SIZE_MAX, EOVERFLOW) ||
(rval = malloc(size + 1)) == NULL) {
if (dest != NULL)
*dest = NULL;
return -1;
}
memcpy(rval, s, size);
*(rval + size) = '\0';
*dest = rval;
return 0;
}
/* strict strcat */
int
scat(const char *s1, const char *s2,
size_t n, char **dest)
{
size_t size1;
size_t size2;
char *rval;
if (dest == NULL ||
slen(s1, n, &size1) < 0 ||
slen(s2, n, &size2) < 0 ||
if_err(size1 > SIZE_MAX - size2 - 1, EOVERFLOW) ||
(rval = malloc(size1 + size2 + 1)) == NULL) {
if (dest != NULL)
*dest = NULL;
return -1;
}
memcpy(rval, s1, size1);
memcpy(rval + size1, s2, size2);
*(rval + size1 + size2) = '\0';
*dest = rval;
return 0;
}
/* strict split/de-cat - off is where
2nd buffer will start from */
int
dcat(const char *s, size_t n,
size_t off, char **dest1,
char **dest2)
{
size_t size;
char *rval1 = NULL;
char *rval2 = NULL;
if (dest1 == NULL || dest2 == NULL ||
slen(s, n, &size) < 0 ||
if_err(size == SIZE_MAX, EOVERFLOW) ||
if_err(off >= size, EOVERFLOW) ||
(rval1 = malloc(off + 1)) == NULL ||
(rval2 = malloc(size - off + 1)) == NULL) {
goto err;
}
memcpy(rval1, s, off);
*(rval1 + off) = '\0';
memcpy(rval2, s + off, size - off);
*(rval2 + size - off) = '\0';
*dest1 = rval1;
*dest2 = rval2;
return 0;
err:
if (rval1 != NULL)
free(rval1);
if (rval2 != NULL)
free(rval2);
if (dest1 != NULL)
*dest1 = NULL;
if (dest2 != NULL)
*dest2 = NULL;
return -1;
}
/* the one for nvmutil state is in state.c */
/* this one just exits */
void
err_no_cleanup(int stfu, int nvm_errval, const char *msg, ...)
{
va_list args;
int saved_errno = errno;
const char *p;
#if defined(__OpenBSD__) && defined(OpenBSD)
#if (OpenBSD) >= 509
if (pledge("stdio", NULL) == -1)
fprintf(stderr, "pledge failure during exit");
#endif
#endif
if (!errno)
saved_errno = errno = ECANCELED;
if ((p = getnvmprogname()) != NULL)
fprintf(stderr, "%s: ", p);
va_start(args, msg);
vfprintf(stderr, msg, args);
va_end(args);
if (p != NULL)
fprintf(stderr, ": %s\n", strerror(errno));
else
fprintf(stderr, "%s\n", strerror(errno));
exit(EXIT_FAILURE);
}
const char *
getnvmprogname(void)
{
static char *rval = NULL;
static char *p;
static int setname = 0;
if (!setname) {
if ((rval = lbgetprogname(NULL)) == NULL)
return NULL;
p = strrchr(rval, '/');
if (p)
rval = p + 1;
setname = 1;
}
return rval;
}
/* singleton. if string not null,
sets the string. after set,
will not set anymore. either
way, returns the string
*/
char *
lbgetprogname(char *argv0)
{
static int setname = 0;
static char *progname = NULL;
size_t len;
if (!setname) {
if (if_err(argv0 == NULL || *argv0 == '\0', EFAULT) ||
slen(argv0, 4096, &len) < 0 ||
(progname = malloc(len + 1)) == NULL)
return NULL;
memcpy(progname, argv0, len + 1);
setname = 1;
}
return progname;
}

View File

@@ -26,5 +26,5 @@ usage(void)
util, util, util, util,
util, util, util);
err(EINVAL, "Too few arguments");
b0rk(EINVAL, "Too few arguments");
}

View File

@@ -13,12 +13,12 @@
#include "../include/common.h"
unsigned short
nvm_word(unsigned long pos16, unsigned long p)
nvm_word(size_t pos16, size_t p)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct xfile *f = &x->f;
unsigned long pos;
size_t pos;
check_nvm_bound(pos16, p);
pos = (pos16 << 1) + (p * GBE_PART_SIZE);
@@ -28,12 +28,12 @@ nvm_word(unsigned long pos16, unsigned long p)
}
void
set_nvm_word(unsigned long pos16, unsigned long p, unsigned short val16)
set_nvm_word(size_t pos16, size_t p, unsigned short val16)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct xfile *f = &x->f;
unsigned long pos;
size_t pos;
check_nvm_bound(pos16, p);
pos = (pos16 << 1) + (p * GBE_PART_SIZE);
@@ -45,9 +45,9 @@ set_nvm_word(unsigned long pos16, unsigned long p, unsigned short val16)
}
void
set_part_modified(unsigned long p)
set_part_modified(size_t p)
{
struct xstate *x = xstatus(0, NULL);
struct xstate *x = xstatus();
struct xfile *f = &x->f;
check_bin(p, "part number");
@@ -55,7 +55,7 @@ set_part_modified(unsigned long p)
}
void
check_nvm_bound(unsigned long c, unsigned long p)
check_nvm_bound(size_t c, size_t p)
{
/* Block out of bound NVM access
*/
@@ -63,6 +63,6 @@ check_nvm_bound(unsigned long c, unsigned long p)
check_bin(p, "part number");
if (c >= NVM_WORDS)
err(ECANCELED, "check_nvm_bound: out of bounds %lu",
(unsigned long)c);
b0rk(ECANCELED, "check_nvm_bound: out of bounds %lu",
(size_t)c);
}

View File

@@ -0,0 +1,211 @@
/* SPDX-License-Identifier: MIT
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
*
* WORK IN PROGRESS (proof of concept), or, v0.0000001
*
* Mkhtemp - Hardened mktemp. Create files and directories
* randomly as determined by user's TMPDIR, or fallback. It
* attemps to provide mitigation against several TOCTOU-based
* attacks e.g. directory rename / symlink attacks, and it
* generally provides much higher strictness than previous
* implementations such as mktemp, mkstemp or even mkdtemp.
*
* It uses several modern features by default, e.g. openat2
* and O_TMPFILE on Linux, with additional hardening; BSD
* projects only have openat so the code uses that there.
*
* Many programs rely on mktemp, and they use TMPDIR in a way
* that is quite insecure. Mkhtemp intends to change that,
* quite dramatically, with: userspace sandbox (and use OS
* level options e.g. OBSD pledge where available), constant
* identity/ownership checks on files, MUCH stricter ownership
* restrictions (e.g. enforce sticky bit policy on world-
* writeable tmpdirs), preventing operation on other people's
* files (only your own files) - even root is restricted,
* depending on how the code is compiled. Please read the code.
*
* This is the utility version, which makes use of the also-
* included library. No docs yet - source code are the docs,
* and the (ever evolving, and hardening) specification.
*
* This was written from scratch, for use in nvmutil, and
* it is designed to be portable (BSD, Linux). Patches
* very much welcome.
*
* WARNING: This is MUCH stricter than every other mktemp
* implementation, even more so than mkdtemp or
* the OpenBSD version of mkstemp. It *will* break,
* or more specifically, reveal the flaws in, almost
* every major critical infrastructure, because most
* people already use mktemp extremely insecurely.
*
* This tool is written by me, for me, and also Libreboot, but
* it will be summitted for review to various Linux distros
* and BSD projects once it has reached maturity.
*/
#if defined(__linux__) && !defined(_GNU_SOURCE)
/* for openat2 on linux */
#define _GNU_SOURCE 1
#endif
#ifdef __OpenBSD__
#include <sys/param.h> /* pledge(2) */
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "include/common.h"
int
main(int argc, char *argv[])
{
#if defined (PATH_LEN) && \
(PATH_LEN) >= 256
size_t maxlen = PATH_LEN;
#else
size_t maxlen = 4096;
#endif
size_t len;
size_t tlen;
size_t xc = 0;
char *tmpdir = NULL;
char *template = NULL;
char *p;
char *s = NULL;
char *rp;
char resolved[maxlen];
char c;
int fd = -1;
int type = MKHTEMP_FILE;
int stfu = 0; /* -q option */
if (lbgetprogname(argv[0]) == NULL)
err_no_cleanup(stfu, errno, "could not set progname");
/* https://man.openbsd.org/pledge.2 */
#if defined(__OpenBSD__) && defined(OpenBSD)
#if (OpenBSD) >= 509
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
goto err_usage;
#endif
#endif
while ((c =
getopt(argc, argv, "qdp:")) != -1) {
switch (c) {
case 'd':
type = MKHTEMP_DIR;
break;
case 'p':
tmpdir = optarg;
break;
case 'q': /* don't print errors */
/* (exit status unchanged) */
stfu = 1;
break;
default:
goto err_usage;
}
}
if (optind < argc)
template = argv[optind];
if (optind + 1 < argc)
goto err_usage;
/* custom template e.g. foo.XXXXXXXXXXXXXXXXXXXXX */
if (template != NULL) {
if (slen(template, maxlen, &tlen) < 0)
err_no_cleanup(stfu, EINVAL,
"invalid template");
for (p = template + tlen;
p > template && *--p == 'X'; xc++);
if (xc < 3) /* the gnu mktemp errs on less than 3 */
err_no_cleanup(stfu, EINVAL,
"template must have 3 X or more on end (12+ advised");
}
/* user supplied -p PATH - WARNING:
* this permits symlinks, but only here,
* not in the library, so they are resolved
* here first, and *only here*. the mkhtemp
* library blocks them. be careful
* when using -p
*/
if (tmpdir != NULL) {
rp = realpath(tmpdir, resolved);
if (rp == NULL)
err_no_cleanup(stfu, errno, "%s", tmpdir);
tmpdir = resolved;
}
if (new_tmp_common(&fd, &s, type,
tmpdir, template) < 0)
err_no_cleanup(stfu, errno, "%s", s);
#if defined(__OpenBSD__) && defined(OpenBSD)
#if (OpenBSD) >= 509
if (pledge("stdio", NULL) == -1)
err_no_cleanup(stfu, errno, "pledge, exit");
#endif
#endif
if (s == NULL)
err_no_cleanup(stfu, EFAULT, "bad string initialisation");
if (*s == '\0')
err_no_cleanup(stfu, EFAULT, "empty string initialisation");
if (slen(s, maxlen, &len) < 0)
err_no_cleanup(stfu, EFAULT, "unterminated string initialisiert");
printf("%s\n", s);
return EXIT_SUCCESS;
err_usage:
err_no_cleanup(stfu, EINVAL,
"usage: %s [-d] [-p dir] [template]\n", getnvmprogname());
}/*
( >:3 )
/| |\
/ \
*/

View File

@@ -0,0 +1,132 @@
/* SPDX-License-Identifier: MIT
* Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
*
* This tool lets you modify Intel GbE NVM (Gigabit Ethernet
* Non-Volatile Memory) images, e.g. change the MAC address.
* These images configure your Intel Gigabit Ethernet adapter.
*/
#ifdef __OpenBSD__
/* for pledge/unveil test:
*/
#include <sys/param.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "include/common.h"
int
main(int argc, char *argv[])
{
struct xstate *x;
struct commands *cmd;
struct xfile *f;
size_t c;
if (lbgetprogname(argv[0]) == NULL)
err_no_cleanup(0, errno, "could not set progname");
/* https://man.openbsd.org/pledge.2
https://man.openbsd.org/unveil.2 */
#if defined(__OpenBSD__) && defined(OpenBSD)
#if (OpenBSD) >= 604
if (pledge("stdio flock rpath wpath cpath unveil", NULL) == -1)
err_no_cleanup(0, errno, "pledge plus unveil, main");
if (unveil("/dev/null", "r") == -1)
err_no_cleanup(0, errno, "unveil r: /dev/null");
#elif (OpenBSD) >= 509
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
err_no_cleanup(0, errno, "pledge, main");
#endif
#endif
#ifndef S_ISREG
err_no_cleanup(0, ECANCELED,
"Can't determine file types (S_ISREG undefined)");
#endif
#if ((CHAR_BIT) != 8)
err_no_cleanup(0, ECANCELED, "Unsupported char size");
#endif
x = xstart(argc, argv);
if (x == NULL)
err_no_cleanup(0, ECANCELED, "NULL state on init");
/* parse user command */
/* TODO: CHECK ACCESSES VIA xstatus() */
set_cmd(argc, argv);
set_cmd_args(argc, argv);
cmd = &x->cmd[x->i];
f = &x->f;
/* https://man.openbsd.org/pledge.2
https://man.openbsd.org/unveil.2 */
#if defined(__OpenBSD__) && defined(OpenBSD)
#if (OpenBSD) >= 604
if ((us.cmd[i].flags & O_ACCMODE) == O_RDONLY) {
if (unveil(us.f.fname, "r") == -1)
b0rk(errno, "%s: unveil r", us.f.fname);
} else {
if (unveil(us.f.fname, "rwc") == -1)
b0rk(errno, "%s: unveil rw", us.f.fname);
}
if (unveil(us.f.tname, "rwc") == -1)
b0rk(errno, "unveil rwc: %s", us.f.tname);
if (unveil(NULL, NULL) == -1)
b0rk(errno, "unveil block (rw)");
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
b0rk(errno, "pledge (kill unveil)");
#elif (OpenBSD) >= 509
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
b0rk(errno, "pledge");
#endif
#endif
if (cmd->run == NULL)
b0rk(errno, "Command not set");
sanitize_command_list();
open_gbe_file();
copy_gbe();
read_checksums();
cmd->run();
for (c = 0; c < items(x->cmd); c++)
x->cmd[c].run = cmd_helper_err;
if ((cmd->flags & O_ACCMODE) == O_RDWR)
write_to_gbe_bin();
if (exit_cleanup() == -1)
b0rk(EIO, "%s: close", f->fname);
if (f->io_err_gbe_bin)
b0rk(EIO, "%s: error writing final file");
free_if_null(&f->tname);
return EXIT_SUCCESS;
}

View File

@@ -1,890 +0,0 @@
/* SPDX-License-Identifier: MIT
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "../include/common.h"
/* check that a file changed
*/
int
same_file(int fd, struct stat *st_old,
int check_size)
{
struct stat st;
int saved_errno = errno;
if (st_old == NULL || fd < 0)
goto err_same_file;
if (fstat(fd, &st) == -1)
return -1;
if (st.st_dev != st_old->st_dev ||
st.st_ino != st_old->st_ino ||
!S_ISREG(st.st_mode))
goto err_same_file;
if (check_size &&
st.st_size != st_old->st_size)
goto err_same_file;
errno = saved_errno;
return 0;
err_same_file:
errno = EIO;
return -1;
}
/* open() but with abort traps
*/
void
xopen(int *fd_ptr, const char *path, int flags, struct stat *st)
{
if ((*fd_ptr = open(path, flags)) == -1)
err(errno, "%s", path);
if (fstat(*fd_ptr, st) == -1)
err(errno, "%s: stat", path);
if (!S_ISREG(st->st_mode))
err(errno, "%s: not a regular file", path);
if (lseek(*fd_ptr, 0, SEEK_CUR) == (off_t)-1)
err(errno, "%s: file not seekable", path);
}
/* fsync() the directory of a file,
* useful for atomic writes
*/
int
fsync_dir(const char *path)
{
int saved_errno = errno;
unsigned long pathlen;
unsigned long maxlen;
char *dirbuf;
int dirfd;
char *slash;
struct stat st;
#if defined(PATH_LEN) && \
(PATH_LEN) >= 256
maxlen = PATH_LEN;
#else
maxlen = 1024;
#endif
dirbuf = NULL;
dirfd = -1;
pathlen = xstrxlen(path, maxlen);
if (pathlen >= maxlen) {
fprintf(stderr, "Path too long for fsync_parent_dir\n");
goto err_fsync_dir;
}
if (pathlen == 0)
{
errno = EINVAL;
goto err_fsync_dir;
}
dirbuf = malloc(pathlen + 1);
if (dirbuf == NULL)
goto err_fsync_dir;
memcpy(dirbuf, path, pathlen + 1);
slash = strrchr(dirbuf, '/');
if (slash != NULL) {
*slash = '\0';
if (*dirbuf == '\0') {
dirbuf[0] = '/';
dirbuf[1] = '\0';
}
} else {
dirbuf[0] = '.';
dirbuf[1] = '\0';
}
dirfd = open(dirbuf, O_RDONLY
#ifdef O_DIRECTORY
| O_DIRECTORY
#endif
#ifdef O_NOFOLLOW
| O_NOFOLLOW
#endif
);
if (dirfd == -1)
goto err_fsync_dir;
if (fstat(dirfd, &st) < 0)
goto err_fsync_dir;
if (!S_ISDIR(st.st_mode)) {
fprintf(stderr, "%s: not a directory\n", dirbuf);
goto err_fsync_dir;
}
/* sync file on disk */
if (fsync_on_eintr(dirfd) == -1)
goto err_fsync_dir;
if (close_on_eintr(dirfd) == -1)
goto err_fsync_dir;
if (dirbuf != NULL)
free(dirbuf);
errno = saved_errno;
return 0;
err_fsync_dir:
if (!errno)
errno = EIO;
if (errno != saved_errno)
fprintf(stderr, "%s: %s\n", path, strerror(errno));
if (dirbuf != NULL)
free(dirbuf);
if (dirfd > -1)
close_on_eintr(dirfd);
errno = saved_errno;
return -1;
}
/* returns ptr to path (string). if local>0:
* make tmpfile in the same directory as the
* file. if local==0, use TMPDIR
*
* if local==0, the 3rd argument is ignored
*/
char *
new_tmpfile(int *fd, int local, const char *path)
{
unsigned long maxlen;
struct stat st;
/* please do not modify the
* strings or I will get mad
*/
char tmp_none[] = "";
char tmp_default[] = "/tmp";
char default_tmpname[] = "tmpXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
char *tmpname;
char *base = NULL;
char *dest = NULL;
unsigned long tmpdir_len = 0;
unsigned long tmpname_len = 0;
unsigned long tmppath_len = 0;
int fd_tmp = -1;
int flags;
#if defined(PATH_LEN) && \
(PATH_LEN) >= 256
maxlen = PATH_LEN;
#else
maxlen = 1024;
#endif
tmpname = default_tmpname;
if (local) {
if (path == NULL)
goto err_new_tmpfile;
if (*path == '\0')
goto err_new_tmpfile;
if (stat(path, &st) == -1)
goto err_new_tmpfile;
if (!S_ISREG(st.st_mode))
goto err_new_tmpfile;
tmpname = (char *)path;
}
if (local) {
base = tmp_none;
/* appended to filename for tmp:
*/
tmpdir_len = xstrxlen(default_tmpname, maxlen);
} else {
base = get_tmpdir();
if (base == NULL)
base = tmp_default;
if (*base == '\0')
base = tmp_default;
tmpdir_len = xstrxlen(base, maxlen);
}
tmpname_len = xstrxlen(tmpname, maxlen);
tmppath_len = tmpdir_len + tmpname_len;
++tmppath_len; /* for '/' or '.' */
/* max length -1 of maxlen
* for termination
*/
if (tmpdir_len > maxlen - tmpname_len - 1)
goto err_new_tmpfile;
/* +1 for NULL */
dest = malloc(tmppath_len + 1);
if (dest == NULL)
goto err_new_tmpfile;
if (local) {
*dest = '.'; /* hidden file */
memcpy(dest + (unsigned long)1, tmpname, tmpname_len);
memcpy(dest + (unsigned long)1 + tmpname_len,
default_tmpname, tmpdir_len);
} else {
memcpy(dest, base, tmpdir_len);
dest[tmpdir_len] = '/';
memcpy(dest + tmpdir_len + 1, tmpname, tmpname_len);
}
dest[tmppath_len] = '\0';
fd_tmp = mkstemp_n(dest);
if (fd_tmp == -1)
goto err_new_tmpfile;
if (fchmod(fd_tmp, 0600) == -1)
goto err_new_tmpfile;
flags = fcntl(fd_tmp, F_GETFL);
if (flags == -1)
goto err_new_tmpfile;
/*
* O_APPEND would permit offsets
* to be ignored, which breaks
* positional read/write
*/
if (flags & O_APPEND)
goto err_new_tmpfile;
if (lock_file(fd_tmp, flags) == -1)
goto err_new_tmpfile;
if (fstat(fd_tmp, &st) == -1)
goto err_new_tmpfile;
/*
* Extremely defensive
* likely pointless checks
*/
/* check if it's a file */
if (!S_ISREG(st.st_mode))
goto err_new_tmpfile;
/* check if it's seekable */
if (lseek(fd_tmp, 0, SEEK_CUR) == (off_t)-1)
goto err_new_tmpfile;
/* tmpfile has >1 hardlinks */
if (st.st_nlink > 1)
goto err_new_tmpfile;
/* tmpfile unlinked while opened */
if (st.st_nlink == 0)
goto err_new_tmpfile;
*fd = fd_tmp;
return dest;
err_new_tmpfile:
if (dest != NULL)
free(dest);
if (fd_tmp > -1)
close_on_eintr(fd_tmp);
return NULL;
}
int
lock_file(int fd, int flags)
{
struct flock fl;
memset(&fl, 0, sizeof(fl));
if ((flags & O_ACCMODE) == O_RDONLY)
fl.l_type = F_RDLCK;
else
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
if (fcntl(fd, F_SETLK, &fl) == -1)
return -1;
return 0;
}
/* return TMPDIR, or fall back
* to portable defaults
*/
char *
get_tmpdir(void)
{
char *t;
struct stat st;
t = getenv("TMPDIR");
if (t && *t) {
if (stat(t, &st) == 0 && S_ISDIR(st.st_mode)) {
if ((st.st_mode & S_IWOTH) && !(st.st_mode & S_ISVTX))
return NULL;
return t;
}
}
if (stat("/tmp", &st) == 0 && S_ISDIR(st.st_mode))
return "/tmp";
if (stat("/var/tmp", &st) == 0 && S_ISDIR(st.st_mode))
return "/var/tmp";
return ".";
}
/* portable mkstemp
*/
int
mkstemp_n(char *template)
{
int fd;
unsigned long i, j;
unsigned long len;
char *p;
unsigned long xc = 0;
static char ch[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
unsigned long r;
#if defined(PATH_LEN) && \
(PATH_LEN) >= 256
unsigned long max_len = PATH_LEN;
#else
unsigned long max_len = 4096;
#endif
len = xstrxlen(template, max_len);
if (len < 6) {
errno = EINVAL;
return -1;
}
p = template + len;
while (p > template && p[-1] == 'X') {
--p;
++xc;
}
if (xc < 6) {
errno = EINVAL;
return -1;
}
for (i = 0; i < 200; i++) {
for (j = 0; j < xc; j++) {
r = rlong();
p[j] = ch[(unsigned long)(r >> 1) % (sizeof(ch) - 1)];
}
fd = open(template,
O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC, 0600);
if (fd >= 0)
return fd;
if (errno != EEXIST)
return -1;
}
errno = EEXIST;
return -1;
}
/*
* Safe I/O functions wrapping around
* read(), write() and providing a portable
* analog of both pread() and pwrite().
* These functions are designed for maximum
* robustness, checking NULL inputs, overflowed
* outputs, and all kinds of errors that the
* standard libc functions don't.
*
* Looping on EINTR and EAGAIN is supported.
* EINTR/EAGAIN looping is done indefinitely.
*/
/* rw_file_exact() - Read perfectly or die
*
* Read/write, and absolutely insist on an
* absolute read; e.g. if 100 bytes are
* requested, this MUST return 100.
*
* This function will never return zero.
* It will only return below (error),
* or above (success). On error, -1 is
* returned and errno is set accordingly.
*
* Zero-byte returns are not allowed.
* It will re-spin a finite number of
* times upon zero-return, to recover,
* otherwise it will return an error.
*/
long
rw_file_exact(int fd, unsigned char *mem, unsigned long nrw,
off_t off, int rw_type, int loop_eagain,
int loop_eintr, unsigned long max_retries,
int off_reset)
{
long rval;
long rc;
unsigned long nrw_cur;
off_t off_cur;
void *mem_cur;
unsigned long retries_on_zero;
rval = 0;
rc = 0;
retries_on_zero = 0;
if (io_args(fd, mem, nrw, off, rw_type) == -1)
return -1;
while (1) {
/* Prevent theoretical overflow */
if (rval >= 0 && (unsigned long)rval > (nrw - rc))
goto err_rw_file_exact;
rc += rval;
if ((unsigned long)rc >= nrw)
break;
mem_cur = (void *)(mem + (unsigned long)rc);
nrw_cur = (unsigned long)(nrw - (unsigned long)rc);
if (off < 0)
goto err_rw_file_exact;
off_cur = off + (off_t)rc;
rval = prw(fd, mem_cur, nrw_cur, off_cur,
rw_type, loop_eagain, loop_eintr,
off_reset);
if (rval < 0)
return -1;
if (rval == 0) {
if (retries_on_zero++ < max_retries)
continue;
goto err_rw_file_exact;
}
retries_on_zero = 0;
}
if ((unsigned long)rc != nrw)
goto err_rw_file_exact;
return rw_over_nrw(rc, nrw);
err_rw_file_exact:
errno = EIO;
return -1;
}
/* prw() - portable read-write with more
* safety checks than barebones libc
*
* portable pwrite/pread on request, or real
* pwrite/pread libc functions can be used.
* the portable (non-libc) pread/pwrite is not
* thread-safe, because it does not prevent or
* mitigate race conditions on file descriptors
*
* If you need real pwrite/pread, just compile
* with flag: HAVE_REAL_PREAD_PWRITE=1
*
* A fallback is provided for regular read/write.
* rw_type can be IO_READ (read), IO_WRITE (write),
* IO_PREAD (pread) or IO_PWRITE
*
* loop_eagain does a retry loop on EAGAIN if set
* loop_eintr does a retry loop on EINTR if set
*
* race conditions on non-libc pread/pwrite:
* if a file offset changes, abort, to mitage.
*
* off_reset 1: reset the file offset *once* if
* a change was detected, assuming
* nothing else is touching it now
* off_reset 0: never reset if changed
*/
long
prw(int fd, void *mem, unsigned long nrw,
off_t off, int rw_type,
int loop_eagain, int loop_eintr,
int off_reset)
{
long r;
int positional_rw;
struct stat st;
#if !defined(HAVE_REAL_PREAD_PWRITE) || \
HAVE_REAL_PREAD_PWRITE < 1
int saved_errno;
off_t verified;
off_t off_orig;
off_t off_last;
#endif
if (io_args(fd, mem, nrw, off, rw_type)
== -1) {
return -1;
}
r = -1;
/* do not use loop_eagain on
* normal files
*/
if (!loop_eagain) {
/* check whether the file
* changed
*/
if (check_file(fd, &st) == -1)
return -1;
}
if (rw_type >= IO_PREAD)
positional_rw = 1; /* pread/pwrite */
else
positional_rw = 0; /* read/write */
try_rw_again:
if (!positional_rw) {
#if defined(HAVE_REAL_PREAD_PWRITE) && \
HAVE_REAL_PREAD_PWRITE > 0
real_pread_pwrite:
#endif
if (rw_type == IO_WRITE)
r = write(fd, mem, nrw);
else if (rw_type == IO_READ)
r = read(fd, mem, nrw);
#if defined(HAVE_REAL_PREAD_PWRITE) && \
HAVE_REAL_PREAD_PWRITE > 0
else if (rw_type == IO_PWRITE)
r = pwrite(fd, mem, nrw, off);
else if (rw_type == IO_PREAD)
r = pread(fd, mem, nrw, off);
#endif
if (r == -1 && (errno == try_err(loop_eintr, EINTR)
|| errno == try_err(loop_eagain, EAGAIN)))
goto try_rw_again;
return rw_over_nrw(r, nrw);
}
#if defined(HAVE_REAL_PREAD_PWRITE) && \
HAVE_REAL_PREAD_PWRITE > 0
goto real_pread_pwrite;
#else
if ((off_orig = lseek_on_eintr(fd, (off_t)0, SEEK_CUR,
loop_eagain, loop_eintr)) == (off_t)-1) {
r = -1;
} else if (lseek_on_eintr(fd, off, SEEK_SET,
loop_eagain, loop_eintr) == (off_t)-1) {
r = -1;
} else {
verified = lseek_on_eintr(fd, (off_t)0, SEEK_CUR,
loop_eagain, loop_eintr);
/* abort if the offset changed,
* indicating race condition. if
* off_reset enabled, reset *ONCE*
*/
if (off_reset && off != verified)
lseek_on_eintr(fd, off, SEEK_SET,
loop_eagain, loop_eintr);
do {
/* check offset again, repeatedly.
* even if off_reset is set, this
* aborts if offsets change again
*/
verified = lseek_on_eintr(fd, (off_t)0, SEEK_CUR,
loop_eagain, loop_eintr);
if (off != verified)
goto err_prw;
if (rw_type == IO_PREAD)
r = read(fd, mem, nrw);
else if (rw_type == IO_PWRITE)
r = write(fd, mem, nrw);
if (rw_over_nrw(r, nrw) == -1) {
errno = EIO;
break;
}
} while (r == -1 &&
(errno == try_err(loop_eintr, EINTR) ||
errno == try_err(loop_eagain, EAGAIN)));
}
saved_errno = errno;
off_last = lseek_on_eintr(fd, off_orig, SEEK_SET,
loop_eagain, loop_eintr);
if (off_last != off_orig) {
errno = saved_errno;
goto err_prw;
}
errno = saved_errno;
return rw_over_nrw(r, nrw);
#endif
err_prw:
errno = EIO;
return -1;
}
int
io_args(int fd, void *mem, unsigned long nrw,
off_t off, int rw_type)
{
/* obviously */
if (mem == NULL)
goto err_io_args;
/* uninitialised fd */
if (fd < 0)
goto err_io_args;
/* negative offset */
if (off < 0)
goto err_io_args;
/* prevent zero-byte rw */
if (!nrw)
goto err_io_args;
/* prevent overflow */
if (nrw > (unsigned long)X_LONG_MAX)
goto err_io_args;
/* prevent overflow */
if (((unsigned long)off + nrw) < (unsigned long)off)
goto err_io_args;
if (rw_type > IO_PWRITE)
goto err_io_args;
return 0;
err_io_args:
errno = EIO;
return -1;
}
int
check_file(int fd, struct stat *st)
{
if (fstat(fd, st) == -1)
goto err_is_file;
if (!S_ISREG(st->st_mode))
goto err_is_file;
return 0;
err_is_file:
errno = EIO;
return -1;
}
/* POSIX can say whatever it wants.
* specification != implementation
*/
long
rw_over_nrw(long r, unsigned long nrw)
{
/* not a libc bug, but we
* don't like the number zero
*/
if (!nrw)
goto err_rw_over_nrw;
if (r == -1)
return r;
if ((unsigned long)
r > X_LONG_MAX) {
/* Theoretical buggy libc
* check. Extremely academic.
*
* Specifications never
* allow this return value
* to exceed SSIZE_T, but
* spec != implementation
*
* Check this after using
* [p]read() or [p]write()
*
* NOTE: here, we assume
* long integers are the
* same size as SSIZE_T
*/
goto err_rw_over_nrw;
}
/* Theoretical buggy libc:
* Should never return a number of
* bytes above the requested length.
*/
if ((unsigned long)r > nrw)
goto err_rw_over_nrw;
return r;
err_rw_over_nrw:
errno = EIO;
return -1;
}
#if !defined(HAVE_REAL_PREAD_PWRITE) || \
HAVE_REAL_PREAD_PWRITE < 1
off_t
lseek_on_eintr(int fd, off_t off, int whence,
int loop_eagain, int loop_eintr)
{
off_t old;
old = -1;
do {
old = lseek(fd, off, whence);
} while (old == (off_t)-1 && (
errno == try_err(loop_eintr, EINTR) ||
errno == try_err(loop_eagain, EAGAIN)));
return old;
}
#endif
int
try_err(int loop_err, int errval)
{
if (loop_err)
return errval;
return -1;
}
int
close_on_eintr(int fd)
{
int r;
int saved_errno = errno;
do {
r = close(fd);
} while (r == -1 && errno == EINTR);
if (r > -1)
errno = saved_errno;
return r;
}
int
fsync_on_eintr(int fd)
{
int r;
do {
r = fsync(fd);
} while (r == -1 && errno == EINTR);
return r;
}

View File

@@ -1,349 +0,0 @@
/* SPDX-License-Identifier: MIT
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
*
* Numerical functions.
*/
#ifdef __OpenBSD__
#include <sys/param.h>
#endif
#include <sys/types.h>
#if defined(FALLBACK_RAND_1989) && \
(FALLBACK_RAND_1989) > 0
#include <sys/time.h>
#endif
#include <errno.h>
#if !((defined(__OpenBSD__) && (OpenBSD) >= 201) || \
defined(__FreeBSD__) || \
defined(__NetBSD__) || defined(__APPLE__))
#include <fcntl.h> /* if not arc4random: /dev/urandom */
#endif
#include <limits.h>
#include <stddef.h>
#include <string.h>
#if defined(FALLBACK_RAND_1989) && \
(FALLBACK_RAND_1989) > 0
#include <time.h>
#endif
#include <unistd.h>
#include "../include/common.h"
unsigned short
hextonum(char ch_s)
{
unsigned char ch;
ch = (unsigned char)ch_s;
if ((unsigned int)(ch - '0') <= 9)
return ch - '0';
ch |= 0x20;
if ((unsigned int)(ch - 'a') <= 5)
return ch - 'a' + 10;
if (ch == '?' || ch == 'x')
return (unsigned short)rlong() & 0xf;
return 16; /* invalid character */
}
/* Random numbers
*/
unsigned long
rlong(void)
{
#if !(defined(FALLBACK_RAND_1989) && \
((FALLBACK_RAND_1989) > 0))
#if (defined(__OpenBSD__) && (OpenBSD) >= 201) || \
defined(__FreeBSD__) || \
defined(__NetBSD__) || defined(__APPLE__)
unsigned long rval;
arc4random_buf(&rval, sizeof(unsigned long));
return rval;
#else
static int fd = -1;
static long nr = -1;
static unsigned long off = 0;
#if defined (BUFSIZ)
static char rbuf[BUFSIZ];
#else
#ifndef PORTABLE
static char rbuf[4096];
#elif ((PORTABLE) > 0)
static char rbuf[256]; /* scarce memory on old systems */
#else
static char rbuf[4096]; /* typical 32-bit BUFSIZ */
#endif
#endif
unsigned long rval;
long new_nr;
int retries = 0;
int max_retries = 100;
#if defined(__linux__)
#if defined(HAVE_GETRANDOM) || \
defined(HAVE_GETRANDOM_SYSCALL)
/* linux getrandom()
*
* we *can* use arc4random on
* modern linux, but not on
* every libc. better use the
* official linux function
*
* similar benefits to arc4random
* e.g. works in chroot, blocks
* until it has enough entropy,
* and works even when /dev/urandom
* is available (doesn't use it);
* it's generally more reliable
*/
if (fallback_rand_getrandom(&rval, sizeof(rval)) == 0)
return rval;
/*
* now fall back to urandom if getrandom failed:
*/
#endif
#endif
/* reading from urandom is inherently
* unreliable on old systems, even if
* newer systems make it more reliable
*
* modern linux/bsd make it safe, but
* we have to assume that someone is
* compiling this on linux from 1999
*
* this logic therefore applies various
* tricks to mitigate possible os bugs
*/
retry_urandom_read:
if (++retries > max_retries)
goto rlong_next;
if (nr < 0 || nr < (long)sizeof(unsigned long)) {
if (fd < 0) {
fd = open("/dev/urandom",
O_RDONLY | O_BINARY | O_NOFOLLOW |
O_CLOEXEC);
#ifdef USE_OLD_DEV_RANDOM
#if (USE_OLD_DEV_RANDOM) > 0
/* WARNING:
* /dev/random may block
* forever and does **NOT**
* guarantee better entropy
* on old systems
*
* only use it if needed
*/
if (fd < 0)
fd = open("/dev/random",
O_RDONLY | O_BINARY | O_NOFOLLOW |
O_CLOEXEC);
#endif
#endif
if (fd < 0)
goto retry_urandom_read;
retries = 0;
}
new_nr = rw_file_exact(fd, (unsigned char *)rbuf,
sizeof(rbuf), 0, IO_READ, LOOP_EAGAIN,
LOOP_EINTR, MAX_ZERO_RW_RETRY, OFF_ERR);
if (new_nr < 0 || new_nr < (long)sizeof(rbuf))
goto retry_urandom_read;
/* only reset buffer after successful refill */
nr = new_nr;
off = 0;
/* to mitigate file descriptor
* injection, we do not re-use
* the same descriptor each time
*/
(void) close_on_eintr(fd);
fd = -1;
}
fd = -1;
retries = 0;
memcpy(&rval, rbuf + off, sizeof(unsigned long));
nr -= (long)sizeof(unsigned long);
off += sizeof(unsigned long);
return rval;
rlong_next:
fd = -1;
off = 0;
nr = -1;
err(EIO, "Can't read from /dev/[ua]random");
return 0;
#endif
#else /* FALLBACK_RAND_1989 */
/* your computer is from a museum
*/
unsigned long mix = 0;
int nr;
/* 100 times, for entropy
*/
for (nr = 0; nr < 100; nr++)
mix ^= fallback_rand_1989();
/* 101 times ;)
*/
return fallback_rand_1989();
#endif
}
#if !(defined(FALLBACK_RAND_1989) && \
((FALLBACK_RAND_1989) > 0))
#if defined(__linux__)
#if defined(HAVE_GETRANDOM) || \
defined(HAVE_GETRANDOM_SYSCALL)
int
fallback_rand_getrandom(void *buf, unsigned long len)
{
unsigned long off = 0;
long rval = -1;
if (!len)
return -1;
if (buf == NULL)
return -1;
#if defined(HAVE_GETRANDOM) || \
defined(HAVE_GETRANDOM_SYSCALL)
while (off < len) {
#if defined(HAVE_GETRANDOM)
rval = (long)getrandom((char *)buf + off, len - off, 0);
#elif defined(HAVE_GETRANDOM_SYSCALL)
rval = (long)syscall(SYS_getrandom,
(char *)buf + off, len - off, 0);
#endif
if (rval < 0) {
if (errno == EINTR)
continue;
return -1; /* unsupported by kernel */
}
off += (unsigned long)rval;
}
return 0;
#else
(void)buf;
(void)len;
return -1;
#endif
}
#endif
#endif
#else
/* nobody should use this
* (not crypto-safe)
*/
unsigned long
fallback_rand_1989(void)
{
static unsigned long mix = 0;
static unsigned long counter = 0;
struct timeval tv;
gettimeofday(&tv, NULL);
mix ^= (unsigned long)tv.tv_sec
^ (unsigned long)tv.tv_usec
^ (unsigned long)getpid()
^ (unsigned long)&mix
^ counter++
^ entropy_jitter();
/*
* Stack addresses can vary between
* calls, thus increasing entropy.
*/
mix ^= (unsigned long)&mix;
mix ^= (unsigned long)&tv;
mix ^= (unsigned long)&counter;
return mix;
}
unsigned long
entropy_jitter(void)
{
unsigned long mix;
struct timeval a, b;
long mix_diff;
int c;
mix = 0;
gettimeofday(&a, NULL);
for (c = 0; c < 32; c++) {
getpid();
gettimeofday(&b, NULL);
/*
* prevent negative numbers to prevent overflow,
* which would bias rand to large numbers
*/
mix_diff = (long)(b.tv_usec - a.tv_usec);
if (mix_diff < 0)
mix_diff = -mix_diff;
mix ^= (unsigned long)(mix_diff);
mix ^= (unsigned long)&mix;
}
return mix;
}
#endif
void
check_bin(unsigned long a, const char *a_name)
{
if (a > 1)
err(EINVAL, "%s must be 0 or 1, but is %lu",
a_name, (unsigned long)a);
}

View File

@@ -1,75 +0,0 @@
/* SPDX-License-Identifier: MIT
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
*
* String functions
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stddef.h>
#include <unistd.h>
#include "../include/common.h"
/* Portable strncmp() that blocks
* NULL/empty/unterminated strings
*/
int
xstrxcmp(const char *a, const char *b, unsigned long maxlen)
{
unsigned long i;
if (a == NULL || b == NULL)
err(EINVAL, "NULL input to xstrxcmp");
if (*a == '\0' || *b == '\0')
err(EINVAL, "Empty string in xstrxcmp");
for (i = 0; i < maxlen; i++) {
unsigned char ac = (unsigned char)a[i];
unsigned char bc = (unsigned char)b[i];
if (ac == '\0' || bc == '\0') {
if (ac == bc)
return 0;
return ac - bc;
}
if (ac != bc)
return ac - bc;
}
err(EINVAL, "Unterminated string in xstrxcmp");
errno = EINVAL;
return -1;
}
/* Portable strncmp() that blocks
* NULL/empty/unterminated strings
*/
unsigned long
xstrxlen(const char *scmp, unsigned long maxlen)
{
unsigned long xstr_index;
if (scmp == NULL)
err(EINVAL, "NULL input to xstrxlen");
if (*scmp == '\0')
err(EINVAL, "Empty string in xstrxlen");
for (xstr_index = 0;
xstr_index < maxlen && scmp[xstr_index] != '\0';
xstr_index++);
if (xstr_index == maxlen)
err(EINVAL, "Unterminated string in xstrxlen");
return xstr_index;
}

View File

@@ -1,50 +0,0 @@
/* SPDX-License-Identifier: MIT
* Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
*
* This tool lets you modify Intel GbE NVM (Gigabit Ethernet
* Non-Volatile Memory) images, e.g. change the MAC address.
* These images configure your Intel Gigabit Ethernet adapter.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
#include "include/common.h"
int
main(int argc, char *argv[])
{
struct xstate *x = xstatus(argc, argv);
struct commands *cmd = &x->cmd[x->i];
struct xfile *f = &x->f;
unsigned long c;
if (cmd->run == NULL)
err(errno, "Command not set");
cmd->run();
for (c = 0; c < items(x->cmd); c++)
x->cmd[c].run = cmd_helper_err;
if ((cmd->flags & O_ACCMODE) == O_RDWR)
write_to_gbe_bin();
if (exit_cleanup() == -1)
err(EIO, "%s: close", f->fname);
if (f->io_err_gbe_bin)
err(EIO, "%s: error writing final file");
if (f->tname != NULL)
free(f->tname);
return EXIT_SUCCESS;
}