aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <m@maandree.se>2026-05-11 23:15:33 +0200
committerMattias Andrée <m@maandree.se>2026-05-11 23:15:33 +0200
commit86087e5f9cf4a0512ba36b4d01086b905574a47d (patch)
tree03ce90743ef4d9e3da6ba45b70f11494e12b667c
parentMisc (diff)
downloadlibrecrypt-86087e5f9cf4a0512ba36b4d01086b905574a47d.tar.gz
librecrypt-86087e5f9cf4a0512ba36b4d01086b905574a47d.tar.bz2
librecrypt-86087e5f9cf4a0512ba36b4d01086b905574a47d.tar.xz
Misc
Signed-off-by: Mattias Andrée <m@maandree.se>
-rw-r--r--Makefile1
-rw-r--r--common.h1
-rw-r--r--librecrypt_crypt.c76
-rw-r--r--librecrypt_hash.c99
-rw-r--r--librecrypt_hash_.c62
-rw-r--r--librecrypt_hash_binary.c77
-rw-r--r--librecrypt_realise_salts.c143
-rw-r--r--librecrypt_rng_.c310
-rw-r--r--libtest/Makefile1
-rw-r--r--libtest/TODO7
-rw-r--r--libtest/alloc_have_custom.c10
-rw-r--r--libtest/common.h4
-rw-r--r--libtest/libtest.h18
-rw-r--r--libtest/mmap.c27
-rw-r--r--libtest/random.c140
15 files changed, 892 insertions, 84 deletions
diff --git a/Makefile b/Makefile
index 46b6a83..68d3e4b 100644
--- a/Makefile
+++ b/Makefile
@@ -149,3 +149,4 @@ clean:
.SUFFIXES: .lo .o .c .to .t
.PHONY: all check install uninstall clean
+.PHONY: libtest/libtest.a
diff --git a/common.h b/common.h
index fdbd25b..c6bea51 100644
--- a/common.h
+++ b/common.h
@@ -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