summaryrefslogtreecommitdiff
path: root/util/libreboot-utils/lib/string.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/libreboot-utils/lib/string.c')
-rw-r--r--util/libreboot-utils/lib/string.c382
1 files changed, 296 insertions, 86 deletions
diff --git a/util/libreboot-utils/lib/string.c b/util/libreboot-utils/lib/string.c
index 39b31cb0..c083bd6d 100644
--- a/util/libreboot-utils/lib/string.c
+++ b/util/libreboot-utils/lib/string.c
@@ -19,6 +19,47 @@
#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)
{
@@ -78,103 +119,224 @@ 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;
- if (dest == NULL ||
- slen(s, n, &size) < 0) {
- if (dest != NULL)
- *dest = NULL;
- return -1;
+ out = smalloc(dest, max);
+
+ for ( ; ((uintptr_t)(s + i) % sizeof(size_t)) != 0; i++) {
+
+ if (if_err(i >= max, EOVERFLOW))
+ goto err;
+
+ 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 */
-int
+char *
scatn(ssize_t sc, const char **sv,
size_t max, char **rval)
{
@@ -194,61 +356,73 @@ scatn(ssize_t sc, const char **sv,
if (if_err(sv[i] == NULL, EFAULT))
goto err;
else if (i == 0) {
- if (sdup(sv[0], max, &final) < 0)
- goto err;
+ (void) sdup(sv[0], max, &final);
continue;
}
rtmp = NULL;
- if (scat(final, sv[i], max, &rtmp) < 0)
- goto err;
+ scat(final, sv[i], max, &rtmp);
free_and_set_null(&final);
final = rtmp;
rtmp = NULL;
}
+ errno = saved_errno;
*rval = final;
- return 0;
+ return *rval;
err:
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 (if_err(dest == NULL || *dest != NULL, EFAULT) ||
- 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);
- memcpy(smalloc(&rval, size1 + size2 + 1),
- s1, size1);
+ if (if_err(size1
+ > SIZE_MAX - size2 - 1, EOVERFLOW))
+ goto err;
+
+ smalloc(&rval, size1 + size2 + 1);
+
+ 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)
@@ -256,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);
@@ -276,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,
@@ -390,8 +599,9 @@ lbsetprogname(char *argv0)
static int set = 0;
if (!set) {
- if (argv0 == NULL || sdup(argv0, 4096, &progname) < 0)
+ if (argv0 == NULL)
return "libreboot-utils";
+ (void) sdup(argv0, 4096, &progname);
set = 1;
}