diff options
Diffstat (limited to 'util/libreboot-utils/lib/string.c')
| -rw-r--r-- | util/libreboot-utils/lib/string.c | 382 |
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; } |
