diff options
| author | Mattias Andrée <m@maandree.se> | 2026-05-11 23:15:33 +0200 |
|---|---|---|
| committer | Mattias Andrée <m@maandree.se> | 2026-05-11 23:15:33 +0200 |
| commit | 86087e5f9cf4a0512ba36b4d01086b905574a47d (patch) | |
| tree | 03ce90743ef4d9e3da6ba45b70f11494e12b667c | |
| parent | Misc (diff) | |
| download | librecrypt-86087e5f9cf4a0512ba36b4d01086b905574a47d.tar.gz librecrypt-86087e5f9cf4a0512ba36b4d01086b905574a47d.tar.bz2 librecrypt-86087e5f9cf4a0512ba36b4d01086b905574a47d.tar.xz | |
Misc
Signed-off-by: Mattias Andrée <m@maandree.se>
| -rw-r--r-- | Makefile | 1 | ||||
| -rw-r--r-- | common.h | 1 | ||||
| -rw-r--r-- | librecrypt_crypt.c | 76 | ||||
| -rw-r--r-- | librecrypt_hash.c | 99 | ||||
| -rw-r--r-- | librecrypt_hash_.c | 62 | ||||
| -rw-r--r-- | librecrypt_hash_binary.c | 77 | ||||
| -rw-r--r-- | librecrypt_realise_salts.c | 143 | ||||
| -rw-r--r-- | librecrypt_rng_.c | 310 | ||||
| -rw-r--r-- | libtest/Makefile | 1 | ||||
| -rw-r--r-- | libtest/TODO | 7 | ||||
| -rw-r--r-- | libtest/alloc_have_custom.c | 10 | ||||
| -rw-r--r-- | libtest/common.h | 4 | ||||
| -rw-r--r-- | libtest/libtest.h | 18 | ||||
| -rw-r--r-- | libtest/mmap.c | 27 | ||||
| -rw-r--r-- | libtest/random.c | 140 |
15 files changed, 892 insertions, 84 deletions
@@ -149,3 +149,4 @@ clean: .SUFFIXES: .lo .o .c .to .t .PHONY: all check install uninstall clean +.PHONY: libtest/libtest.a @@ -448,6 +448,7 @@ int librecrypt_check_settings_(const char *settings, size_t len, const char *fmt # include <sys/resource.h> # include <sys/types.h> # include <sys/wait.h> +# include <setjmp.h> # include <signal.h> # include <string.h> # include <unistd.h> diff --git a/librecrypt_crypt.c b/librecrypt_crypt.c index a223680..c41a9c4 100644 --- a/librecrypt_crypt.c +++ b/librecrypt_crypt.c @@ -13,16 +13,90 @@ librecrypt_crypt(char *restrict out_buffer, size_t size, const char *phrase, siz #else +static void +check(const char *phrase, const char *settings, const char *hash) +{ + size_t hashlen = strlen(hash); + size_t len = strlen(phrase); + char buf[1024]; + + assert(hashlen <= sizeof(buf)); + + memset(buf, 0, sizeof(buf)); + EXPECT(librecrypt_crypt(buf, sizeof(buf), phrase, len, settings, NULL) == (ssize_t)hashlen); + EXPECT(!memcmp(hash, buf, hashlen + 1u)); + + memset(buf, 0, sizeof(buf)); + EXPECT(librecrypt_crypt(buf, hashlen + 1u, phrase, len, settings, NULL) == (ssize_t)hashlen); + EXPECT(!memcmp(hash, buf, hashlen + 1u)); + + memset(buf, 0, sizeof(buf)); + EXPECT(librecrypt_crypt(buf, hashlen, phrase, len, settings, NULL) == (ssize_t)hashlen); + EXPECT(!memcmp(hash, buf, hashlen - 1u)); + EXPECT(!buf[hashlen]); + + memset(buf, 0, sizeof(buf)); + EXPECT(librecrypt_crypt(buf, 2u, phrase, len, settings, NULL) == (ssize_t)hashlen); + EXPECT(!memcmp(hash, buf, 1u)); + EXPECT(!buf[1u]); + + memset(buf, 0, sizeof(buf)); + EXPECT(librecrypt_crypt(buf, 1u, phrase, len, settings, NULL) == (ssize_t)hashlen); + EXPECT(!buf[0u]); + + EXPECT(librecrypt_crypt(buf, 0u, phrase, len, settings, NULL) == (ssize_t)hashlen); + EXPECT(librecrypt_crypt(NULL, 0u, phrase, len, settings, NULL) == (ssize_t)hashlen); +} + + +#define CHECK(PHRASE, CONF, HASHLEN, IS_DEFAULT_HASHLEN, HASH)\ + do {\ + check(PHRASE, CONF HASH, CONF HASH);\ + check(PHRASE, CONF "*" #HASHLEN, CONF HASH);\ + if (IS_DEFAULT_HASHLEN)\ + check(PHRASE, CONF, CONF HASH);\ + } while (0) + + +#define CHECK_BAD(ALGO)\ + do {\ + errno = 0;\ + EXPECT(librecrypt_crypt(NULL, 0u, NULL, 0u, ALGO"m=0,t=999999999999999999,p=0$AAAABBBB$*0", NULL) == -1);\ + EXPECT(errno == EINVAL);\ + } while (0) + + int main(void) { SET_UP_ALARM(); INIT_RESOURCE_TEST(); +#if defined(SUPPORT_ARGON2I) + CHECK("password", "$argon2i$" "m=256,t=2,p=1$c29tZXNhbHQ$", 32, 1, "/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY"); + CHECK("password", "$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQ$", 32, 1, "iekCn0Y3spW+sCcFanM2xBT63UP2sghkUoHLIUpWRS8"); + CHECK_BAD("$argon2i$"); +#endif +#if defined(SUPPORT_ARGON2ID) + CHECK("password", "$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$", 32, 1, "nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"); + CHECK_BAD("$argon2id$"); +#endif +#if defined(SUPPORT_ARGON2DS) + CHECK("", "$argon2ds$v=16$m=""8,t=1,p=1$ICAgICAgICA$", 32, 1, "zgdykk9ZjN5VyrW0LxGw8LmrJ1Z6fqSC+3jPQtn4n0s"); + CHECK_BAD("$argon2ds$"); +#endif +#if defined(SUPPORT_ARGON2D) + CHECK("", "$argon2d$v=16$m=""8,t=1,p=1$ICAgICAgICA$", 100, 0, "NjODMrWrS7zeivNNpHsuxD9c6uDmUQ6YqPRhb8H5DSNw9" + "n683FUCJZ3tyxgfJpYYANI+01WT/S5zp1UVs+qNRwnkdE" + "yLKZMg+DIOXVc9z1po9ZlZG8+Gp4g5brqfza3lvkR9vw"); + CHECK_BAD("$argon2d$"); +#endif + STOP_RESOURCE_TEST(); return 0; } #endif -/* TODO test */ +/* TODO test chaining */ +/* TODO test salt generation */ diff --git a/librecrypt_hash.c b/librecrypt_hash.c index 0d24954..f793085 100644 --- a/librecrypt_hash.c +++ b/librecrypt_hash.c @@ -13,16 +13,113 @@ librecrypt_hash(char *restrict out_buffer, size_t size, const char *phrase, size #else +static void +check(const char *phrase, const char *settings, const char *hash) +{ + size_t hashlen = strlen(hash); + size_t len = strlen(phrase); + char buf[1024]; + + assert(hashlen <= sizeof(buf)); + + memset(buf, 0, sizeof(buf)); + EXPECT(librecrypt_hash(buf, sizeof(buf), phrase, len, settings, NULL) == (ssize_t)hashlen); + EXPECT(!memcmp(hash, buf, hashlen + 1u)); + + memset(buf, 0, sizeof(buf)); + EXPECT(librecrypt_hash(buf, hashlen + 1u, phrase, len, settings, NULL) == (ssize_t)hashlen); + EXPECT(!memcmp(hash, buf, hashlen + 1u)); + + memset(buf, 0, sizeof(buf)); + EXPECT(librecrypt_hash(buf, hashlen, phrase, len, settings, NULL) == (ssize_t)hashlen); + EXPECT(!memcmp(hash, buf, hashlen - 1u)); + EXPECT(!buf[hashlen]); + + memset(buf, 0, sizeof(buf)); + EXPECT(librecrypt_hash(buf, 2u, phrase, len, settings, NULL) == (ssize_t)hashlen); + EXPECT(!memcmp(hash, buf, 1u)); + EXPECT(!buf[1u]); + + memset(buf, 0, sizeof(buf)); + EXPECT(librecrypt_hash(buf, 1u, phrase, len, settings, NULL) == (ssize_t)hashlen); + EXPECT(!buf[0u]); + + EXPECT(librecrypt_hash(buf, 0u, phrase, len, settings, NULL) == (ssize_t)hashlen); + EXPECT(librecrypt_hash(NULL, 0u, phrase, len, settings, NULL) == (ssize_t)hashlen); +} + + +#define CHECK(PHRASE, CONF, HASHLEN, IS_DEFAULT_HASHLEN, HASH)\ + do {\ + check(PHRASE, CONF HASH, HASH);\ + check(PHRASE, CONF "*" #HASHLEN, HASH);\ + if (IS_DEFAULT_HASHLEN)\ + check(PHRASE, CONF, HASH);\ + } while (0) + + +#define CHECK_BAD(ALGO)\ + do {\ + errno = 0;\ + EXPECT(librecrypt_hash(NULL, 0u, NULL, 0u, ALGO"m=0,t=999999999999999999,p=0$AAAABBBB$*0", NULL) == -1);\ + EXPECT(errno == EINVAL);\ + errno = 0;\ + EXPECT(librecrypt_hash(NULL, 0u, NULL, 0u, ALGO"m=4096,t=10,p=1$*32$", NULL) == -1);\ + EXPECT(errno == EINVAL);\ + errno = 0;\ + EXPECT(librecrypt_hash(NULL, 0u, NULL, 0u, ALGO"m=4096,t=10,p=1$AAAABBBBCCCCDDDD$*0", NULL) == -1);\ + EXPECT(errno == EINVAL);\ + errno = 0;\ + EXPECT(librecrypt_hash(NULL, 0u, NULL, 0u, ALGO"m=4096,t=10,p=1$AAAABBBBCCCCDDDD$*x", NULL) == -1);\ + EXPECT(errno == EINVAL);\ + errno = 0;\ + EXPECT(librecrypt_hash(NULL, 0u, NULL, 0u, ALGO"m=4096,t=10,p=1$AAAABBBBCCCCDDDD$*2x", NULL) == -1);\ + EXPECT(errno == EINVAL);\ + errno = 0;\ + EXPECT(librecrypt_hash(NULL, 0u, NULL, 0u, ALGO"m=4096,t=10,p=1$AAAABBBBCCCCDDDD$*9999999999999999999999999999999", NULL) == -1);\ + EXPECT(errno == EINVAL);\ + errno = 0;\ + EXPECT(librecrypt_hash(NULL, 0u, NULL, 0u, ALGO"m=4096,t=10,p=1$AAAABBBBCCCCDDDD$AAAABBBBCCCCDDDDEEEEFFFF>", NULL) == -1);\ + EXPECT(errno == EINVAL);\ + errno = 0;\ + EXPECT(librecrypt_hash(NULL, 0u, NULL, 0u, ALGO"m=4096,t=10,p=1$AAAABBBBCCCCDDDD$AAAABBBBCCCCDDDDEEEEFFFFG", NULL) == -1);\ + EXPECT(errno == EINVAL);\ + errno = 0;\ + EXPECT(librecrypt_hash(NULL, 0u, NULL, 0u, ALGO"m=4096,t=10,p=1$AAAABBBBCCCCDDDD$AAAABBBBCCCCDDDDEEEEFFFF~", NULL) == -1);\ + EXPECT(errno == EINVAL);\ + } while (0) + + int main(void) { SET_UP_ALARM(); INIT_RESOURCE_TEST(); +#if defined(SUPPORT_ARGON2I) + CHECK("password", "$argon2i$" "m=256,t=2,p=1$c29tZXNhbHQ$", 32, 1, "/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY"); + CHECK("password", "$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQ$", 32, 1, "iekCn0Y3spW+sCcFanM2xBT63UP2sghkUoHLIUpWRS8"); + CHECK_BAD("$argon2i$"); +#endif +#if defined(SUPPORT_ARGON2ID) + CHECK("password", "$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$", 32, 1, "nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"); + CHECK_BAD("$argon2id$"); +#endif +#if defined(SUPPORT_ARGON2DS) + CHECK("", "$argon2ds$v=16$m=""8,t=1,p=1$ICAgICAgICA$", 32, 1, "zgdykk9ZjN5VyrW0LxGw8LmrJ1Z6fqSC+3jPQtn4n0s"); + CHECK_BAD("$argon2ds$"); +#endif +#if defined(SUPPORT_ARGON2D) + CHECK("", "$argon2d$v=16$m=""8,t=1,p=1$ICAgICAgICA$", 100, 0, "NjODMrWrS7zeivNNpHsuxD9c6uDmUQ6YqPRhb8H5DSNw9" + "n683FUCJZ3tyxgfJpYYANI+01WT/S5zp1UVs+qNRwnkdE" + "yLKZMg+DIOXVc9z1po9ZlZG8+Gp4g5brqfza3lvkR9vw"); + CHECK_BAD("$argon2d$"); +#endif + STOP_RESOURCE_TEST(); return 0; } #endif -/* TODO test */ +/* TODO test chaining */ diff --git a/librecrypt_hash_.c b/librecrypt_hash_.c index 2ee03ee..c66d4be 100644 --- a/librecrypt_hash_.c +++ b/librecrypt_hash_.c @@ -41,7 +41,7 @@ librecrypt_hash_(char *restrict out_buffer, size_t size, const char *phrase, siz size_t hash_size, digit, quotient, remainder; int has_next, phrase_scratch_i = 0; ssize_t r_len; - int r; + int r, saved_errno; void *new; /* Ensure the reserved parameter is NULL */ @@ -269,12 +269,18 @@ next: } /* Erase and deallocate scratch memory */ - librecrypt_wipe(phrase_scratches[0u], phrase_scratch_sizes[0u]); - librecrypt_wipe(phrase_scratches[1u], phrase_scratch_sizes[1u]); - librecrypt_wipe_str(settings_scratch); - free(phrase_scratches[0u]); - free(phrase_scratches[1u]); - free(settings_scratch); + if (phrase_scratches[0u]) { + librecrypt_wipe(phrase_scratches[0u], phrase_scratch_sizes[0u]); + free(phrase_scratches[0u]); + } + if (phrase_scratches[1u]) { + librecrypt_wipe(phrase_scratches[1u], phrase_scratch_sizes[1u]); + free(phrase_scratches[1u]); + } + if (settings_scratch) { + librecrypt_wipe_str(settings_scratch); + free(settings_scratch); + } /* NUL-terminate output if it is a string (`out_buffer` is offset at every write to it) */ if (size && action != BINARY_HASH) @@ -285,12 +291,20 @@ next: einval: errno = EINVAL; fail: - librecrypt_wipe(phrase_scratches[0u], phrase_scratch_sizes[0u]); - librecrypt_wipe(phrase_scratches[1u], phrase_scratch_sizes[1u]); - librecrypt_wipe_str(settings_scratch); - free(phrase_scratches[0u]); - free(phrase_scratches[1u]); - free(settings_scratch); + saved_errno = errno; + if (phrase_scratches[0u]) { + librecrypt_wipe(phrase_scratches[0u], phrase_scratch_sizes[0u]); + free(phrase_scratches[0u]); + } + if (phrase_scratches[1u]) { + librecrypt_wipe(phrase_scratches[1u], phrase_scratch_sizes[1u]); + free(phrase_scratches[1u]); + } + if (settings_scratch) { + librecrypt_wipe_str(settings_scratch); + free(settings_scratch); + } + errno = saved_errno; return -1; } @@ -298,8 +312,26 @@ fail: #else -/* Tested via librecrypt_hash_binary, librecrypt_hash, and librecrypt_crypt */ -CONST int main(void) { return 0; } +/* Mainly tested via librecrypt_hash_binary, librecrypt_hash, and librecrypt_crypt */ + + +int +main(void) +{ + SET_UP_ALARM(); + INIT_RESOURCE_TEST(); + + errno = 0; + EXPECT(librecrypt_hash_(NULL, 0u, NULL, 0u, "$~no~such~algorithm~$", &(char){0}, ASCII_CRYPT) == -1); + EXPECT(errno == EINVAL); + + errno = 0; + EXPECT(librecrypt_hash_(NULL, 0u, NULL, 0u, "$~no~such~algorithm~$", NULL, ASCII_CRYPT) == -1); + EXPECT(errno == ENOSYS); + + STOP_RESOURCE_TEST(); + return 0; +} #endif diff --git a/librecrypt_hash_binary.c b/librecrypt_hash_binary.c index 089e8be..a56c61b 100644 --- a/librecrypt_hash_binary.c +++ b/librecrypt_hash_binary.c @@ -13,16 +13,91 @@ librecrypt_hash_binary(char *restrict out_buffer, size_t size, const char *phras #else +static void +check(const char *phrase, const char *settings, const char *hash, size_t hashlen) +{ + size_t len = strlen(phrase); + char buf[1024], expected[256], pad; + int strict_pad; + const void *lut; + ssize_t r; + + assert(hashlen <= sizeof(buf)); + assert(hashlen <= sizeof(expected)); + + lut = librecrypt_get_encoding(settings, strlen(settings), &pad, &strict_pad, 1); + assert(lut); + + r = librecrypt_decode(expected, sizeof(expected), hash, strlen(hash), lut, pad, strict_pad); + assert(r > 0 && (size_t)r == hashlen); + + memset(buf, 0, sizeof(buf)); + EXPECT(librecrypt_hash_binary(buf, sizeof(buf), phrase, len, settings, NULL) == (ssize_t)hashlen); + EXPECT(!memcmp(buf, expected, hashlen)); + + memset(buf, 0, sizeof(buf)); + EXPECT(librecrypt_hash_binary(buf, hashlen, phrase, len, settings, NULL) == (ssize_t)hashlen); + EXPECT(!memcmp(buf, expected, hashlen)); + + memset(buf, 0, sizeof(buf)); + EXPECT(librecrypt_hash_binary(buf, 1u, phrase, len, settings, NULL) == (ssize_t)hashlen); + EXPECT(!memcmp(buf, expected, 1u)); + + EXPECT(librecrypt_hash_binary(buf, 0u, phrase, len, settings, NULL) == (ssize_t)hashlen); + EXPECT(librecrypt_hash_binary(NULL, 0u, phrase, len, settings, NULL) == (ssize_t)hashlen); +} + + +#define CHECK(PHRASE, CONF, HASHLEN, IS_DEFAULT_HASHLEN, HASH)\ + do {\ + check(PHRASE, CONF HASH, HASH, (size_t)HASHLEN);\ + check(PHRASE, CONF "*" #HASHLEN, HASH, (size_t)HASHLEN);\ + if (IS_DEFAULT_HASHLEN)\ + check(PHRASE, CONF, HASH, (size_t)HASHLEN);\ + } while (0) + + +#define CHECK_BAD(ALGO)\ + do {\ + errno = 0;\ + EXPECT(librecrypt_hash_binary(NULL, 0u, NULL, 0u, ALGO"m=0,t=999999999999999999,p=0$AAAABBBB$*0", NULL) == -1);\ + EXPECT(errno == EINVAL);\ + errno = 0;\ + EXPECT(librecrypt_hash_binary(NULL, 0u, NULL, 0u, ALGO"m=4096,t=10,p=1$*32$", NULL) == -1);\ + EXPECT(errno == EINVAL);\ + } while (0) + + int main(void) { SET_UP_ALARM(); INIT_RESOURCE_TEST(); +#if defined(SUPPORT_ARGON2I) + CHECK("password", "$argon2i$" "m=256,t=2,p=1$c29tZXNhbHQ$", 32, 1, "/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY"); + CHECK("password", "$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQ$", 32, 1, "iekCn0Y3spW+sCcFanM2xBT63UP2sghkUoHLIUpWRS8"); + CHECK_BAD("$argon2i$"); +#endif +#if defined(SUPPORT_ARGON2ID) + CHECK("password", "$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$", 32, 1, "nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"); + CHECK_BAD("$argon2id$"); +#endif +#if defined(SUPPORT_ARGON2DS) + CHECK("", "$argon2ds$v=16$m=""8,t=1,p=1$ICAgICAgICA$", 32, 1, "zgdykk9ZjN5VyrW0LxGw8LmrJ1Z6fqSC+3jPQtn4n0s"); + CHECK_BAD("$argon2ds$"); +#endif +#if defined(SUPPORT_ARGON2D) + CHECK("", "$argon2d$v=16$m=""8,t=1,p=1$ICAgICAgICA$", 100, 0, "NjODMrWrS7zeivNNpHsuxD9c6uDmUQ6YqPRhb8H5DSNw9" + "n683FUCJZ3tyxgfJpYYANI+01WT/S5zp1UVs+qNRwnkdE" + "yLKZMg+DIOXVc9z1po9ZlZG8+Gp4g5brqfza3lvkR9vw"); + CHECK_BAD("$argon2d$"); +#endif + STOP_RESOURCE_TEST(); return 0; } #endif -/* TODO test */ +/* TODO test chaining */ diff --git a/librecrypt_realise_salts.c b/librecrypt_realise_salts.c index a21c4df..58ba6d3 100644 --- a/librecrypt_realise_salts.c +++ b/librecrypt_realise_salts.c @@ -51,6 +51,7 @@ librecrypt_realise_salts(char *restrict out_buffer, size_t size, const char *set for (i = 0u; settings[i] != '*'; i++); min = i < size ? i : size; memcpy(out_buffer, settings, min); + out_buffer = &out_buffer[min]; size -= min; settings = &settings[i]; ret += i; @@ -60,7 +61,7 @@ librecrypt_realise_salts(char *restrict out_buffer, size_t size, const char *set /* If the '*' is not followed by an unsigned, * decimal integer include it literally */ - if ('0' > settings[1u] || settings[1u] > '9') { + if ('0' > settings[0u] || settings[0u] > '9') { if (size) { *out_buffer++ = '*'; size -= 1u; @@ -95,7 +96,9 @@ librecrypt_realise_salts(char *restrict out_buffer, size_t size, const char *set * r==2: 3 base-64 characters, so 1 padding characters */ right = (r && pad) ? 3u - r : 0u; /* Get total length */ - if (q > ((size_t)SSIZE_MAX - right) / 4u) + if (q > ((size_t)SSIZE_MAX - (r + mid + right)) / 4u) + goto erange; + if (ret > (size_t)SSIZE_MAX - (left + mid + right)) goto erange; ret += left + mid + right; @@ -104,7 +107,7 @@ librecrypt_realise_salts(char *restrict out_buffer, size_t size, const char *set * one variable for all random characters */ left += mid; if (left > size) { - left = 0u; + left = size; mid = 0u; } @@ -134,6 +137,7 @@ librecrypt_realise_salts(char *restrict out_buffer, size_t size, const char *set break; min = i < size ? i : size; memcpy(out_buffer, settings, min); + out_buffer = &out_buffer[min]; size -= min; settings = &settings[i]; ret += i; @@ -160,16 +164,147 @@ erange: #else +# define LARGE "99999999999999999999999999999999999999999999999999999999999999" +# define A10 "AAAAAAAAAA" +# define A40 A10 A10 A10 A10 + + +static unsigned char saltbyte = 0u; + + +static ssize_t +saltgen(void *out, size_t n, void *user) +{ + (void) user; + if (n > (size_t)SSIZE_MAX) + n = (size_t)SSIZE_MAX; + memset(out, *(unsigned char *)user, n); + return (ssize_t)n; +} + + +static ssize_t +saltfail(void *out, size_t n, void *user) +{ + (void) out; + (void) n; + (void) user; + errno = EDOM; + return -1; +} + + int main(void) { + char buf[1024], buf2[1024], conf[128]; + size_t i; + int r; + SET_UP_ALARM(); INIT_RESOURCE_TEST(); + errno = 0; + EXPECT(librecrypt_realise_salts(NULL, 0u, "$~no~such~algorithm~$", NULL, NULL) == -1); + EXPECT(errno == ENOSYS); + + EXPECT(librecrypt_realise_salts(NULL, 0u, "", NULL, NULL) == 0); + +#if defined(SUPPORT_ARGON2ID) +# define ALGO "$argon2id$" +#elif defined(SUPPORT_ARGON2I) +# define ALGO "$argon2i$" +#elif defined(SUPPORT_ARGON2D) +# define ALGO "$argon2d$" +#elif defined(SUPPORT_ARGON2DS) +# define ALGO "$argon2ds$" +#endif + +#if defined(ALGO) +# define CHECK(IN, OUT)\ + do {\ + EXPECT(librecrypt_realise_salts(NULL, 0u, (IN), NULL, NULL) == (ssize_t)sizeof(OUT) - 1);\ + EXPECT(librecrypt_realise_salts(buf, 0u, (IN), NULL, NULL) == (ssize_t)sizeof(OUT) - 1);\ + memset(buf, 99, sizeof(buf));\ + EXPECT(librecrypt_realise_salts(buf, sizeof(buf), (IN), &saltgen, &saltbyte) == (ssize_t)sizeof(OUT) - 1);\ + EXPECT(!strcmp(buf, (OUT)));\ + for (i = 0u; i < sizeof(OUT); i++) {\ + memset(buf, 99, sizeof(buf));\ + EXPECT(librecrypt_realise_salts(buf, i + 1u, (IN), &saltgen, &saltbyte) == (ssize_t)sizeof(OUT) - 1);\ + EXPECT(!memcmp(buf, (OUT), i));\ + EXPECT(!buf[i]);\ + }\ + } while (0) + + CHECK(ALGO, ALGO); + CHECK(ALGO"*100", ALGO "*100"); + CHECK(ALGO"*30$", ALGO A40 "$"); + CHECK(ALGO"*30$*100", ALGO A40 "$*100"); + CHECK(ALGO"*31$*100", ALGO A40 "AA$*100"); + CHECK(ALGO"*32$*100", ALGO A40 "AAA$*100"); + CHECK(ALGO"*33$*100", ALGO A40 "AAAA$*100"); + CHECK(ALGO"*1$*100", ALGO "AA$*100"); + CHECK(ALGO"*2$*100", ALGO "AAA$*100"); + CHECK(ALGO"*3$*100", ALGO "AAAA$*100"); + CHECK(ALGO"*033$*100", ALGO A40 "AAAA$*100"); + CHECK(ALGO"*0$*100", ALGO "$*100"); + CHECK(ALGO"*30*30$*100", ALGO A40 A40"$*100"); + CHECK(ALGO"*30*3$*100", ALGO A40 "AAAA$*100"); + CHECK(ALGO"*30$*30$*100", ALGO A40 "$" A40"$*100"); + CHECK(ALGO"*30$*3$*100", ALGO A40 "$AAAA$*100"); + CHECK(ALGO"*$*100", ALGO "*$*100"); + CHECK(ALGO"*x$*100", ALGO "*x$*100"); + CHECK(ALGO">"ALGO, ALGO">"ALGO); + CHECK(ALGO"*30$*100>"ALGO"*30$*10", ALGO A40 "$*100>" ALGO A40 "$*10"); + CHECK(ALGO"*30$>"ALGO"*30$", ALGO A40 "$>" ALGO A40 "$"); + +# undef CHECK + + errno = 0; + EXPECT(librecrypt_realise_salts(NULL, 0u, ALGO">$~no~such~algorithm~$", &saltgen, &saltbyte) == -1); + EXPECT(errno == ENOSYS); + errno = 0; + EXPECT(librecrypt_realise_salts(NULL, 0u, "$~no~such~algorithm~$>"ALGO, &saltgen, &saltbyte) == -1); + EXPECT(errno == ENOSYS); + errno = 0; + EXPECT(librecrypt_realise_salts(NULL, 0u, ALGO"*"LARGE"$", &saltgen, &saltbyte) == -1); + EXPECT(errno == ERANGE); + + EXPECT(librecrypt_realise_salts(buf, sizeof(ALGO) - 1u, ALGO"*3$", &saltfail, NULL) == (ssize_t)sizeof(ALGO"$") - 1 + 4); + errno = 0; + EXPECT(librecrypt_realise_salts(buf, sizeof(buf), ALGO"*3$", &saltfail, NULL) == -1); + EXPECT(errno == EDOM); + + r = snprintf(conf, sizeof(conf), "%s*%zu$", ALGO, (size_t)SSIZE_MAX / 4u * 3u + 3u); + assert(r > 0 && (size_t)r < sizeof(conf)); + errno = 0; + EXPECT(librecrypt_realise_salts(NULL, 0u, conf, &saltgen, &saltbyte) == -1); + EXPECT(errno == ERANGE); + + r = snprintf(conf, sizeof(conf), "%s*%zu$", ALGO, (size_t)SSIZE_MAX / 4u * 3u); + assert(r > 0 && (size_t)r < sizeof(conf)); + errno = 0; + EXPECT(librecrypt_realise_salts(NULL, 0u, conf, &saltgen, &saltbyte) == -1); + EXPECT(errno == ERANGE); + + r = snprintf(conf, sizeof(conf), "%s*%zu$abcdef", ALGO, ((size_t)SSIZE_MAX - (sizeof(ALGO) - 1u)) / 4u * 3u); + assert(r > 0 && (size_t)r < sizeof(conf)); + errno = 0; + EXPECT(librecrypt_realise_salts(NULL, 0u, conf, &saltgen, &saltbyte) == -1); + EXPECT(errno == ERANGE); + + memset(buf, 99, sizeof(buf)); + memset(buf2, 99, sizeof(buf2)); + EXPECT(librecrypt_realise_salts(buf, sizeof(buf), ALGO"*30$", NULL, NULL) == (ssize_t)sizeof(ALGO"$") - 1 + 40); + EXPECT(librecrypt_realise_salts(buf2, sizeof(buf2), ALGO"*30$", NULL, NULL) == (ssize_t)sizeof(ALGO"$") - 1 + 40); + EXPECT(!buf[sizeof(ALGO"$") - 1u + 40u]); + EXPECT(!buf2[sizeof(ALGO"$") - 1u + 40u]); + EXPECT(memcmp(buf, buf2, sizeof(ALGO"$") - 1u + 40u)); +#endif + STOP_RESOURCE_TEST(); return 0; } #endif -/* TODO test */ diff --git a/librecrypt_rng_.c b/librecrypt_rng_.c index d53b999..3f9b9eb 100644 --- a/librecrypt_rng_.c +++ b/librecrypt_rng_.c @@ -69,68 +69,76 @@ librecrypt_rng_(void *out, size_t n, void *user) #ifndef O_CLOEXEC # define O_CLOEXEC 0 #endif - fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); - if (fd >= 0) { - for (;;) { - r = read(fd, buf, n); - if (r < 0) { - if (errno != EINTR) { - close(fd); - break; - } - saved_errno = EINTR; - } else if (r > 0) { - ret += (size_t)r; - buf = &buf[(size_t)r]; - n -= (size_t)r; - if (!n) { - close(fd); - goto out; - } - } else { +#ifndef O_CLOFORK +# define O_CLOFORK 0 +#endif + fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_CLOFORK); + if (fd < 0) + goto no_urandom; /* this is a goto to make coverage test more comprehensive */ + /* TODO we should make sure this is a character special device */ + for (;;) { + r = read(fd, buf, n); + if (r < 0) { + if (errno != EINTR) { close(fd); break; } + saved_errno = EINTR; + } else if (r > 0) { + ret += (size_t)r; + buf = &buf[(size_t)r]; + n -= (size_t)r; + if (!n) { + close(fd); + goto out; + } + } else { + /* /dev/urandom cannot be exhausted, your system + * has been compromised if this happens */ + /* TODO we should probably warn the user */ + abort(); } } - /* Last restort, use non-entropy based RNG */ +no_urandom: + /* Last resort, use non-entropy based RNG */ /* but try to use a good seed */ state = seed; - if (!state) { - /* Hopefully the application has already called srand(3) */ - state = (unsigned int)rand(); + if (state) + goto have_initial_seed; + /* Hopefully the application has already called srand(3) */ + state = (unsigned int)rand(); #if defined(__linux__) && defined(AT_RANDOM) - /* Otherwise can we use random data the kernel gave to the process */ - random_addr = (uintptr_t)getauxval(AT_RANDOM); - if (random_addr) { - at_random = (const void *)random_addr; - for (i = 0u; i < 16u; i++) - state ^= (unsigned int)at_random[i] << (i % sizeof(unsigned int) * 8u); - goto have_initial_seed; - } + /* Otherwise can we use random data the kernel gave to the process */ + random_addr = (uintptr_t)getauxval(AT_RANDOM); + if (random_addr) { + at_random = (const void *)random_addr; + for (i = 0u; i < 16u; i++) + state ^= (unsigned int)at_random[i] << (i % sizeof(unsigned int) * 8u); + goto have_initial_seed; + } #endif #ifndef MAP_UNINITIALIZED # define MAP_UNINITIALIZED 0 #endif - /* But where that is not supported, we can hopefully - * get a random address: here mmap(2) is much better than - * malloc(3), as malloc(3) may be less random when the - * allocation is small, and we don't want to make a big - * allocation. A few bit's are always 0, but that's not - * a big deal. */ - random_ptr = mmap(NULL, 1u, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_UNINITIALIZED, -1, 0); - if (random_ptr != MAP_FAILED) { - random_addr = (uintptr_t)random_ptr; - state ^= (unsigned int)random_addr; - munmap(random_ptr, 1u); - goto have_initial_seed; /* just using goto to simplify the #if'ing */ - } - random_ptr = malloc(1u); /* NULL is OK (MAP_FAILED was actually also OK) */ + /* But where that is not supported, we can hopefully + * get a random address: here mmap(2) is much better than + * malloc(3), as malloc(3) may be less random when the + * allocation is small, and we don't want to make a big + * allocation. A few bit's are always 0, but that's not + * a big deal. */ + random_ptr = mmap(NULL, 1u, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_UNINITIALIZED, -1, 0); + if (random_ptr != MAP_FAILED) { random_addr = (uintptr_t)random_ptr; state ^= (unsigned int)random_addr; - free(random_ptr); + munmap(random_ptr, 1u); + goto have_initial_seed; /* just using goto to simplify the #if'ing */ } + random_ptr = malloc(1u); /* NULL is OK (MAP_FAILED was actually also OK) */ + random_addr = (uintptr_t)random_ptr; + state ^= (unsigned int)random_addr; + free(random_ptr); + have_initial_seed: /* and always do a time-based reseed in case of multithreading, * so multiple passwords don't end up using the same salt */ @@ -160,6 +168,60 @@ out: #else +static size_t open_calls = 0u; +static int open_error = 0; + +int +(open)(const char *path, int flags, ...) +{ + mode_t mode = 0; + + open_calls += 1u; + + if (open_error) { + errno = open_error; + if (open_error == EINTR) + open_error = 0; + return -1; + } + + if ((flags & O_CREAT) || (flags & O_TMPFILE) == O_TMPFILE) { + va_list args; + va_start(args, flags); + mode = va_arg(args, mode_t); + va_end(args); + } + + return openat(AT_FDCWD, path, flags, mode); +} + + +static volatile size_t beyond_ssize_max = (size_t)SSIZE_MAX + 1u; + +static void +test_oversized(void) +{ + volatile int jumped = 0; + char *buf; + + buf = mmap(NULL, 1u, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + assert(buf != MAP_FAILED); + assert(buf != NULL); + buf[0] = 99; + + libtest_getentropy_jmp_val = 1; + if (!setjmp(libtest_getentropy_jmp)) { + EXPECT(librecrypt_rng_(buf, beyond_ssize_max, NULL) == 9999); + } else { + jumped = 1; + } + EXPECT(jumped); + + EXPECT(buf[0] == 99); + assert(!munmap(buf, 1u)); +} + + int main(void) { @@ -173,6 +235,10 @@ main(void) SET_UP_ALARM(); INIT_RESOURCE_TEST(); + open_calls = 0u; + + /* TODO Test with output pattern (useful for other tests) */ + /* Check that output is random */ n1 = librecrypt_rng_(buf1, sizeof(buf1), NULL); n2 = librecrypt_rng_(buf2, sizeof(buf2), &user); @@ -180,8 +246,160 @@ main(void) EXPECT(n2 >= 128 && (size_t)n2 <= sizeof(buf2)); EXPECT(memcmp(buf1, buf2, (size_t)(n1 < n2 ? n1 : n2))); +#if defined(__linux__) + libtest_getentropy_calls = 0u; + + /* Check with short getrandom(3) */ + errno = 0; + libtest_getrandom_max_return = 1u; + n1 = librecrypt_rng_(buf1, sizeof(buf1), NULL); + EXPECT(n1 >= 128 && (size_t)n1 <= sizeof(buf1)); + EXPECT(memcmp(buf1, buf2, (size_t)(n1 < n2 ? n1 : n2))); + libtest_getrandom_max_return = SIZE_MAX; + EXPECT(libtest_getentropy_calls == 0u); + EXPECT(errno == 0); + + /* Check with getrandom(3) with EINTR */ + errno = 0; + libtest_getrandom_error = EINTR; + n1 = librecrypt_rng_(buf1, sizeof(buf1), NULL); + EXPECT(n1 >= 128 && (size_t)n1 <= sizeof(buf1)); + EXPECT(memcmp(buf1, buf2, (size_t)(n1 < n2 ? n1 : n2))); + assert(!libtest_getrandom_error); + EXPECT(libtest_getentropy_calls == 0u); + EXPECT(errno == EINTR); + + /* Check with getrandom(3) with ENOSYS */ + errno = 0; + libtest_getrandom_error = ENOSYS; + n1 = librecrypt_rng_(buf1, sizeof(buf1), NULL); + EXPECT(n1 >= 128 && (size_t)n1 <= sizeof(buf1)); + EXPECT(memcmp(buf1, buf2, (size_t)(n1 < n2 ? n1 : n2))); + assert(libtest_getrandom_error == ENOSYS); + EXPECT(libtest_getentropy_calls > 0u); + libtest_getentropy_calls = 0u; + EXPECT(errno == 0); + + /* Check with getrandom(3) other error */ + errno = 0; + libtest_getrandom_error = EDOM; + n1 = librecrypt_rng_(buf1, sizeof(buf1), NULL); + EXPECT(n1 >= 128 && (size_t)n1 <= sizeof(buf1)); + EXPECT(memcmp(buf1, buf2, (size_t)(n1 < n2 ? n1 : n2))); + assert(libtest_getrandom_error == EDOM); + EXPECT(libtest_getentropy_calls > 0u); + libtest_getentropy_calls = 0u; + libtest_getrandom_error = 0; + EXPECT(errno == 0); + + /* Check with getrandom(3) zero return */ + errno = 0; + libtest_getrandom_max_return = 0u; + n1 = librecrypt_rng_(buf1, sizeof(buf1), NULL); + EXPECT(n1 >= 128 && (size_t)n1 <= sizeof(buf1)); + EXPECT(memcmp(buf1, buf2, (size_t)(n1 < n2 ? n1 : n2))); + libtest_getrandom_max_return = SIZE_MAX; + EXPECT(libtest_getentropy_calls > 0u); + libtest_getentropy_calls = 0u; + EXPECT(errno == 0); + + /* Don't use getrandom(3) for reminder of the test */ + libtest_getrandom_error = ENOSYS; +#endif + + /* Check with getentropy(3) with EINTR */ + errno = 0; + libtest_getentropy_error = EINTR; + n1 = librecrypt_rng_(buf1, sizeof(buf1), NULL); + EXPECT(n1 >= 128 && (size_t)n1 <= sizeof(buf1)); + EXPECT(memcmp(buf1, buf2, (size_t)(n1 < n2 ? n1 : n2))); + assert(!libtest_getentropy_error); + EXPECT(libtest_getentropy_calls > 0u); + libtest_getentropy_calls = 0u; + EXPECT(errno == EINTR); + + /* Check with getentropy(3) with ENOSYS */ + errno = 0; + libtest_getentropy_error = ENOSYS; + n1 = librecrypt_rng_(buf1, sizeof(buf1), NULL); + EXPECT(n1 >= 128 && (size_t)n1 <= sizeof(buf1)); + EXPECT(memcmp(buf1, buf2, (size_t)(n1 < n2 ? n1 : n2))); + assert(libtest_getentropy_error == ENOSYS); + EXPECT(libtest_getentropy_calls > 0u); + libtest_getentropy_calls = 0u; + EXPECT(errno == 0); + + /* Check with getentropy(3) other error */ + errno = 0; + libtest_getentropy_error = EDOM; + n1 = librecrypt_rng_(buf1, sizeof(buf1), NULL); + EXPECT(n1 >= 128 && (size_t)n1 <= sizeof(buf1)); + EXPECT(memcmp(buf1, buf2, (size_t)(n1 < n2 ? n1 : n2))); + assert(libtest_getentropy_error == EDOM); + EXPECT(libtest_getentropy_calls > 0u); + libtest_getentropy_calls = 0u; + libtest_getentropy_error = 0; + EXPECT(errno == 0); + + /* Check with getentropy(3) */ + assert(open_calls > 0u); + open_calls = 0u; + errno = 0; + n1 = librecrypt_rng_(buf1, sizeof(buf1), NULL); + n2 = librecrypt_rng_(buf2, sizeof(buf2), &user); + EXPECT(n1 >= 128 && (size_t)n1 <= sizeof(buf1)); + EXPECT(n2 >= 128 && (size_t)n2 <= sizeof(buf2)); + EXPECT(memcmp(buf1, buf2, (size_t)(n1 < n2 ? n1 : n2))); + EXPECT(libtest_getentropy_calls > 0u); + libtest_getentropy_calls = 0u; + EXPECT(errno == 0); + libtest_getentropy_real = 1; + EXPECT(open_calls == 0u); + + /* TODO Check with getentropy(3) with small buffer */ + + /* Don't use getentropy(3) for reminder of the test */ + libtest_getentropy_error = ENOSYS; + + /* Check with urandom(4) */ + errno = 0; + n1 = librecrypt_rng_(buf1, sizeof(buf1), NULL); + n2 = librecrypt_rng_(buf2, sizeof(buf2), &user); + EXPECT(n1 >= 128 && (size_t)n1 <= sizeof(buf1)); + EXPECT(n2 >= 128 && (size_t)n2 <= sizeof(buf2)); + EXPECT(memcmp(buf1, buf2, (size_t)(n1 < n2 ? n1 : n2))); + EXPECT(errno == 0); + /* TODO check that rand(3) was not called */ + + /* Check with urandom(4) not available */ + open_calls = 0; + open_error = ENOENT; + n1 = librecrypt_rng_(buf1, sizeof(buf1), NULL); + n2 = librecrypt_rng_(buf2, sizeof(buf2), &user); + EXPECT(n1 >= 128 && (size_t)n1 <= sizeof(buf1)); + EXPECT(n2 >= 128 && (size_t)n2 <= sizeof(buf2)); + EXPECT(memcmp(buf1, buf2, (size_t)(n1 < n2 ? n1 : n2))); + assert(open_error == ENOENT); + open_error = 0; + EXPECT(open_calls > 0u); + + /* TODO Check with urandom(4) read(3) EINTR */ + /* TODO Check with urandom(4) read(3) EIO */ + /* TODO Check with urandom(4) read(3) exhaustion */ + + /* Don't use urandom(4) for reminder of the test */ + open_error = ENOENT; + + /* TODO Check with rand(3) */ + /* TODO Check with mmap(3) failure */ + /* TODO Check with clock_gettime(3) failure */ + /* Check zero-request */ + errno = 0; EXPECT(librecrypt_rng_(NULL, 0u, NULL) == 0u); + EXPECT(errno == 0); + + test_oversized(); STOP_RESOURCE_TEST(); return 0; diff --git a/libtest/Makefile b/libtest/Makefile index 8eddb00..75a76cd 100644 --- a/libtest/Makefile +++ b/libtest/Makefile @@ -28,6 +28,7 @@ OBJ =\ libtest_stack_on_signal.o\ libtest_get_alloc_failure_in.o\ libtest_set_alloc_failure_in.o\ + random.o\ $(OBJ_BACKTRACE)\ HDR =\ diff --git a/libtest/TODO b/libtest/TODO new file mode 100644 index 0000000..65e2642 --- /dev/null +++ b/libtest/TODO @@ -0,0 +1,7 @@ +libtest_alloc should overallocate and fill the extra data with random +bytes and store a checksum which libtest_free should use to determine +if and out of bounds write has been performed. This requires +libtest_alloc only sets the unusable memory size to what has been +required (rounded up to the next page size multiple for pvalloc). It +may also be a good idea to allocate a few bytes before the returned +address. diff --git a/libtest/alloc_have_custom.c b/libtest/alloc_have_custom.c index d532c3f..9c213bb 100644 --- a/libtest/alloc_have_custom.c +++ b/libtest/alloc_have_custom.c @@ -105,11 +105,11 @@ int libtest_have_custom_valloc(void) { CHECK_CUSTOM_ALLOC(freeable_valloc, 1u); int libtest_have_custom_pvalloc(void) { CHECK_CUSTOM_ALLOC(freeable_pvalloc, 1u); } int libtest_have_custom_memalign(void) { CHECK_CUSTOM_ALLOC(memalign, 1u, 1u); } int libtest_have_custom_aligned_alloc(void) { CHECK_CUSTOM_ALLOC(aligned_alloc, 1u, 1u); } -int libtest_have_custom_strdup(void) { CHECK_CUSTOM_ALLOC(strdup, "x"); } -int libtest_have_custom_strndup(void) { CHECK_CUSTOM_ALLOC(strndup, "x", 1u); } -int libtest_have_custom_wcsdup(void) { CHECK_CUSTOM_ALLOC(wcsdup, (wchar_t[]){1, 0}); } -int libtest_have_custom_wcsndup(void) { CHECK_CUSTOM_ALLOC(wcsndup, &(wchar_t){1}, 1u); } -int libtest_have_custom_memdup(void) { CHECK_CUSTOM_ALLOC(memdup, "x", 1u); } +int libtest_have_custom_strdup(void) { CHECK_CUSTOM_ALLOC(strdup, ""); } +int libtest_have_custom_strndup(void) { CHECK_CUSTOM_ALLOC(strndup, "", 1u); } +int libtest_have_custom_wcsdup(void) { CHECK_CUSTOM_ALLOC(wcsdup, &(wchar_t){0}); } +int libtest_have_custom_wcsndup(void) { CHECK_CUSTOM_ALLOC(wcsndup, &(wchar_t){0}, 1u); } +int libtest_have_custom_memdup(void) { CHECK_CUSTOM_ALLOC(memdup, "", 1u); } int diff --git a/libtest/common.h b/libtest/common.h index 667743a..b78b69d 100644 --- a/libtest/common.h +++ b/libtest/common.h @@ -4,6 +4,10 @@ # include <libunwind.h> # include <elfutils/libdwfl.h> #endif +#if defined(__linux__) +# include <sys/random.h> +# include <sys/syscall.h> +#endif #include <sys/mman.h> #include <errno.h> #include <inttypes.h> diff --git a/libtest/libtest.h b/libtest/libtest.h index b6484b2..41d1a41 100644 --- a/libtest/libtest.h +++ b/libtest/libtest.h @@ -1,4 +1,5 @@ /* See LICENSE file for copyright and license details. */ +#include <setjmp.h> #include <stddef.h> #include <signal.h> @@ -402,3 +403,20 @@ size_t libtest_get_alloc_failure_in(void); * a real failure) */ void libtest_set_alloc_failure_in(size_t n); + + +extern unsigned char *libtest_random_pattern; +extern size_t libtest_random_pattern_length; +extern size_t libtest_random_pattern_offset; + +#if defined(__linux__) +extern int libtest_getrandom_real; +extern int libtest_getrandom_error; +extern size_t libtest_getrandom_max_return; +#endif + +extern int libtest_getentropy_real; +extern int libtest_getentropy_error; +extern size_t libtest_getentropy_calls; +extern int libtest_getentropy_jmp_val; +extern jmp_buf libtest_getentropy_jmp; diff --git a/libtest/mmap.c b/libtest/mmap.c index d8a2a60..62ef06e 100644 --- a/libtest/mmap.c +++ b/libtest/mmap.c @@ -2,33 +2,38 @@ #include "common.h" #ifndef TEST -#include <sys/syscall.h> - - #if !defined(__linux__) # errno "Don't know how to implement mmap(3), mumap(3), and mremap(3)" #endif +#ifdef SYS_mmap2 +# define IF_MMAP2(A) (A) +#else +# define IF_MMAP2(A) ((void)0) +#endif + +#if defined(__x86_64__) && defined(__ILP32__) /* x32 */ +# define SYSCALL_ARG_MAX LLONG_MAX +#else +# define SYSCALL_ARG_MAX LONG_MAX +#endif + + void * libtest_real_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off) { size_t pagesize = libtest_get_pagesize(); + IF_MMAP2(assert(pagesize == 4096u)); if (off < 0 || off % (off_t)pagesize) goto einval; - off /= (off_t)pagesize; + IF_MMAP2(off /= (off_t)pagesize); -#if defined(__x86_64__) && defined(__ILP32__) /* x32 */ - if (off > LLONG_MAX) + if (off > SYSCALL_ARG_MAX) goto einval; -#else - if (off > LONG_MAX) - goto einval; -#endif #ifdef SYS_mmap2 - assert(pagesize == 4096u); return (void *)syscall(SYS_mmap2, addr, len, prot, flags, fd, off); #else return (void *)syscall(SYS_mmap, addr, len, prot, flags, fd, off); diff --git a/libtest/random.c b/libtest/random.c new file mode 100644 index 0000000..77e9218 --- /dev/null +++ b/libtest/random.c @@ -0,0 +1,140 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +unsigned char *libtest_random_pattern = NULL; +size_t libtest_random_pattern_length = 0u; +size_t libtest_random_pattern_offset = 0u; + +static ssize_t +genpattern(void *buf, size_t size) +{ + unsigned char *out = buf; + size_t n; + + if (size > (size_t)SSIZE_MAX) + size = (size_t)SSIZE_MAX; + + if (libtest_random_pattern_length) { + while (size) { + if (libtest_random_pattern_offset == libtest_random_pattern_length) + libtest_random_pattern_offset = 0u; + n = libtest_random_pattern_length - libtest_random_pattern_offset; + if (n > size) + n = size; + memcpy(out, &libtest_random_pattern[libtest_random_pattern_offset], n); + out = &out[n]; + size -= n; + libtest_random_pattern_offset += n; + } + } else { + for (; size; size--, out++) + out[0] = (unsigned char)rand(); + } + + return (ssize_t)size; +} + + +#if defined(__linux__) +int libtest_getrandom_real = 1; +int libtest_getrandom_error = 0; +size_t libtest_getrandom_max_return = SIZE_MAX; + +ssize_t +(getrandom)(void *buf, size_t size, unsigned int flags) +{ + if (size > libtest_getrandom_max_return) + size = libtest_getrandom_max_return; + + if (libtest_getrandom_error) { + errno = libtest_getrandom_error; + if (libtest_getrandom_error == EINTR) + libtest_getrandom_error = 0; + return -1; + } + + if (!libtest_getrandom_real) + return genpattern(buf, size); + +# if defined(SYS_getrandom) + return syscall(SYS_getrandom, buf, size, flags); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif + + +int libtest_getentropy_real = 1; +int libtest_getentropy_error = 0; +size_t libtest_getentropy_calls = 0u; +int libtest_getentropy_jmp_val = 0; +jmp_buf libtest_getentropy_jmp; + +int +(getentropy)(void *buf, size_t size) +{ + unsigned char *out = buf; + ssize_t r; + + libtest_getentropy_calls += 1u; + + if (libtest_getentropy_jmp_val) + longjmp(libtest_getentropy_jmp, libtest_getentropy_jmp_val); + + if (libtest_getentropy_error) { + errno = libtest_getentropy_error; + if (libtest_getentropy_error == EINTR) + libtest_getentropy_error = 0; + return -1; + } + + if (size > 256u) { + errno = EIO; + return -1; + } + + if (!libtest_getentropy_real) { + r = genpattern(out, size); + if (r == (ssize_t)size) + return 0; + errno = EIO; + return -1; + } + +#if defined(__linux__) && defined(SYS_getrandom) + while (size) { + r = (ssize_t)syscall(SYS_getrandom, out, size, 0); + if (r < 0) { + if (errno == EINTR) + continue; + if (errno != ENOSYS) + errno = EIO; + return -1; + } + out = &out[r]; + size -= (size_t)r; + } + return 0; +#else + errno = ENOSYS; + return -1; +#endif +} + + +#else + + +CONST int +main(void) +{ + /* TODO maybe test */ + return 0; +} + + +#endif |
