summaryrefslogtreecommitdiff
path: root/util/libreboot-utils
diff options
context:
space:
mode:
Diffstat (limited to 'util/libreboot-utils')
-rw-r--r--util/libreboot-utils/README.md4
-rw-r--r--util/libreboot-utils/include/common.h32
-rw-r--r--util/libreboot-utils/lib/command.c15
-rw-r--r--util/libreboot-utils/lib/file.c145
-rw-r--r--util/libreboot-utils/lib/io.c4
-rw-r--r--util/libreboot-utils/lib/mkhtemp.c110
-rw-r--r--util/libreboot-utils/lib/num.c21
-rw-r--r--util/libreboot-utils/lib/rand.c59
-rw-r--r--util/libreboot-utils/lib/state.c17
-rw-r--r--util/libreboot-utils/lib/string.c487
-rw-r--r--util/libreboot-utils/lottery.c14
-rw-r--r--util/libreboot-utils/mkhtemp.c14
-rw-r--r--util/libreboot-utils/nvmutil.c22
13 files changed, 616 insertions, 328 deletions
diff --git a/util/libreboot-utils/README.md b/util/libreboot-utils/README.md
index 6e94035b..dca1b92e 100644
--- a/util/libreboot-utils/README.md
+++ b/util/libreboot-utils/README.md
@@ -31,7 +31,7 @@ of something like e.g. unveil, but in userspace!
Mkhtemp is designed to be the most secure implementation
possible, of mktemp, offering a heavy amount of hardening
-over traditional mktemp. Written in C89, and the plan is
+over traditional mktemp. Written in C99, and the plan is
very much to keep this code portable over time - patches
very much welcome.
@@ -241,7 +241,7 @@ a drop-in replacement on Linux distros (and BSDs if they
want it), while providing a more hardened version and
recommending that where possible.
-~~Rewrite it in rust~~ (nothing against it though, I just like C89 for some reason)
+~~Rewrite it in rust~~ (nothing against it though, I just like C99 for some reason)
Also, generally document the history of mktemp, and how
mkhtemp works in comparison.
diff --git a/util/libreboot-utils/include/common.h b/util/libreboot-utils/include/common.h
index abd153be..8276d6da 100644
--- a/util/libreboot-utils/include/common.h
+++ b/util/libreboot-utils/include/common.h
@@ -301,8 +301,6 @@ struct xstate {
struct macaddr mac;
struct xfile f;
- char *argv0;
-
size_t i; /* index to cmd[] for current command */
int no_cmd;
@@ -374,21 +372,26 @@ void write_mac_part(size_t partnum);
/* string functions
*/
+size_t page_remain(const void *p);
+long pagesize(void);
int xunveilx(const char *path, const char *permissions);
int xpledgex(const char *promises, const char *execpromises);
char *smalloc(char **buf, size_t size);
void *vmalloc(void **buf, size_t size);
-int slen(const char *scmp, size_t maxlen,
+size_t slen(const char *scmp, size_t maxlen,
size_t *rval);
+int vcmp(const void *s1, const void *s2, size_t n);
int scmp(const char *a, const char *b,
size_t maxlen, int *rval);
-int sdup(const char *s,
+int ccmp(const char *a, const char *b, size_t i,
+ int *rval);
+char *sdup(const char *s,
size_t n, char **dest);
-int scatn(ssize_t sc, const char **sv,
+char *scatn(ssize_t sc, const char **sv,
size_t max, char **rval);
-int scat(const char *s1, const char *s2,
+char *scat(const char *s1, const char *s2,
size_t n, char **dest);
-int dcat(const char *s, size_t n,
+void dcat(const char *s, size_t n,
size_t off, char **dest1,
char **dest2);
/* numerical functions
@@ -396,10 +399,10 @@ int dcat(const char *s, size_t n,
unsigned short hextonum(char ch_s);
void spew_hex(const void *data, size_t len);
-void *mkrbuf(size_t n);
+void *rmalloc(size_t n);
void rset(void *buf, size_t n);
-void *mkrbuf(size_t n);
-char *mkrstr(size_t n);
+void *rmalloc(size_t n);
+char *rchars(size_t n);
size_t rsize(size_t n);
/* Helper functions for command: dump
@@ -484,6 +487,14 @@ ssize_t rw_over_nrw(ssize_t r, size_t nrw);
off_t lseek_on_eintr(int fd, off_t off,
int whence, int loop_eagain, int loop_eintr);
int try_err(int loop_err, int errval);
+ssize_t read_on_eintr(int fd,
+ void *buf, size_t count);
+ssize_t write_on_eintr(int fd,
+ void *buf, size_t count);
+ssize_t pread_on_eintr(int fd,
+ void *buf, size_t count, off_t off);
+ssize_t pwrite_on_eintr(int fd,
+ void *buf, size_t count, off_t off);
/* Error handling and cleanup
*/
@@ -524,7 +535,6 @@ mkhtemp_tmpfile_linux(int dirfd,
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);
diff --git a/util/libreboot-utils/lib/command.c b/util/libreboot-utils/lib/command.c
index a1e46e5f..3ee75628 100644
--- a/util/libreboot-utils/lib/command.c
+++ b/util/libreboot-utils/lib/command.c
@@ -57,10 +57,7 @@ sanitize_command_index(size_t c)
err_exit(EINVAL, "cmd index %lu: empty str",
(size_t)c);
- if (slen(cmd->str, MAX_CMD_LEN +1, &rval) < 0)
- err_exit(errno, "Could not get command length");
-
- if (rval > MAX_CMD_LEN) {
+ if (slen(cmd->str, MAX_CMD_LEN +1, &rval) > MAX_CMD_LEN) {
err_exit(EINVAL, "cmd index %lu: str too long: %s",
(size_t)c, cmd->str);
}
@@ -109,10 +106,7 @@ set_cmd(int argc, char *argv[])
cmd = x->cmd[c].str;
- if (scmp(argv[2], cmd, MAX_CMD_LEN, &rval) < 0)
- err_exit(EINVAL,
- "could not compare command strings");
- if (rval != 0)
+ if (scmp(argv[2], cmd, MAX_CMD_LEN, &rval))
continue; /* not the right command */
/* valid command found */
@@ -239,10 +233,7 @@ parse_mac_string(void)
size_t rval;
- if (slen(x->mac.str, 18, &rval) < 0)
- err_exit(EINVAL, "Could not determine MAC length");
-
- if (rval != 17)
+ if (slen(x->mac.str, 18, &rval) != 17)
err_exit(EINVAL, "MAC address is the wrong length");
memset(mac->mac_buf, 0, sizeof(mac->mac_buf));
diff --git a/util/libreboot-utils/lib/file.c b/util/libreboot-utils/lib/file.c
index 3620f425..b9d31ad7 100644
--- a/util/libreboot-utils/lib/file.c
+++ b/util/libreboot-utils/lib/file.c
@@ -88,10 +88,7 @@ fsync_dir(const char *path)
maxlen = 4096;
#endif
- if (if_err(path == NULL, EFAULT) ||
- if_err_sys(slen(path, maxlen, &pathlen) < 0) ||
- if_err(pathlen >= maxlen || pathlen < 0, EMSGSIZE) ||
- if_err(pathlen == 0, EINVAL))
+ if (if_err(slen(path, maxlen, &pathlen) == 0, EINVAL))
goto err_fsync_dir;
memcpy(smalloc(&dirbuf, pathlen + 1),
@@ -323,15 +320,15 @@ try_rw_again:
real_pread_pwrite:
#endif
if (rw_type == IO_WRITE)
- r = write(fd, mem, nrw);
+ r = write_on_eintr(fd, mem, nrw);
else if (rw_type == IO_READ)
- r = read(fd, mem, nrw);
+ r = read_on_eintr(fd, mem, nrw);
#if defined(REAL_POS_IO) && \
REAL_POS_IO > 0
else if (rw_type == IO_PWRITE)
- r = pwrite(fd, mem, nrw, off);
+ r = pwrite_on_eintr(fd, mem, nrw, off);
else if (rw_type == IO_PREAD)
- r = pread(fd, mem, nrw, off);
+ r = pread_on_eintr(fd, mem, nrw, off);
#endif
if (r == -1 && (errno == try_err(loop_eintr, EINTR)
@@ -386,9 +383,9 @@ real_pread_pwrite:
}
if (rw_type == IO_PREAD)
- r = read(fd, mem, nrw);
+ r = read_on_eintr(fd, mem, nrw);
else if (rw_type == IO_PWRITE)
- r = write(fd, mem, nrw);
+ r = write_on_eintr(fd, mem, nrw);
if (rw_over_nrw(r, nrw) == -1)
break;
@@ -538,20 +535,6 @@ try_err(int loop_err, int errval)
}
void
-free_and_set_null(char **buf)
-{
- if (buf == NULL)
- err_exit(EFAULT,
- "null ptr (to ptr for freeing) in free_and_set_null");
-
- if (*buf == NULL)
- return;
-
- free(*buf);
- *buf = NULL;
-}
-
-void
open_on_eintr(const char *path,
int *fd, int flags, mode_t mode,
struct stat *st)
@@ -855,11 +838,12 @@ fs_dirname_basename(const char *path,
size_t maxlen = 4096;
#endif
- if (path == NULL || dir == NULL || base == NULL ||
- if_err_sys(slen(path, maxlen, &len) < 0))
+ if (if_err(path == NULL || dir == NULL || base == NULL, EFAULT))
return -1;
- memcpy(smalloc(&buf, len + 1), path, len + 1);
+ slen(path, maxlen, &len);
+ memcpy(smalloc(&buf, len + 1),
+ path, len + 1);
/* strip trailing slashes */
while (len > 1 && buf[len - 1] == '/')
@@ -879,7 +863,7 @@ fs_dirname_basename(const char *path,
}
} else if (allow_relative) {
- *dir = strdup(".");
+ sdup(".", maxlen, dir);
*base = buf;
} else {
errno = EINVAL;
@@ -969,13 +953,118 @@ retry:
return rval;
}
+ssize_t
+read_on_eintr(int fd,
+ void *buf, size_t count)
+{
+ int saved_errno = errno;
+ int rval;
+ if (if_err(buf == NULL, EFAULT) ||
+ if_err(fd < 0, EBADF) ||
+ if_err(count == 0, EINVAL))
+ goto err;
+retry:
+ errno = 0;
+ if ((rval = read(fd, buf, count)) == -1 && (
+ errno == EINTR ||
+ errno == EAGAIN ||
+ errno == EWOULDBLOCK ||
+ errno == ETXTBSY))
+ goto retry;
+ errno = saved_errno;
+ return rval;
+err:
+ return set_errno(saved_errno, EIO);
+}
+ssize_t
+pread_on_eintr(int fd,
+ void *buf, size_t count,
+ off_t off)
+{
+ int saved_errno = errno;
+ int rval;
+ if (if_err(buf == NULL, EFAULT) ||
+ if_err(fd < 0, EBADF) ||
+ if_err(off < 0, EFAULT) ||
+ if_err(count == 0, EINVAL))
+ goto err;
+retry:
+ errno = 0;
+ if ((rval = pread(fd, buf, count, off)) == -1 && (
+ errno == EINTR ||
+ errno == EAGAIN ||
+ errno == EWOULDBLOCK ||
+ errno == ETXTBSY))
+ goto retry;
+ errno = saved_errno;
+ return rval;
+err:
+ return set_errno(saved_errno, EIO);
+}
+ssize_t
+write_on_eintr(int fd,
+ void *buf, size_t count)
+{
+ int saved_errno = errno;
+ int rval;
+
+ if (if_err(buf == NULL, EFAULT) ||
+ if_err(fd < 0, EBADF) ||
+ if_err(count == 0, EINVAL))
+ goto err;
+
+retry:
+ errno = 0;
+
+ if ((rval = write(fd, buf, count)) == -1 && (
+ errno == EINTR ||
+ errno == EAGAIN ||
+ errno == EWOULDBLOCK ||
+ errno == ETXTBSY))
+ goto retry;
+
+ errno = saved_errno;
+ return rval;
+err:
+ return set_errno(saved_errno, EIO);
+}
+
+ssize_t
+pwrite_on_eintr(int fd,
+ void *buf, size_t count,
+ off_t off)
+{
+ int saved_errno = errno;
+ int rval;
+
+ if (if_err(buf == NULL, EFAULT) ||
+ if_err(fd < 0, EBADF) ||
+ if_err(off < 0, EFAULT) ||
+ if_err(count == 0, EINVAL))
+ goto err;
+
+retry:
+ errno = 0;
+
+ if ((rval = pwrite(fd, buf, count, off)) == -1 && (
+ errno == EINTR ||
+ errno == EAGAIN ||
+ errno == EWOULDBLOCK ||
+ errno == ETXTBSY))
+ goto retry;
+
+ errno = saved_errno;
+ return rval;
+err:
+ return set_errno(saved_errno, EIO);
+}
diff --git a/util/libreboot-utils/lib/io.c b/util/libreboot-utils/lib/io.c
index eac6073e..4938cdc8 100644
--- a/util/libreboot-utils/lib/io.c
+++ b/util/libreboot-utils/lib/io.c
@@ -143,7 +143,7 @@ read_file(void)
if (_r < 0)
err_exit(errno, "%s: read failed (cmp)", f->tname);
- if (memcmp(f->buf, f->bufcmp, f->gbe_file_size) != 0)
+ if (vcmp(f->buf, f->bufcmp, f->gbe_file_size) != 0)
err_exit(errno, "%s: %s: read contents differ (pre-test)",
f->fname, f->tname);
}
@@ -329,7 +329,7 @@ check_written_part(size_t p)
f->rw_check_err_read[p] = f->io_err_gbe = 1;
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)
+ else if (vcmp(mem_offset, f->pad, gbe_rw_size) != 0)
f->rw_check_bad_part[p] = f->io_err_gbe = 1;
if (f->rw_check_err_read[p] ||
diff --git a/util/libreboot-utils/lib/mkhtemp.c b/util/libreboot-utils/lib/mkhtemp.c
index 61479b25..0560da47 100644
--- a/util/libreboot-utils/lib/mkhtemp.c
+++ b/util/libreboot-utils/lib/mkhtemp.c
@@ -120,8 +120,6 @@ new_tmp_common(int *fd, char **path, int type,
if (tmpdir == NULL)
goto err;
- if (slen(tmpdir, maxlen, &dirlen) < 0)
- goto err;
if (*tmpdir == '\0')
goto err;
if (*tmpdir != '/')
@@ -132,22 +130,12 @@ new_tmp_common(int *fd, char **path, int type,
else
templatestr = "tmp.XXXXXXXXXX";
- if (slen(templatestr, maxlen, &templatestr_len) < 0)
- goto err;
-
- /* sizeof adds an extra byte, useful
- * because we also want '.' or '/'
- */
- destlen = dirlen + 1 + templatestr_len;
- if (destlen > maxlen - 1) {
- errno = EOVERFLOW;
- goto err;
- }
-
- memcpy(smalloc(&dest, destlen + 1), tmpdir, dirlen);
- *(dest + dirlen) = '/';
- memcpy(dest + dirlen + 1, templatestr, templatestr_len);
- *(dest + destlen) = '\0';
+ /* may as well calculate in advance */
+ destlen = slen(tmpdir, maxlen, &dirlen) + 1
+ + slen(templatestr, maxlen, &templatestr_len);
+ /* full path: */
+ dest = scatn(3, (const char *[]) { tmpdir, "/", templatestr },
+ maxlen, &dest);
fname = dest + dirlen + 1;
@@ -319,12 +307,10 @@ same_dir(const char *a, const char *b)
/* optimisation: if both dirs
are the same, we don't need
- to check anything. sehr schnell:
+ to check anything. sehr schnell!
*/
- if (scmp(a, b, maxlen, &rval_scmp) < 0)
- goto err_same_dir;
/* bonus: scmp checks null for us */
- if (rval_scmp == 0)
+ if (!scmp(a, b, maxlen, &rval_scmp))
goto success_same_dir;
fd_a = fs_open(a, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
@@ -533,7 +519,7 @@ mkhtemp(int *fd,
struct stat *st_dir_initial,
int type)
{
- size_t len = 0;
+ size_t template_len = 0;
size_t xc = 0;
size_t fname_len = 0;
@@ -557,30 +543,28 @@ mkhtemp(int *fd,
if (if_err(fd == NULL || template == NULL || fname == NULL ||
st_dir_initial == NULL, EFAULT) ||
if_err(*fd >= 0, EEXIST) ||
- if_err(dirfd < 0, EBADF)
- ||
- if_err_sys(slen(template, max_len, &len) < 0) ||
- if_err(len >= max_len, EMSGSIZE)
- ||
- if_err_sys(slen(fname, max_len, &fname_len) < 0) ||
- if_err(fname == NULL, EINVAL) ||
- if_err(strrchr(fname, '/') != NULL, EINVAL))
+ if_err(dirfd < 0, EBADF))
return -1;
- for (end = template + len; /* count X */
+ /* count X */
+ for (end = template + slen(template, max_len, &template_len);
end > template && *--end == 'X'; xc++);
- if (if_err(xc < 3 || xc > len, EINVAL) ||
- if_err(fname_len > len, EOVERFLOW))
+ fname_len = slen(fname, max_len, &fname_len);
+ if (if_err(strrchr(fname, '/') != NULL, EINVAL))
return -1;
- if (if_err(memcmp(fname, template + len - fname_len,
+ if (if_err(xc < 3 || xc > template_len, EINVAL) ||
+ if_err(fname_len > template_len, EOVERFLOW))
+ return -1;
+
+ if (if_err(vcmp(fname, template + template_len - fname_len,
fname_len) != 0, EINVAL))
return -1;
/* fname_copy = templatestr region only; p points to trailing XXXXXX */
memcpy(smalloc(&fname_copy, fname_len + 1),
- template + len - fname_len,
+ template + template_len - fname_len,
fname_len + 1);
p = fname_copy + fname_len - xc;
@@ -596,7 +580,7 @@ mkhtemp(int *fd,
goto err;
/* success: copy final name back */
- memcpy(template + len - fname_len,
+ memcpy(template + template_len - fname_len,
fname_copy, fname_len);
errno = saved_errno;
@@ -627,6 +611,7 @@ mkhtemp_try_create(int dirfd,
struct stat st_open;
int saved_errno = errno;
int rval = -1;
+ char *rstr = NULL;
int file_created = 0;
int dir_created = 0;
@@ -636,8 +621,13 @@ mkhtemp_try_create(int dirfd,
if_err(*fd >= 0, EEXIST))
goto err;
- if (if_err_sys(mkhtemp_fill_random(p, xc) < 0) ||
- if_err_sys(fd_verify_dir_identity(dirfd, st_dir_initial) < 0))
+ /* TODO: potential infinite loop under entropy failure.
+ * if attacker has control of rand - TODO: maybe add timeout
+ */
+ memcpy(p, rstr = rchars(xc), xc);
+ free_and_set_null(&rstr);
+
+ if (if_err_sys(fd_verify_dir_identity(dirfd, st_dir_initial) < 0))
goto err;
if (type == MKHTEMP_FILE) {
@@ -765,6 +755,7 @@ mkhtemp_tmpfile_linux(int dirfd,
int tmpfd = -1;
size_t retries;
int linked = 0;
+ char *rstr = NULL;
if (fd == NULL || st == NULL ||
fname_copy == NULL || p == NULL ||
@@ -785,8 +776,8 @@ mkhtemp_tmpfile_linux(int dirfd,
for (retries = 0; retries < MKHTEMP_RETRY_MAX; retries++) {
- if (mkhtemp_fill_random(p, xc) < 0)
- goto err;
+ memcpy(p, rstr = rchars(xc), xc);
+ free_and_set_null(&rstr);
if (fd_verify_dir_identity(dirfd,
st_dir_initial) < 0)
@@ -832,43 +823,6 @@ err:
}
#endif
-/* TODO: potential infinite loop under entropy failure.
- * e.g. keeps returning low quality RNG, or atacker
- * has control (DoS attack potential).
- * possible solution: add a timeout (and abort if
- * the timeout is reached)
- */
-int
-mkhtemp_fill_random(char *p, size_t xc)
-{
- static const char ch[] =
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
-
- unsigned char scratch[64];
-
- size_t off = 0;
- size_t i;
-
- /* clamp rand to prevent modulo bias */
- size_t limit = 256 - (256 % (sizeof(ch) - 1));
-
- if (if_err(p == NULL, EFAULT))
- return -1;
-
-retry_rand:
- rset(scratch, sizeof(scratch));
-
- for (i = 0; i < sizeof(scratch) && off < xc; i++) {
- if (scratch[i] < limit)
- p[off++] = ch[scratch[i] % (sizeof(ch) - 1)];
- }
-
- if (off < xc)
- goto retry_rand;
-
- return 0;
-}
-
/* WARNING: **ONCE** per file.
*
* some of these checks will trip up
diff --git a/util/libreboot-utils/lib/num.c b/util/libreboot-utils/lib/num.c
index e4d0ce6b..e13a8853 100644
--- a/util/libreboot-utils/lib/num.c
+++ b/util/libreboot-utils/lib/num.c
@@ -50,6 +50,22 @@ hextonum(char ch_s)
}
/* basically hexdump -C */
+/*
+ TODO: optimise this
+ write a full util for hexdump
+ how to optimise:
+ don't call print tens of thousands of times!
+ convert the numbers manually, and cache everything
+ in a BUFSIZ sized buffer, with everything properly
+ aligned. i worked out that i could fit 79 rows
+ in a 8KB buffer (1264 bytes of numbers represented
+ as strings in hex)
+ this depends on the OS, and would be calculated at
+ runtime.
+ then:
+ don't use printf. just write it to stdout (basically
+ a simple cat implementation)
+*/
void
spew_hex(const void *data, size_t len)
{
@@ -64,7 +80,10 @@ spew_hex(const void *data, size_t len)
for (i = 0; i < len; i += 16) {
- printf("%08zx ", i);
+ if (len <= 4294967296) /* below 4GB */
+ printf("%08zx ", i);
+ else
+ printf("%0*zx ", sizeof(size_t) * 2, i);
for (j = 0; j < 16; j++) {
diff --git a/util/libreboot-utils/lib/rand.c b/util/libreboot-utils/lib/rand.c
index 9edc8a5b..9da8d9eb 100644
--- a/util/libreboot-utils/lib/rand.c
+++ b/util/libreboot-utils/lib/rand.c
@@ -72,6 +72,36 @@
* or your program dies.
*/
+/* random string generator, with
+ * rejection sampling. NOTE: only
+ * uses ASCII-safe characters, for
+ * printing on a unix terminal
+ *
+ * you still shouldn't use this for
+ * password generation; open diceware
+ * passphrases are better for that
+ *
+ * NOTE: the generated strings must
+ * ALSO be safe for file/directory names
+ * on unix-like os e.g. linux/bsd
+ */
+char *
+rchars(size_t n) /* emulates spkmodem-decode */
+{
+ static char ch[] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+ char *s = NULL;
+ size_t i;
+
+ smalloc(&s, n + 1);
+ for (i = 0; i < n; i++)
+ s[i] = ch[rsize(sizeof(ch) - 1)];
+
+ *(s + n) = '\0';
+ return s;
+}
+
size_t
rsize(size_t n)
{
@@ -85,32 +115,8 @@ rsize(size_t n)
return rval % n;
}
-char *
-mkrstr(size_t n) /* emulates spkmodem-decode */
-{
- char *s;
- size_t i;
-
- if (n == 0)
- err_exit(EPERM, "mkrbuf: zero-byte request");
-
- if (n >= SIZE_MAX - 1)
- err_exit(EOVERFLOW, "mkrbuf: overflow");
-
- if (if_err((s = mkrbuf(n + 1)) == NULL, EFAULT))
- err_exit(EFAULT, "mkrstr: null");
-
- for (i = 0; i < n; i++)
- while(*(s + i) == '\0')
- rset(s + i, 1);
-
- *(s + n) = '\0';
-
- return s;
-}
-
void *
-mkrbuf(size_t n)
+rmalloc(size_t n)
{
void *buf = NULL;
rset(vmalloc(&buf, n), n);
@@ -144,7 +150,8 @@ rset(void *buf, size_t n)
int fd = -1;
open_on_eintr("/dev/urandom", &fd, O_RDONLY, 0400, NULL);
retry_rand:
- if ((rc = read(fd, (unsigned char *)buf + off, n - off)) < 0) {
+ if ((rc = read_on_eintr(fd,
+ (unsigned char *)buf + off, n - off)) < 0) {
#elif defined(__linux__)
retry_rand:
if ((rc = (ssize_t)syscall(SYS_getrandom,
diff --git a/util/libreboot-utils/lib/state.c b/util/libreboot-utils/lib/state.c
index a3cd5b1f..f0be5656 100644
--- a/util/libreboot-utils/lib/state.c
+++ b/util/libreboot-utils/lib/state.c
@@ -22,6 +22,12 @@
struct xstate *
xstart(int argc, char *argv[])
{
+#if defined(PATH_LEN) && \
+ ((PATH_LEN) >= 256)
+ static size_t maxlen = PATH_LEN;
+#else
+ static size_t maxlen = 4096;
+#endif
static int first_run = 1;
static char *dir = NULL;
static char *base = NULL;
@@ -77,9 +83,6 @@ xstart(int argc, char *argv[])
/* .f */
{0},
- /* .argv0 (for our getprogname implementation) */
- NULL,
-
/* ->i (index to cmd[]) */
0,
@@ -103,7 +106,6 @@ xstart(int argc, char *argv[])
us.f.buf = us.f.real_buf;
- us.argv0 = argv[0];
us.f.fname = argv[1];
us.f.tmp_fd = -1;
@@ -117,8 +119,7 @@ xstart(int argc, char *argv[])
err_exit(errno, "xstart: don't know CWD of %s",
us.f.fname);
- if ((us.f.base = strdup(base)) == NULL)
- err_exit(errno, "strdup base");
+ sdup(base, maxlen, &us.f.base);
us.f.dirfd = fs_open(dir,
O_RDONLY | O_DIRECTORY);
@@ -132,9 +133,7 @@ xstart(int argc, char *argv[])
&tmpdir, &tmpbase_local, 0) < 0)
err_exit(errno, "tmp basename");
- us.f.tmpbase = strdup(tmpbase_local);
- if (us.f.tmpbase == NULL)
- err_exit(errno, "strdup tmpbase");
+ sdup(tmpbase_local, maxlen, &us.f.tmpbase);
free_and_set_null(&tmpdir);
diff --git a/util/libreboot-utils/lib/string.c b/util/libreboot-utils/lib/string.c
index d5e2de85..c083bd6d 100644
--- a/util/libreboot-utils/lib/string.c
+++ b/util/libreboot-utils/lib/string.c
@@ -19,6 +19,61 @@
#include "../include/common.h"
+/* for null detection inside
+ * word-optimised string functions
+ */
+#define ff ((size_t)-1 / 0xFF)
+#define high ((ff) * 0x80)
+/* NOTE:
+ * do not assume that a match means
+ * both words have null at the same
+ * location. see how this is handled
+ * e.g. in scmp.
+ */
+#define zeroes(x) (((x) - (ff)) & ~(x) & (high))
+
+size_t
+page_remain(const void *p)
+{
+ /* calling sysconf repeatedly
+ * is folly. cache it (static)
+ */
+ static size_t pagesz = 0;
+ if (!pagesz)
+ pagesz = (size_t)pagesize();
+
+ return pagesz - ((uintptr_t)p & (pagesz - 1));
+}
+
+long
+pagesize(void)
+{
+ static long rval = 0;
+ static int set = 0;
+
+ if (!set) {
+ if ((rval = sysconf(_SC_PAGESIZE)) < 0)
+ err_exit(errno, "could not determine page size");
+ set = 1;
+ }
+
+ return rval;
+}
+
+void
+free_and_set_null(char **buf)
+{
+ if (buf == NULL)
+ err_exit(EFAULT,
+ "null ptr (to ptr for freeing) in free_and_set_null");
+
+ if (*buf == NULL)
+ return;
+
+ free(*buf);
+ *buf = NULL;
+}
+
/* safe(ish) malloc.
use this and free_and_set_null()
@@ -64,180 +119,310 @@ vmalloc(void **buf, size_t size)
return *buf = rval;
}
-/* strict strcmp */
+/* strict word-based strcmp */
int
scmp(const char *a,
const char *b,
size_t maxlen,
int *rval)
{
- size_t ch;
- unsigned char ac;
- unsigned char bc;
+ size_t i = 0;
+ size_t j;
+ size_t wa;
+ size_t wb;
+ int saved_errno = errno;
- if (a == NULL ||
- b == NULL ||
- rval == NULL) {
- errno = EFAULT;
+ if (if_err(a == NULL || b == NULL || rval == NULL, EFAULT))
goto err;
+
+ for ( ; ((uintptr_t)(a + i) % sizeof(size_t)) != 0; i++) {
+
+ if (if_err(i >= maxlen, EOVERFLOW))
+ goto err;
+ else if (!ccmp(a, b, i, rval))
+ goto out;
}
- for (ch = 0; ch < maxlen; ch++) {
+ for ( ; i + sizeof(size_t) <= maxlen;
+ i += sizeof(size_t)) {
- ac = (unsigned char)a[ch];
- bc = (unsigned char)b[ch];
+ /* prevent crossing page boundary on word check */
+ if (page_remain(a + i) < sizeof(size_t) ||
+ page_remain(b + i) < sizeof(size_t))
+ break;
- if (ac != bc) {
- *rval = ac - bc;
- return 0;
- }
+ memcpy(&wa, a + i, sizeof(size_t));
+ memcpy(&wb, b + i, sizeof(size_t));
- if (ac == '\0') {
- *rval = 0;
- return 0;
- }
+ if (wa != wb)
+ for (j = 0; j < sizeof(size_t); j++)
+ if (!ccmp(a, b, i + j, rval))
+ goto out;
+
+ if (!zeroes(wa))
+ continue;
+
+ *rval = 0;
+ goto out;
}
+ for ( ; i < maxlen; i++)
+ if (!ccmp(a, b, i, rval))
+ goto out;
+
err:
- errno = EFAULT;
+ (void) set_errno(saved_errno, EFAULT);
if (rval != NULL)
*rval = -1;
+
+ err_exit(errno, "scmp");
return -1;
+out:
+ errno = saved_errno;
+ return *rval;
}
-/* strict strlen */
-int
+int ccmp(const char *a, const char *b,
+ size_t i, int *rval)
+{
+ unsigned char ac;
+ unsigned char bc;
+
+ if (if_err(a == NULL || b == NULL || rval == NULL, EFAULT))
+ err_exit(errno, "ccmp");
+
+ ac = (unsigned char)a[i];
+ bc = (unsigned char)b[i];
+
+ if (ac != bc) {
+ *rval = ac - bc;
+ return 0;
+ } else if (ac == '\0') {
+ *rval = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+/* strict word-based strlen */
+size_t
slen(const char *s,
size_t maxlen,
size_t *rval)
{
- size_t ch;
+ int saved_errno = errno;
+ size_t i = 0;
+ size_t w;
+ size_t j;
- if (s == NULL ||
- rval == NULL) {
- errno = EFAULT;
+ if (if_err(s == NULL || rval == NULL, EFAULT))
goto err;
+
+ for ( ; ((uintptr_t)(s + i) % sizeof(size_t)) != 0; i++) {
+
+ if (i >= maxlen)
+ goto err;
+ if (s[i] == '\0') {
+ *rval = i;
+ goto out;
+ }
}
- for (ch = 0;
- ch < maxlen && s[ch] != '\0';
- ch++);
+ for ( ; i + sizeof(size_t) <= maxlen;
+ i += sizeof(size_t)) {
- if (ch == maxlen) {
- /* unterminated */
- errno = EFAULT;
- goto err;
+ memcpy(&w, s + i, sizeof(size_t));
+ if (!zeroes(w))
+ continue;
+
+ for (j = 0; j < sizeof(size_t); j++) {
+ if (s[i + j] == '\0') {
+ *rval = i + j;
+ goto out;
+ }
+ }
+ }
+
+ for ( ; i < maxlen; i++) {
+ if (s[i] == '\0') {
+ *rval = i;
+ goto out;
+ }
}
- *rval = ch;
- return 0;
err:
+ (void) set_errno(saved_errno, EFAULT);
if (rval != NULL)
*rval = 0;
- return -1;
+
+ err_exit(errno, "slen"); /* abort */
+ return 0; /* gcc15 is happy */
+out:
+ errno = saved_errno;
+ return *rval;
}
-/* strict strdup */
-int
+/* strict word-based strdup */
+char *
sdup(const char *s,
- size_t n, char **dest)
+ size_t max, char **dest)
{
- size_t size;
- char *rval = NULL;
+ size_t j;
+ size_t w;
+ size_t i = 0;
+ char *out = NULL;
+ int saved_errno = errno;
+
+ if (if_err(dest == NULL || *dest != NULL || s == NULL, EFAULT))
+ goto err;
+
+ out = smalloc(dest, max);
+
+ for ( ; ((uintptr_t)(s + i) % sizeof(size_t)) != 0; i++) {
+
+ if (if_err(i >= max, EOVERFLOW))
+ goto err;
- if (dest == NULL ||
- slen(s, n, &size) < 0) {
- if (dest != NULL)
- *dest = NULL;
- return -1;
+ out[i] = s[i];
+ if (s[i] == '\0') {
+ *dest = out;
+ goto out;
+ }
}
- memcpy(smalloc(&rval, size + 1), s, size);
- *(rval + size) = '\0';
+ for ( ; i + sizeof(size_t) <= max; i += sizeof(size_t)) {
- *dest = rval;
- return 0;
+ if (page_remain(s + i) < sizeof(size_t))
+ break;
+
+ memcpy(&w, s + i, sizeof(size_t));
+ if (!zeroes(w)) {
+ memcpy(out + i, &w, sizeof(size_t));
+ continue;
+ }
+
+ for (j = 0; j < sizeof(size_t); j++) {
+
+ out[i + j] = s[i + j];
+ if (s[i + j] == '\0') {
+ *dest = out;
+ goto out;
+ }
+ }
+ }
+
+ for ( ; i < max; i++) {
+
+ out[i] = s[i];
+ if (s[i] == '\0') {
+ *dest = out;
+ goto out;
+ }
+ }
+
+err:
+ free_and_set_null(&out);
+ if (dest != NULL)
+ *dest = NULL;
+
+ (void) set_errno(saved_errno, EFAULT);
+ err_exit(errno, "sdup");
+
+ return NULL;
+out:
+ errno = saved_errno;
+ return *dest;
}
/* concatenate N number of strings */
-/* slen already checks null/termination */
-int
+char *
scatn(ssize_t sc, const char **sv,
size_t max, char **rval)
{
- ssize_t i = 0;
-
- size_t ts = 0;
- size_t *size = NULL;
-
- char *ct = NULL;
int saved_errno = errno;
-
- if (if_err(sc <= 0, EINVAL) ||
- if_err(sc > SIZE_MAX / sizeof(size_t), EOVERFLOW) ||
- if_err(sv == NULL, EINVAL))
+ char *final = NULL;
+ char *rcur = NULL;
+ char *rtmp = NULL;
+ size_t i;
+
+ if (if_err(sc < 2, EINVAL) ||
+ if_err(sv == NULL, EFAULT) ||
+ if_err(rval == NULL || *rval != NULL, EFAULT))
goto err;
- vmalloc((void **)&size, sizeof(size_t) * sc);
+ for (i = 0; i < sc; i++) {
- for (i = 0; i < sc; i++, ts += size[i])
- if (if_err(sv[i] == NULL, EINVAL) ||
- slen(sv[i], max, &size[i]) < 0 ||
- if_err(size[i] > max - 1, EOVERFLOW) ||
- if_err((size[i] + ts) < ts, EOVERFLOW))
+ if (if_err(sv[i] == NULL, EFAULT))
goto err;
+ else if (i == 0) {
+ (void) sdup(sv[0], max, &final);
+ continue;
+ }
- if (if_err(ts > SIZE_MAX - 1, EOVERFLOW) ||
- if_err(ts > max - 1, EOVERFLOW))
- goto err;
-
- smalloc(&ct, ts + 1);
- for (ts = i = 0; i < sc; i++, ts += size[i])
- memcpy(ct + ts, sv[i], size[i]);
+ rtmp = NULL;
+ scat(final, sv[i], max, &rtmp);
- *(ct + ts) = '\0';
- *rval = ct;
+ free_and_set_null(&final);
+ final = rtmp;
+ rtmp = NULL;
+ }
errno = saved_errno;
- return 0;
+ *rval = final;
+ return *rval;
err:
- free_and_set_null(&ct);
- free_and_set_null((char **)&size);
+ free_and_set_null(&rcur);
+ free_and_set_null(&rtmp);
+ free_and_set_null(&final);
- return set_errno(saved_errno, EFAULT);
+ (void) set_errno(saved_errno, EFAULT);
+
+ err_exit(errno, "scatn");
+ return NULL;
}
/* strict strcat */
-int
+char *
scat(const char *s1, const char *s2,
size_t n, char **dest)
{
size_t size1;
size_t size2;
char *rval = NULL;
+ int saved_errno = errno;
- if (dest == NULL ||
- slen(s1, n, &size1) < 0 ||
- slen(s2, n, &size2) < 0 ||
- if_err(size1 > SIZE_MAX - size2 - 1, EOVERFLOW)) {
+ if (if_err(dest == NULL || *dest != NULL, EFAULT))
+ goto err;
- if (dest != NULL)
- *dest = NULL;
- return -1;
- }
+ slen(s1, n, &size1);
+ slen(s2, n, &size2);
+
+ if (if_err(size1
+ > SIZE_MAX - size2 - 1, EOVERFLOW))
+ goto err;
+
+ smalloc(&rval, size1 + size2 + 1);
- memcpy(smalloc(&rval, size1 + size2 + 1),
- s1, size1);
+ memcpy(rval, s1, size1);
memcpy(rval + size1, s2, size2);
*(rval + size1 + size2) = '\0';
*dest = rval;
- return 0;
+ errno = saved_errno;
+ return *dest;
+err:
+ (void) set_errno(saved_errno, EINVAL);
+ if (dest != NULL)
+ *dest = NULL;
+ err_exit(errno, "scat");
+
+ return NULL;
}
/* strict split/de-cat - off is where
2nd buffer will start from */
-int
+void
dcat(const char *s, size_t n,
size_t off, char **dest1,
char **dest2)
@@ -245,14 +430,14 @@ dcat(const char *s, size_t n,
size_t size;
char *rval1 = NULL;
char *rval2 = NULL;
+ int saved_errno = errno;
- if (dest1 == NULL || dest2 == NULL ||
- slen(s, n, &size) < 0 ||
- if_err(size == SIZE_MAX, EOVERFLOW) ||
- if_err(off >= size, EOVERFLOW)) {
+ if (if_err(dest1 == NULL || dest2 == NULL, EFAULT))
+ goto err;
+ if (if_err(slen(s, n, &size) >= SIZE_MAX - 1, EOVERFLOW) ||
+ if_err(off >= size, EOVERFLOW))
goto err;
- }
memcpy(smalloc(&rval1, off + 1),
s, off);
@@ -265,20 +450,55 @@ dcat(const char *s, size_t n,
*dest1 = rval1;
*dest2 = rval2;
- return 0;
+ errno = saved_errno;
+ return;
err:
- if (rval1 != NULL)
- free(rval1);
- if (rval2 != NULL)
- free(rval2);
+ *dest1 = *dest2 = NULL;
- if (dest1 != NULL)
- *dest1 = NULL;
- if (dest2 != NULL)
- *dest2 = NULL;
+ free_and_set_null(&rval1);
+ free_and_set_null(&rval2);
- return -1;
+ (void) set_errno(saved_errno, EINVAL);
+ err_exit(errno, "dcat");
+}
+
+/* because no libc reimagination is complete
+ * without a reimplementation of memcmp. and
+ * no safe one is complete without null checks.
+ */
+int
+vcmp(const void *s1, const void *s2, size_t n)
+{
+ int saved_errno = errno;
+ size_t i = 0;
+ size_t a;
+ size_t b;
+
+ const unsigned char *x;
+ const unsigned char *y;
+
+ if (if_err(s1 == NULL || s2 == NULL, EFAULT))
+ err_exit(EFAULT, "vcmp: null input");
+
+ x = s1;
+ y = s2;
+
+ for ( ; i + sizeof(size_t) <= n; i += sizeof(size_t)) {
+
+ memcpy(&a, x + i, sizeof(size_t));
+ memcpy(&b, y + i, sizeof(size_t));
+
+ if (a != b)
+ break;
+ }
+
+ for ( ; i < n; i++)
+ if (x[i] != y[i])
+ return (int)x[i] - (int)y[i];
+
+ errno = saved_errno;
+ return 0;
}
/* on functions that return with errno,
@@ -310,17 +530,13 @@ err_exit(int nvm_errval, const char *msg, ...)
if (!errno)
saved_errno = errno = ECANCELED;
- if ((p = lbgetprogname()) != NULL)
- fprintf(stderr, "%s: ", p);
+ fprintf(stderr, "%s: ", lbgetprogname());
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));
+ fprintf(stderr, ": %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
@@ -359,22 +575,16 @@ no_op(void)
const char *
lbgetprogname(void)
{
- static char *rval = NULL;
- static char *p;
- static int setname = 0;
-
- if (!setname) {
- if ((rval = lbsetprogname(NULL)) == NULL)
- return NULL;
-
- p = strrchr(rval, '/');
- if (p)
- rval = p + 1;
-
- setname = 1;
- }
-
- return rval;
+ char *name = lbsetprogname(NULL);
+ char *p = NULL;
+ if (name)
+ p = strrchr(name, '/');
+ if (p)
+ return p + 1;
+ else if (name)
+ return name;
+ else
+ return "libreboot-utils";
}
/* singleton. if string not null,
@@ -385,17 +595,14 @@ lbgetprogname(void)
char *
lbsetprogname(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)
- return NULL;
+ static int set = 0;
- memcpy(smalloc(&progname, len + 1), argv0, len + 1);
- setname = 1;
+ if (!set) {
+ if (argv0 == NULL)
+ return "libreboot-utils";
+ (void) sdup(argv0, 4096, &progname);
+ set = 1;
}
return progname;
diff --git a/util/libreboot-utils/lottery.c b/util/libreboot-utils/lottery.c
index 0b3719a4..7370de1b 100644
--- a/util/libreboot-utils/lottery.c
+++ b/util/libreboot-utils/lottery.c
@@ -18,25 +18,25 @@ main(int argc, char **argv)
{
int same = 0;
char *buf;
+ size_t size = BUFSIZ;
(void) argc, (void) argv;
(void) errhook(exit_cleanup);
- if (lbsetprogname(argv[0]) == NULL)
- err_exit(errno, "could not set progname");
+ (void) lbsetprogname(argv[0]);
/* https://man.openbsd.org/pledge.2 */
xpledgex("stdio", NULL);
- buf = mkrbuf(BUFSIZ);
- if (!memcmp(buf, buf + (BUFSIZ >> 1), BUFSIZ >> 1))
+ buf = rmalloc(size);
+ if (!vcmp(buf, buf + (size >> 1), size >> 1))
same = 1;
if (argc < 2) /* no spew */
- spew_hex(buf, BUFSIZ);
- free(buf);
+ spew_hex(buf, size);
+ free_and_set_null(&buf);
fprintf(stderr, "\n%s\n", same ? "You win!" : "You lose!");
- return same ^ 1;
+ return same ? EXIT_SUCCESS : EXIT_FAILURE;
}
static void
diff --git a/util/libreboot-utils/mkhtemp.c b/util/libreboot-utils/mkhtemp.c
index 04ce1993..f4c2b646 100644
--- a/util/libreboot-utils/mkhtemp.c
+++ b/util/libreboot-utils/mkhtemp.c
@@ -62,8 +62,7 @@ main(int argc, char *argv[])
int type = MKHTEMP_FILE;
(void) errhook(exit_cleanup);
- if (lbsetprogname(argv[0]) == NULL)
- err_exit(errno, "could not set progname");
+ (void) lbsetprogname(argv[0]);
/* https://man.openbsd.org/pledge.2 */
xpledgex("stdio flock rpath wpath cpath", NULL);
@@ -96,11 +95,7 @@ main(int argc, char *argv[])
/* custom template e.g. foo.XXXXXXXXXXXXXXXXXXXXX */
if (template != NULL) {
- if (slen(template, maxlen, &tlen) < 0)
- err_exit(EINVAL,
- "invalid template");
-
- for (p = template + tlen;
+ for (p = template + slen(template, maxlen, &tlen);
p > template && *--p == 'X'; xc++);
if (xc < 3) /* the gnu mktemp errs on less than 3 */
@@ -133,8 +128,9 @@ main(int argc, char *argv[])
err_exit(EFAULT, "bad string initialisation");
if (*s == '\0')
err_exit(EFAULT, "empty string initialisation");
- if (slen(s, maxlen, &len) < 0)
- err_exit(EFAULT, "unterminated string initialisiert");
+
+ slen(s, maxlen, &len); /* Nullterminierung prüfen */
+ /* for good measure */
printf("%s\n", s);
diff --git a/util/libreboot-utils/nvmutil.c b/util/libreboot-utils/nvmutil.c
index 49d81a01..ec41371f 100644
--- a/util/libreboot-utils/nvmutil.c
+++ b/util/libreboot-utils/nvmutil.c
@@ -33,8 +33,7 @@ main(int argc, char *argv[])
size_t c;
(void) errhook(exit_cleanup);
- if (lbsetprogname(argv[0]) == NULL)
- err_exit(errno, "could not set progname");
+ (void) lbsetprogname(argv[0]);
/* https://man.openbsd.org/pledge.2 */
/* https://man.openbsd.org/unveil.2 */
@@ -96,5 +95,22 @@ main(int argc, char *argv[])
static void
exit_cleanup(void)
{
- return;
+ struct xstate *x;
+ struct xfile *f;
+
+ x = xstatus();
+ if (x == NULL)
+ return;
+
+ f = &x->f;
+
+ /* close fds if still open */
+ close_on_eintr(&f->tmp_fd);
+ close_on_eintr(&f->gbe_fd);
+
+ /* unlink tmpfile if it exists */
+ if (f->tname != NULL) {
+ (void) unlink(f->tname);
+ free_and_set_null(&f->tname);
+ }
}