diff options
| author | Mattias Andrée <m@maandree.se> | 2026-05-14 15:55:33 +0200 |
|---|---|---|
| committer | Mattias Andrée <m@maandree.se> | 2026-05-14 15:55:33 +0200 |
| commit | 4e6f25806e3c9fa4753ce959ef990167796acd32 (patch) | |
| tree | e641de9953f5b58afc1f36efe9c42a0d20254cd2 | |
| parent | Fix libtest and add file descriptor leak detection (diff) | |
| download | librecrypt-4e6f25806e3c9fa4753ce959ef990167796acd32.tar.gz librecrypt-4e6f25806e3c9fa4753ce959ef990167796acd32.tar.bz2 librecrypt-4e6f25806e3c9fa4753ce959ef990167796acd32.tar.xz | |
Tests and fixes
Signed-off-by: Mattias Andrée <m@maandree.se>
| -rw-r--r-- | README | 14 | ||||
| -rw-r--r-- | argon2/make_settings.c | 4 | ||||
| -rw-r--r-- | librecrypt.7 | 16 | ||||
| -rw-r--r-- | librecrypt.h | 4 | ||||
| -rw-r--r-- | librecrypt_add_algorithm.3 | 4 | ||||
| -rw-r--r-- | librecrypt_add_algorithm.c | 161 | ||||
| -rw-r--r-- | librecrypt_fill_with_random_.c | 2 | ||||
| -rw-r--r-- | librecrypt_hash_.c | 2 | ||||
| -rw-r--r-- | librecrypt_make_settings.c | 14 | ||||
| -rw-r--r-- | libtest/common.h | 6 | ||||
| -rw-r--r-- | libtest/globals.c | 6 | ||||
| -rw-r--r-- | libtest/libtest.h | 13 | ||||
| -rw-r--r-- | libtest/libtest_fd_tracking.c | 11 | ||||
| -rw-r--r-- | libtest/libtest_free.c | 10 | ||||
| -rw-r--r-- | libtest/libtest_print_backtrace.c | 6 |
15 files changed, 228 insertions, 45 deletions
@@ -100,5 +100,19 @@ DESCRIPTION Get encoding alphabet for the last algorithm in a chain. +NOTES + Using librecrypt_add_algorithm(3) to hash existing password + hashes should be used as a transitional mitigation strategy + when replaing an old password hash function. Once the password + is available in clear text, it should be hashed anew using + only the new password hash function: this will both increase + security and reducing login it, allowing for stronger hash + function configurations. This is especially important if the + password is actually a key and longer than the old hash. It + is also a good idea to force password reset, and lock any + account that hasn't reset its password, because it is + possibly that the old password hashes has been leaked and it + will force a fresh hashing even one counts that seldom log in. + SEE ALSO crypt(3), crypt(5) diff --git a/argon2/make_settings.c b/argon2/make_settings.c index 396c48d..9efb58f 100644 --- a/argon2/make_settings.c +++ b/argon2/make_settings.c @@ -309,8 +309,8 @@ check_aborts(ssize_t (*gen)(char *, size_t, const char *, size_t, uintmax_t, { #define SHORTTEXT "------------------------------------------------------------------------" #define LONGTEXT SHORTTEXT SHORTTEXT SHORTTEXT SHORTTEXT SHORTTEXT SHORTTEXT SHORTTEXT - EXPECT_ABORT(discarded_ssize = (*gen)(NULL, 0, "argon2i$", 0u, 0u, 0, NULL, NULL)); - EXPECT_ABORT(discarded_ssize = (*gen)(NULL, 0, "$argon2"LONGTEXT"$", 0u, 0u, 0, NULL, NULL)); + EXPECT_ABORT(discarded_ssize = (*gen)(NULL, 0u, "argon2i$", 0u, 0u, 0, NULL, NULL)); + EXPECT_ABORT(discarded_ssize = (*gen)(NULL, 0u, "$argon2"LONGTEXT"$", 0u, 0u, 0, NULL, NULL)); } diff --git a/librecrypt.7 b/librecrypt.7 index 7c577a4..d576658 100644 --- a/librecrypt.7 +++ b/librecrypt.7 @@ -123,6 +123,22 @@ binary. Get encoding alphabet for the last algorithm in a chain. +.SH NOTES +Using +.BR librecrypt_add_algorithm (3) +to hash existing password +hashes should be used as a transitional mitigation strategy +when replaing an old password hash function. Once the password +is available in clear text, it should be hashed anew using +only the new password hash function: this will both increase +security and reducing login it, allowing for stronger hash +function configurations. This is especially important if the +password is actually a key and longer than the old hash. It +is also a good idea to force password reset, and lock any +account that hasn't reset its password, because it is +possibly that the old password hashes has been leaked and it +will force a fresh hashing even one counts that seldom log in. + .SH SEE ALSO .BR crypt (3), .BR crypt (5) diff --git a/librecrypt.h b/librecrypt.h index 5218152..550dacc 100644 --- a/librecrypt.h +++ b/librecrypt.h @@ -800,9 +800,9 @@ int librecrypt_test_supported(const char *phrase, size_t len, int text, const ch * * This function is MT-Safe but AS-Unsafe */ -LIBRECRYPT_WRITE_MEM__(1, 2) LIBRECRYPT_READ_WRITE_STR__(3) LIBRECRYPT_READ_STR__(4) +LIBRECRYPT_WRITE_MEM__(1, 2) LIBRECRYPT_READ_STR__(3) LIBRECRYPT_READ_STR__(4) LIBRECRYPT_NONNULL_I__(3) LIBRECRYPT_NONNULL_I__(4) LIBRECRYPT_WUR__ -ssize_t librecrypt_add_algorithm(char *out_buffer, size_t size, char *augend, +ssize_t librecrypt_add_algorithm(char *out_buffer, size_t size, const char *augend, const char *restrict augment, void *reserved); diff --git a/librecrypt_add_algorithm.3 b/librecrypt_add_algorithm.3 index 0bb6566..2c7b365 100644 --- a/librecrypt_add_algorithm.3 +++ b/librecrypt_add_algorithm.3 @@ -6,8 +6,8 @@ librecrypt_add_algorithm - Append an algorithm chain to a password hash string .nf #include <librecrypt.h> -ssize_t \fBlibrecrypt_add_algorithm\fP(char *restrict \fIout_buffer\fP, size_t \fIsize\fP, - char *\fIaugend\fP, const char *restrict \fIaugment\fP, +ssize_t \fBlibrecrypt_add_algorithm\fP(char *\fIout_buffer\fP, size_t \fIsize\fP, + const char *\fIaugend\fP, const char *restrict \fIaugment\fP, void *\fIreserved\fP); .fi .PP diff --git a/librecrypt_add_algorithm.c b/librecrypt_add_algorithm.c index 10a2174..4fa5b94 100644 --- a/librecrypt_add_algorithm.c +++ b/librecrypt_add_algorithm.c @@ -9,7 +9,7 @@ ssize_t -librecrypt_add_algorithm(char *out_buffer, size_t size, char *augend, const char *restrict augment, void *reserved) +librecrypt_add_algorithm(char *out_buffer, size_t size, const char *augend, const char *restrict augment, void *reserved) { size_t prefix1, prefix2, min, ret, len, phraselen; size_t hashsize1, hashsize2; @@ -18,15 +18,6 @@ librecrypt_add_algorithm(char *out_buffer, size_t size, char *augend, const char const unsigned char *lut; ssize_t r; -#define COPY_PREFIX()\ - do {\ - min = prefix1 < size ? prefix1 : size;\ - if (out_buffer != augend)\ - memmove(out_buffer, augend, min);\ - out_buffer = &out_buffer[min];\ - size -= min;\ - } while (0) - /* Reserve space for NUL-termination */ if (size) { nul_term = 1; @@ -39,7 +30,7 @@ librecrypt_add_algorithm(char *out_buffer, size_t size, char *augend, const char prefix1 = librecrypt_settings_prefix(augend, &hashsize1); prefix2 = librecrypt_settings_prefix(augment, &hashsize2); - /* If `augend` specifies as hash size rather than a hash, include it as the prefix */ + /* If `augend` specifies a hash size rather than a hash, include it as the prefix */ if (augend[prefix1] == '*') { prefix1 += strlen(&augend[prefix1]); hashsize1 = 0u; @@ -54,7 +45,11 @@ librecrypt_add_algorithm(char *out_buffer, size_t size, char *augend, const char } ret = prefix1 + 1u + prefix2; if (size) { - COPY_PREFIX(); + min = prefix1 < size ? prefix1 : size; + if (out_buffer != augend) + memmove(out_buffer, augend, min); + out_buffer = &out_buffer[min]; + size -= min; if (size) { *out_buffer++ = LIBRECRYPT_ALGORITHM_LINK_DELIMITER; size -= 1u; @@ -64,25 +59,30 @@ librecrypt_add_algorithm(char *out_buffer, size_t size, char *augend, const char if (hashsize2) { r_int = snprintf(&out_buffer[min], size + 1u, "*%zu", hashsize2); if (r_int < 2) - abort(); + abort(); /* $covered$ (impossible reliably) */ ret += (size_t)r_int; } else { out_buffer[min] = '\0'; } } else { - r_int = snprintf(NULL, 0, "*%zu", hashsize2); + if (!hashsize2) + goto out; + r_int = snprintf(NULL, 0u, "*%zu", hashsize2); if (r_int < 2) - abort(); + abort(); /* $covered$ (impossible reliably) */ ret += (size_t)r_int; + out: + if (nul_term) + out_buffer[0u] = '\0'; } return (ssize_t)ret; } /* Measure size of hash size specification for `augend` */ if (hashsize1) { - r_int = snprintf(NULL, 0, "*%zu", hashsize1); + r_int = snprintf(NULL, 0u, "*%zu", hashsize1); if (r_int < 2) - abort(); + abort(); /* $covered$ (impossible reliably) */ } else { r_int = 0; } @@ -91,7 +91,7 @@ librecrypt_add_algorithm(char *out_buffer, size_t size, char *augend, const char ret = prefix1 + (size_t)r_int + 1u; /* Decode the hash from base-64 to binary */ - if (size <= ret + prefix2 + 1u) { + if (size <= ret + prefix2) { /* If the new hash doesn't fit, don't bother; * hash sizes are independent of password size */ phrase = NULL; @@ -106,7 +106,7 @@ librecrypt_add_algorithm(char *out_buffer, size_t size, char *augend, const char return -1; /* Measure old binary hash */ - r = librecrypt_decode(NULL, 0, &augend[prefix1], len, lut, pad, strict_pad); + r = librecrypt_decode(NULL, 0u, &augend[prefix1], len, lut, pad, strict_pad); if (r <= 0) { if (!r) abort(); @@ -123,10 +123,18 @@ librecrypt_add_algorithm(char *out_buffer, size_t size, char *augend, const char } /* Chain the hash algorithms: write `augent` */ - COPY_PREFIX(); - if (hashsize1 && size) + min = prefix1 < size ? prefix1 : size; + if (out_buffer != augend) + memmove(out_buffer, augend, min); + out_buffer = &out_buffer[min]; + size -= min; + if (hashsize1 && size) { if (snprintf(out_buffer, size + 1u, "*%zu", hashsize1) != r_int) - abort(); + abort(); /* $covered$ (impossible reliably) */ + min = (size_t)r_int < size ? (size_t)r_int : size; + out_buffer = &out_buffer[min]; + size -= min; + } /* Chain the hash algorithms: write '>' */ if (size) { @@ -135,7 +143,7 @@ librecrypt_add_algorithm(char *out_buffer, size_t size, char *augend, const char } /* Chain the hash algorithms: write `augment` and hash */ - r = librecrypt_crypt(out_buffer, size + (nul_term ? 1u : 0u), phrase, phraselen, augment, reserved); + r = librecrypt_crypt(out_buffer, nul_term ? size + 1u : 0u, phrase, phraselen, augment, reserved); if (r <= 0) { librecrypt_wipe(phrase, phraselen); free(phrase); @@ -157,13 +165,118 @@ librecrypt_add_algorithm(char *out_buffer, size_t size, char *augend, const char int main(void) { +#define SALT1 "ABCDabcdABCDabcdABCDabcdABCDabcdABCDabcdABCDabcdABCDabcdABCDabcd" +#define SALT2 "0123abcd0123ABCD0123abcd0123ABCD0123abcd0123ABCD0123abcd0123ABCD" +#define HASH1 "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTYVWXYZ/+" +#define ASTRA "*48" + + char buf[1024], phrase[sizeof(buf)], expected[sizeof(buf)], pad; + size_t i, min, phraselen; + int strict_pad; + const void *lut; + ssize_t r; + SET_UP_ALARM(); INIT_RESOURCE_TEST(); +#define CHECK(AUGEND, AUGMENT, RESULT)\ + do {\ + r = librecrypt_add_algorithm(buf, sizeof(buf), (AUGEND), (AUGMENT), NULL);\ + EXPECT(r > 0);\ + EXPECT((size_t)r == strlen(RESULT));\ + assert((size_t)r < sizeof(buf) + 1u);\ + EXPECT(!buf[r]);\ + EXPECT(!memcmp(buf, (RESULT), (size_t)r));\ + \ + for (i = (size_t)r + 2u;; i--) {\ + memset(buf, 99, sizeof(buf));\ + EXPECT(librecrypt_add_algorithm(buf, i, (AUGEND), (AUGMENT), NULL) == r);\ + if (!i)\ + break;\ + min = i - 1u < (size_t)r ? i - 1u : (size_t)r;\ + EXPECT(!buf[min]);\ + EXPECT(!memcmp(buf, (RESULT), min));\ + }\ + \ + EXPECT(librecrypt_add_algorithm(NULL, 0u, (AUGEND), (AUGMENT), NULL) == r);\ + \ + assert(sizeof(buf) > strlen(AUGEND));\ + stpcpy(buf, (AUGEND));\ + \ + EXPECT(librecrypt_add_algorithm(buf, sizeof(buf), buf, (AUGMENT), NULL) == r);\ + EXPECT(!buf[r]);\ + EXPECT(!memcmp(buf, (RESULT), (size_t)r));\ + \ + for (i = (size_t)r + 2u;; i--) {\ + stpcpy(buf, (AUGEND));\ + EXPECT(librecrypt_add_algorithm(buf, i, buf, (AUGMENT), NULL) == r);\ + if (!i)\ + break;\ + min = i - 1u < (size_t)r ? i - 1u : (size_t)r;\ + EXPECT(!buf[min]);\ + EXPECT(!memcmp(buf, (RESULT), min));\ + }\ + } while (0) + +#if defined(SUPPORT_ARGON2I) && defined(SUPPORT_ARGON2D) + + CHECK("$argon2d$v=16$m=8,t=1,p=1$*16$*40", "$argon2i$v=19$m=16,t=4,p=2$*18$*50", + "$argon2d$v=16$m=8,t=1,p=1$*16$*40>" "$argon2i$v=19$m=16,t=4,p=2$*18$*50"); + + CHECK("$argon2d$m=8,t=1,p=1$"SALT1"$*40", "$argon2i$m=8,t=4,p=2$"SALT2"$*50", + "$argon2d$m=8,t=1,p=1$"SALT1"$*40>" "$argon2i$m=8,t=4,p=2$"SALT2"$*50"); + + CHECK("$argon2d$m=8,t=1,p=1$"SALT1"$", "$argon2i$m=8,t=4,p=2$"SALT2"$", + "$argon2d$m=8,t=1,p=1$"SALT1"$>" "$argon2i$m=8,t=4,p=2$"SALT2"$"); + + CHECK("$argon2d$m=8,t=1,p=1$"SALT1"$*40", "$argon2i$m=8,t=4,p=2$"SALT2"$", + "$argon2d$m=8,t=1,p=1$"SALT1"$*40>" "$argon2i$m=8,t=4,p=2$"SALT2"$"); + + CHECK("$argon2d$m=8,t=1,p=1$"SALT1"$", "$argon2i$m=8,t=4,p=2$"SALT2"$*50", + "$argon2d$m=8,t=1,p=1$"SALT1"$>" "$argon2i$m=8,t=4,p=2$"SALT2"$*50"); + + CHECK("$argon2d$m=8,t=1,p=1$*16$", "$argon2i$m=8,t=4,p=2$"SALT2"$", + "$argon2d$m=8,t=1,p=1$*16$>" "$argon2i$m=8,t=4,p=2$"SALT2"$"); + + CHECK("$argon2d$m=8,t=1,p=1$*16$*50", "$argon2i$m=8,t=4,p=2$"SALT2"$", + "$argon2d$m=8,t=1,p=1$*16$*50>" "$argon2i$m=8,t=4,p=2$"SALT2"$"); + + CHECK("$argon2d$m=8,t=1,p=1$*16$", "$argon2i$m=8,t=4,p=2$"SALT2"$*60", + "$argon2d$m=8,t=1,p=1$*16$>" "$argon2i$m=8,t=4,p=2$"SALT2"$*60"); + + CHECK("$argon2d$m=8,t=1,p=1$"SALT1"$", "$argon2i$m=8,t=4,p=2$*20$", + "$argon2d$m=8,t=1,p=1$"SALT1"$>" "$argon2i$m=8,t=4,p=2$*20$"); + + CHECK("$argon2d$m=8,t=1,p=1$"SALT1"$*32", "$argon2i$m=8,t=4,p=2$*20$", + "$argon2d$m=8,t=1,p=1$"SALT1"$*32>" "$argon2i$m=8,t=4,p=2$*20$"); + + CHECK("$argon2d$m=8,t=1,p=1$"SALT1"$", "$argon2i$m=8,t=4,p=2$*20$*32", + "$argon2d$m=8,t=1,p=1$"SALT1"$>" "$argon2i$m=8,t=4,p=2$*20$*32"); + + lut = librecrypt_get_encoding("$argon2d$", sizeof("$argon2d$") - 1u, &pad, &strict_pad, 1); + assert(lut); + r = librecrypt_decode(phrase, sizeof(phrase), HASH1, strlen(HASH1), lut, pad, strict_pad); + assert(r > 0 && (size_t)r <= sizeof(phrase)); + phraselen = (size_t)r; + stpcpy(expected, "$argon2d$m=8,t=1,p=1$"SALT1"$"ASTRA">$argon2i$m=8,t=4,p=1$"SALT2"$"); + r = librecrypt_hash(&expected[strlen(expected)], sizeof(expected) - strlen(expected), + phrase, phraselen, "$argon2i$m=8,t=4,p=1$"SALT2"$*32", NULL); + assert(r > 0 && (size_t)r < sizeof(expected) - strlen(expected)); + assert(!expected[strlen(expected) + (size_t)r]); + CHECK("$argon2d$m=8,t=1,p=1$"SALT1"$"HASH1, "$argon2i$m=8,t=4,p=1$"SALT2"$*32", expected); + CHECK("$argon2d$m=8,t=1,p=1$"SALT1"$"HASH1, + "$argon2i$m=8,t=4,p=1$"SALT2"$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", expected); + +#endif + STOP_RESOURCE_TEST(); return 0; } #endif -/* TODO test */ +/* TODO test with hash but asterisk-salt on augend */ +/* TODO test with asterisk-salt on augment */ +/* TODO test with empty salt */ +/* TODO test with salts that are not multiple of 4 base64 characters */ +/* TODO test with hashes that are not multiple of 4 base64 characters */ diff --git a/librecrypt_fill_with_random_.c b/librecrypt_fill_with_random_.c index 0d8dc78..1361652 100644 --- a/librecrypt_fill_with_random_.c +++ b/librecrypt_fill_with_random_.c @@ -99,7 +99,7 @@ main(void) INIT_RESOURCE_TEST(); /* Check zero-request */ - EXPECT(librecrypt_fill_with_random_(NULL, 0, NULL, NULL) == 0); + EXPECT(librecrypt_fill_with_random_(NULL, 0u, NULL, NULL) == 0); /* Check default RNG */ EXPECT(librecrypt_fill_with_random_(buf1, sizeof(buf1), NULL, NULL) == 0); diff --git a/librecrypt_hash_.c b/librecrypt_hash_.c index 14c709b..05f1743 100644 --- a/librecrypt_hash_.c +++ b/librecrypt_hash_.c @@ -397,7 +397,7 @@ main(void) /* target settings_scratch */ errno = 0; libtest_set_alloc_failure_in(1u); - EXPECT(librecrypt_hash_(NULL, 0, "hello", 5u, ARGON2ID_PREFIX"*1000$", NULL, ASCII_CRYPT) == -1); + EXPECT(librecrypt_hash_(NULL, 0u, "hello", 5u, ARGON2ID_PREFIX"*1000$", NULL, ASCII_CRYPT) == -1); EXPECT(errno == ENOMEM); EXPECT(libtest_get_alloc_failure_in() == 0u); diff --git a/librecrypt_make_settings.c b/librecrypt_make_settings.c index 3efca4c..7688061 100644 --- a/librecrypt_make_settings.c +++ b/librecrypt_make_settings.c @@ -80,19 +80,19 @@ main(void) INIT_RESOURCE_TEST(); errno = 0; - EXPECT(librecrypt_make_settings(NULL, 0, ">", 0u, 0u, 0, NULL, NULL) == -1); + EXPECT(librecrypt_make_settings(NULL, 0u, ">", 0u, 0u, 0, NULL, NULL) == -1); EXPECT(errno == EINVAL); errno = 0; - EXPECT(librecrypt_make_settings(NULL, 0, "$argon2id$>", 0u, 0u, 0, NULL, NULL) == -1); + EXPECT(librecrypt_make_settings(NULL, 0u, "$argon2id$>", 0u, 0u, 0, NULL, NULL) == -1); EXPECT(errno == EINVAL); errno = 0; - EXPECT(librecrypt_make_settings(NULL, 0, ">$argon2id$", 0u, 0u, 0, NULL, NULL) == -1); + EXPECT(librecrypt_make_settings(NULL, 0u, ">$argon2id$", 0u, 0u, 0, NULL, NULL) == -1); EXPECT(errno == EINVAL); errno = 0; - EXPECT(librecrypt_make_settings(NULL, 0, "$argon2id$>$argon2id$", 0u, 0u, 0, NULL, NULL) == -1); + EXPECT(librecrypt_make_settings(NULL, 0u, "$argon2id$>$argon2id$", 0u, 0u, 0, NULL, NULL) == -1); EXPECT(errno == EINVAL); errno = 0; - EXPECT(librecrypt_make_settings(NULL, 0, "$~no~such~algorithm~$", 0u, 0u, 0, NULL, NULL) == -1); + EXPECT(librecrypt_make_settings(NULL, 0u, "$~no~such~algorithm~$", 0u, 0u, 0, NULL, NULL) == -1); EXPECT(errno == ENOSYS); #if defined(SUPPORT_ARGON2I) @@ -136,7 +136,7 @@ main(void) #endif if (any_supported) { - EXPECT(librecrypt_make_settings(NULL, 0, NULL, 0u, 0u, 0, NULL, NULL) > 0); + EXPECT(librecrypt_make_settings(NULL, 0u, NULL, 0u, 0u, 0, NULL, NULL) > 0); EXPECT(librecrypt_make_settings(buf, sizeof(buf), NULL, 0u, 0u, 0, NULL, NULL) > 0); if (any_salted) { @@ -169,7 +169,7 @@ main(void) EXPECT(strcmp(buf, buf2)); } else { errno = 0; - EXPECT(librecrypt_make_settings(NULL, 0, NULL, 0u, 0u, 0, NULL, NULL) == -1); + EXPECT(librecrypt_make_settings(NULL, 0u, NULL, 0u, 0u, 0, NULL, NULL) == -1); EXPECT(errno == ENOSYS); } diff --git a/libtest/common.h b/libtest/common.h index 54456d2..e4344a9 100644 --- a/libtest/common.h +++ b/libtest/common.h @@ -157,9 +157,9 @@ extern struct meminfo libtest_allocs_tail; extern int libtest_allocs_list_inited; extern atomic_flag libtest_allocs_list_spinlock; -extern int libtest_zero_on_alloc; -extern int libtest_expect_zeroed; -extern int libtest_malloc_accept_leakage; +extern _Thread_local int libtest_zero_on_alloc; +extern _Thread_local int libtest_expect_zeroed; +extern _Thread_local int libtest_malloc_accept_leakage; extern _Thread_local size_t libtest_malloc_internal_usage; extern _Thread_local size_t libtest_kill_malloc_tracking; diff --git a/libtest/globals.c b/libtest/globals.c index cf8164e..e5229a1 100644 --- a/libtest/globals.c +++ b/libtest/globals.c @@ -30,9 +30,9 @@ struct meminfo libtest_allocs_tail; int libtest_allocs_list_inited = 0; atomic_flag libtest_allocs_list_spinlock = ATOMIC_FLAG_INIT; -int libtest_zero_on_alloc = 0; -int libtest_expect_zeroed = 0; -int libtest_malloc_accept_leakage = 1; +_Thread_local int libtest_zero_on_alloc = 0; +_Thread_local int libtest_expect_zeroed = 0; +_Thread_local int libtest_malloc_accept_leakage = 1; _Thread_local size_t libtest_malloc_internal_usage = 0u; _Thread_local size_t libtest_kill_malloc_tracking = 0u; diff --git a/libtest/libtest.h b/libtest/libtest.h index 8f44114..e4b4098 100644 --- a/libtest/libtest.h +++ b/libtest/libtest.h @@ -7,18 +7,27 @@ /** * Start tracking resources which `libtest_check_no_leaks` * will detect if they are not released + * + * Memory resource tracking will only be started for the + * calling thread */ void libtest_start_tracking(void); /** * Stop tracking resources, so that `libtest_check_no_leaks` * will not detect if they are not released + * + * Memory resource tracking will only be stop for the + * calling thread */ void libtest_stop_tracking(void); /** * Check for resource leaks * + * Memory leaks are detected for all threads with + * tracking enabled, not just the calling thread + * * Any leak will be printed to standard error * * @return 1 if there was no leaks, 0 otherwise @@ -30,6 +39,8 @@ int libtest_check_no_leaks(void); * Make all overriden memory allocation functions * full the usable memory area with null bytes, * or disable this feature + * + * The setting applies only to the calling thread * * @param enabled 1 to enable, 0 to disable */ @@ -40,6 +51,8 @@ void libtest_force_zero_on_alloc(int enabled); * check that the entire usable memory area is filled * with null bytes, or disable this feature * + * The setting applies only to the calling thread + * * @param enabled 1 to enable, 0 to disable */ void libtest_expect_zeroed_on_free(int enabled); diff --git a/libtest/libtest_fd_tracking.c b/libtest/libtest_fd_tracking.c index d3c766c..25cea8f 100644 --- a/libtest/libtest_fd_tracking.c +++ b/libtest/libtest_fd_tracking.c @@ -11,6 +11,9 @@ struct fd { static size_t nopened = 0u; static struct fd *opened = NULL; +static atomic_flag spinlock = ATOMIC_FLAG_INIT; +static int tracking_state = -1; + static int cmp_fd(const void *av, const void *bv) @@ -60,6 +63,14 @@ libtest_fd_tracking(int action) int accept_memleak = libtest_malloc_accept_leakage; size_t i, j; char *path; + int old_tracking_state; + + SPINLOCK(spinlock); + old_tracking_state = tracking_state; + tracking_state = action; + SPINUNLOCK(spinlock); + if (old_tracking_state == action) + return 1; /* so libtest doesn't complain about us not zeroing before freeing, * and so it will not report memory leaks in fprintf from our diff --git a/libtest/libtest_free.c b/libtest/libtest_free.c index 3e84ba6..bdc214b 100644 --- a/libtest/libtest_free.c +++ b/libtest/libtest_free.c @@ -57,6 +57,16 @@ libtest_free(void *ptr, enum libtest_zero_check zero_checking) break; } } + if (!memory_zeroed && mem->backtrace) { + libtest_malloc_internal_usage++; + inside_free = 1; + fprintf(stderr, "Memory not zeroed out before deallocation: %p\n", ptr); +#ifdef WITH_BACKTRACE + libtest_print_backtrace(stderr, "\tAllocated at ", "\t at ", + 0u, mem->backtrace, NULL); +#endif + inside_free = 0; + } assert(memory_zeroed); } diff --git a/libtest/libtest_print_backtrace.c b/libtest/libtest_print_backtrace.c index e595131..7bc465c 100644 --- a/libtest/libtest_print_backtrace.c +++ b/libtest/libtest_print_backtrace.c @@ -19,6 +19,7 @@ libtest_print_backtrace(FILE *fp, const char *prefix, const char *indent, size_t unw_context_t context; Dwarf_Addr ip; size_t i; + unsigned int old_alarm; #if defined(HAVE_LINE_INFO) Dwfl_Callbacks callbacks; char *debuginfo_path = NULL; @@ -32,6 +33,9 @@ libtest_print_backtrace(FILE *fp, const char *prefix, const char *indent, size_t if (recursion_guard) return; + + old_alarm = alarm(1u); + saved_errno = errno; recursion_guard = 1; libtest_malloc_internal_usage++; @@ -121,6 +125,8 @@ out: libtest_malloc_internal_usage--; recursion_guard = 0; errno = saved_errno; + + alarm(old_alarm); } |
