aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile48
-rw-r--r--argon2/hash.c61
-rw-r--r--common.h33
-rw-r--r--config-fuzz-clang.mk14
-rw-r--r--librecrypt.h110
-rw-r--r--librecrypt_add_algorithm.33
-rw-r--r--librecrypt_add_algorithm.c22
-rw-r--r--librecrypt_chain_length.c17
-rw-r--r--librecrypt_check_settings_.c2
-rw-r--r--librecrypt_crypt.33
-rw-r--r--librecrypt_crypt.c35
-rw-r--r--librecrypt_decompose_chain.c34
-rw-r--r--librecrypt_decompose_chain1.c22
-rw-r--r--librecrypt_encode.325
-rw-r--r--librecrypt_encode.c29
-rw-r--r--librecrypt_hash.33
-rw-r--r--librecrypt_hash.c21
-rw-r--r--librecrypt_hash_.c35
-rw-r--r--librecrypt_hash_binary.33
-rw-r--r--librecrypt_next_algorithm.c25
-rw-r--r--librecrypt_settings_prefix.c21
-rw-r--r--libtest/common.h2
-rw-r--r--libtest/globals.c4
-rw-r--r--libtest/libtest.h2
-rw-r--r--libtest/libtest_alloc.c58
-rw-r--r--libtest/libtest_free.c10
-rw-r--r--libtest/mmap.c2
28 files changed, 554 insertions, 92 deletions
diff --git a/.gitignore b/.gitignore
index bb11397..637ddc6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,5 @@
*.gcov
*.gcno
*.gcda
+*.f
+*.fo
diff --git a/Makefile b/Makefile
index 40e6476..b96c46f 100644
--- a/Makefile
+++ b/Makefile
@@ -19,19 +19,28 @@ LIB_VERSION = $(LIB_MAJOR).$(LIB_MINOR)
LIB_NAME = recrypt
-OBJ_PUBLIC =\
+OBJ_PUBLIC_FUZZ =\
librecrypt_settings_prefix.o\
+
+OBJ_PUBLIC_NO_FUZZ =\
librecrypt_chain_length.o\
librecrypt_decompose_chain.o\
librecrypt_decompose_chain1.o\
- librecrypt_next_algorithm.o\
- librecrypt_encode.o\
- librecrypt_decode.o\
- librecrypt_get_encoding.o\
+ librecrypt_next_algorithm.o
+
+OBJ_PUBLIC_NO_FUZZ =\
librecrypt_wipe.o\
librecrypt_wipe_str.o\
librecrypt_equal_binary.o\
- librecrypt_equal.o\
+ librecrypt_equal.o
+
+OBJ_PUBLIC =\
+ $(OBJ_PUBLIC_FUZZ)\
+ $(OBJ_PUBLIC_DONT_FUZZ)\
+ $(OBJ_PUBLIC_NO_FUZZ)\
+ librecrypt_encode.o\
+ librecrypt_decode.o\
+ librecrypt_get_encoding.o\
librecrypt_realise_salts.o\
librecrypt_make_settings.o\
librecrypt_hash_binary.o\
@@ -63,6 +72,8 @@ HDR =\
LOBJ = $(OBJ:.o=.lo)
TOBJ = $(OBJ:.o=.to)
TEST = $(OBJ:.o=.t)
+FOBJ = $(OBJ_PUBLIC_FUZZ:.o=.fo)
+FUZZ = $(OBJ_PUBLIC_FUZZ:.o=.f)
MAN3 = $(OBJ_PUBLIC:.o=.3)
MAN7 = librecrypt.7
@@ -70,9 +81,9 @@ all:
include argon2/suffix.mk
-ALL_CFLAGS = $(CFLAGS) $(CFLAGS_MODULES)
-ALL_CPPFLAGS = $(CPPFLAGS) $(CPPFLAGS_MODULES)
-ALL_LDFLAGS = $(LDFLAGS) $(LDFLAGS_MODULES)
+ALL_CPPFLAGS = $(CPPFLAGS) $(CPPFLAGS_MODULES) $(FUZZED_CPPFLAGS)
+ALL_CFLAGS = $(CFLAGS) $(CFLAGS_MODULES) $(FUZZED_CFLAGS)
+ALL_LDFLAGS = $(LDFLAGS) $(LDFLAGS_MODULES) $(FUZZED_LDFLAGS)
TEST_INCLUDE_PREFIX = libtest/
include libtest/config.mk
@@ -83,6 +94,7 @@ $(OBJ): $(HDR)
$(LOBJ): $(HDR)
$(TOBJ): $(HDR) libtest/libtest.h
$(TEST): $(HDR) librecrypt.a libtest/libtest.a libtest/libtest.h
+$(FUZZ): $(HDR) librecrypt.a libtest/libtest.a libtest/libtest.h
.c.o:
$(CC) -c -o $@ $< $(ALL_CFLAGS) $(COV_CFLAGS) $(ALL_CPPFLAGS) $(COV_CPPFLAGS)
@@ -96,6 +108,12 @@ $(TEST): $(HDR) librecrypt.a libtest/libtest.a libtest/libtest.h
.to.t:
$(CC) -o $@ $< librecrypt.a libtest/libtest.a $(G) $(ALL_LDFLAGS) $(TEST_LDFLAGS) $(COV_LDFLAGS)
+.c.fo:
+ $(CC) -DTEST -DFUZZ -c -o $@ $< $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(FUZZ_CFLAGS) $(FUZZ_CPPFLAGS)
+
+.fo.f:
+ $(CC) -o $@ $< librecrypt.a libtest/libtest.a $(G) $(ALL_LDFLAGS) $(TEST_LDFLAGS) $(FUZZ_LDFLAGS)
+
librecrypt.a: $(OBJ)
@rm -f -- $@
$(AR) rc $@ $(OBJ)
@@ -117,6 +135,12 @@ check: $(TEST)
# (specially to use valgrind(1)) may limit what the test code
# is able to test
+fuzz: $(FUZZ)
+ @set -ex;\
+ for f in $(FUZZ); do\
+ $(FUZZ_PREFIX) ./$$f $(FUZZ_SUFFIX);\
+ done
+
install: librecrypt.a librecrypt.$(LIBEXT)
mkdir -p -- "$(DESTDIR)$(PREFIX)/lib"
mkdir -p -- "$(DESTDIR)$(PREFIX)/include"
@@ -144,10 +168,10 @@ clean:
+cd libtest && $(MAKE) clean
-rm -f -- *.o *.a *.lo *.su *.so *.so.* *.dll *.dylib
-rm -f -- *.gch *.gcov *.gcno *.gcda *.$(LIBEXT)
- -rm -f -- *.to *.t
+ -rm -f -- *.to *.t *.fo *.f
.SUFFIXES:
-.SUFFIXES: .lo .o .c .to .t
+.SUFFIXES: .lo .o .c .to .t .fo .f
-.PHONY: all check install uninstall clean
+.PHONY: all check fuzz install uninstall clean
.PHONY: libtest/libtest.a
diff --git a/argon2/hash.c b/argon2/hash.c
index 9c65e6a..a40e54c 100644
--- a/argon2/hash.c
+++ b/argon2/hash.c
@@ -74,7 +74,7 @@ librecrypt__argon2__hash(char *restrict out_buffer, size_t size, const char *phr
/* Decode salt */
if (!salt_encoded) /* this would be if asterisk-notation is used, but it is not */
abort(); /* $covered$ */
- r = librecrypt_decode(NULL, 0u, salt_encoded, saltlen, BASE64);
+ r = librecrypt_decode(NULL, 0u, salt_encoded, (size_t)saltlen, BASE64);
if (r < 0)
return -1; /* $covered$ (impossible) */
if (r > 0) {
@@ -90,7 +90,7 @@ librecrypt__argon2__hash(char *restrict out_buffer, size_t size, const char *phr
salt = malloc((size_t)r);
if (!salt)
return -1;
- if (librecrypt_decode(salt, (size_t)r, salt_encoded, saltlen, BASE64) != r)
+ if (librecrypt_decode(salt, (size_t)r, salt_encoded, (size_t)saltlen, BASE64) != r)
abort(); /* $covered$ (impossible) */
saltlen = (uintmax_t)r;
}
@@ -116,6 +116,19 @@ librecrypt__argon2__hash(char *restrict out_buffer, size_t size, const char *phr
/* Argon2 may require a larger buffer to work with for the hash than it outputs */
scratch_size = libar2_hash_buf_size(&params);
+#if SIZE_MAX > UINT32_MAX
+ /* $covered{$ (impossible) */
+ if (!scratch_size) {
+ errno = ENOMEM;
+ goto fail;
+ }
+ /* $covered}$ */
+#else
+ if (!scratch_size) {
+ errno = ENOMEM;
+ goto fail;
+ }
+#endif
if (scratch_size > size) {
scratch = malloc(scratch_size);
if (!scratch)
@@ -123,8 +136,12 @@ librecrypt__argon2__hash(char *restrict out_buffer, size_t size, const char *phr
}
/* Calculate hash */
+#ifndef FUZZ
if (libar2_hash(scratch ? scratch : out_buffer, REMOVE_CONST(phrase), len, &params, &ctx))
goto fail;
+#else
+ memset(scratch ? scratch : out_buffer, '5', scratch_size);
+#endif
if (scratch && out_buffer)
memcpy(out_buffer, scratch, MIN(params.hashlen, size));
@@ -146,7 +163,7 @@ fail:
free(scratch);
}
if (salt) {
- librecrypt_wipe(salt, saltlen);
+ librecrypt_wipe(salt, (size_t)saltlen);
free(salt);
}
errno = saved_errno;
@@ -198,7 +215,6 @@ check(const char *phrase, const char *settings, const char *hash, size_t hashlen
EXPECT(!memcmp(expected, buf, MIN(i, hashlen)));
CANARY_X_CHECK(buf, MIN(i, hashlen), scratchsize);
}
-
}
@@ -266,6 +282,11 @@ check(const char *phrase, const char *settings, const char *hash, size_t hashlen
} while (0)
+#define phony librecrypt_test_phony__
+extern void *volatile librecrypt_test_phony__;
+void *volatile librecrypt_test_phony__ = (void *)(uintptr_t)4096u;
+
+
int
main(void)
{
@@ -277,6 +298,38 @@ main(void)
#define GET_SCRATCH_SIZE(HASHLEN) ((HASHLEN) > 64u ? ((HASHLEN) + 63u) & ~31u : (HASHLEN))
#if defined(SUPPORT_ARGON2I)
+# if SIZE_MAX > UINT32_MAX
+ errno = 0;
+ EXPECT(librecrypt__argon2__hash(NULL, 0u, phony, (size_t)UINT32_MAX + 1u, "$argon2i$m=256,t=2,p=1$c29tZXNhbHQ$",
+ sizeof("$argon2i$m=256,t=2,p=1$c29tZXNhbHQ$"), NULL) == -1);
+ EXPECT(errno == EINVAL);
+#else
+ if (libtest_have_custom_malloc()) {
+ char conf[256];
+ int r;
+
+ r = snprintf(conf, sizeof(conf), "$argon2i$m=256,t=2,p=1$c29tZXNhbHQ$%*zu", (size_t)UINT32_MAX);
+ assert(r > 0);
+ assert(r < sizeof(conf));
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(librecrypt__argon2__hash(NULL, 0u, NULL, 0u, conf, strlen(conf), NULL) == -1);
+ EXPECT(errno == ENOMEM);
+ EXPECT(libtest_get_alloc_failure_in() == 0u);
+ libtest_set_alloc_failure_in(0u);
+
+ r = snprintf(conf, sizeof(conf), "$argon2i$m=256,t=2,p=1$c29tZXNhbHQ$%*zu", (size_t)UINT32_MAX + 1u);
+ assert(r > 0);
+ assert(r < sizeof(conf));
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(librecrypt__argon2__hash(NULL, 0u, NULL, 0u, conf, strlen(conf), NULL) == -1);
+ EXPECT(errno == ENOMEM);
+ EXPECT(libtest_get_alloc_failure_in() == 1u);
+ libtest_set_alloc_failure_in(0u);
+ }
+
+# endif
CHECK("password", "$argon2i$" "m=256,t=2,p=1$c29tZXNhbHQ$", 32, "/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY");
CHECK("password", "$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQ$", 32, "iekCn0Y3spW+sCcFanM2xBT63UP2sghkUoHLIUpWRS8");
CHECK_BAD("$argon2i$");
diff --git a/common.h b/common.h
index 717f43b..116ec17 100644
--- a/common.h
+++ b/common.h
@@ -1,4 +1,19 @@
/* See LICENSE file for copyright and license details. */
+#if defined(__clang__)
+# pragma clang diagnostic ignored "-Wunsafe-buffer-usage" /* completely broken */
+# pragma clang diagnostic ignored "-Wpadded" /* don't care */
+# pragma clang diagnostic ignored "-Wdisabled-macro-expansion" /* glibc issue */
+# pragma clang diagnostic ignored "-Wc11-extensions" /* glibc issue */
+# pragma clang diagnostic ignored "-Wpre-c11-compat" /* glibc issue */
+# pragma clang diagnostic ignored "-Wunknown-warning-option" /* ignoring -Wsuggest-attribute=const|pure */
+# pragma clang diagnostic ignored "-Wimplicit-void-ptr-cast" /* C++ warning, and we are in internal files */
+# pragma clang diagnostic ignored "-Wc++-keyword" /* C++ warning, and we are in internal files */
+# pragma clang diagnostic ignored "-Wc++-unterminated-string-initialization" /* Stupid C++ warning, and we are in internal files */
+#endif
+#if defined(__GNUC__)
+# pragma GCC diagnostic ignored "-Winline"
+#endif
+
#include "librecrypt.h"
#if defined(__linux__)
# include <sys/auxv.h>
@@ -15,20 +30,6 @@
#include <unistd.h>
-#if defined(__clang__)
-# pragma clang diagnostic ignored "-Wunsafe-buffer-usage" /* completely broken */
-# pragma clang diagnostic ignored "-Wpadded" /* don't care */
-# pragma clang diagnostic ignored "-Wdisabled-macro-expansion" /* glibc issue */
-# pragma clang diagnostic ignored "-Wc11-extensions" /* glibc issue */
-# pragma clang diagnostic ignored "-Wunknown-warning-option" /* ignoring -Wsuggest-attribute=const|pure */
-# pragma clang diagnostic ignored "-Wimplicit-void-ptr-cast" /* C++ warning, and we are in internal files */
-# pragma clang diagnostic ignored "-Wc++-keyword" /* C++ warning, and we are in internal files */
-#endif
-#if defined(__GNUC__)
-# pragma GCC diagnostic ignored "-Winline"
-#endif
-
-
#if defined(__GNUC__)
# define HIDDEN __attribute__((__visibility__("hidden")))
# define CONST __attribute__((__const__))
@@ -40,8 +41,8 @@
#endif
#define NONSTRING
-#if defined(__GNUC__) && !defined(__clang__)
-# if __GNUC__ >= 8
+#if defined(__GNUC__)
+# if __GNUC__ >= 8 || defined(__clang__)
# undef NONSTRING
# define NONSTRING __attribute__((__nonstring__))
# endif
diff --git a/config-fuzz-clang.mk b/config-fuzz-clang.mk
new file mode 100644
index 0000000..f5a1898
--- /dev/null
+++ b/config-fuzz-clang.mk
@@ -0,0 +1,14 @@
+CONFIGFILE_PROPER = config.mk
+include $(CONFIGFILE_PROPER)
+
+CC = $(CC_PREFIX)clang -std=c99
+
+SANITIZE = $(CLANG_SANITIZE)
+FUZZ_CFLAGS = -fsanitize=fuzzer
+FUZZ_LDFLAGS = -fsanitize=fuzzer
+FUZZED_CPPFLAGS = -DFUZZ
+
+all: fuzz
+
+# These configurations will modify the library code
+# so that it doesn't perform password hashing !!!!!
diff --git a/librecrypt.h b/librecrypt.h
index 5ffb111..48d2ff7 100644
--- a/librecrypt.h
+++ b/librecrypt.h
@@ -276,8 +276,18 @@ librecrypt_next_algorithm(char **hash)
* @param lut The encoding alphabet, consisting of 64 characters,
* repeated 4 times
* @param pad The padding character to use at the end; the NUL byte if none
- * @return The number of bytes that would have been written to `out_buffer`,
- * excluding the terminating NUL byte, if `size` was sufficiently large
+ * @return len_out The number of bytes that would have been written to `out_buffer`,
+ * excluding the terminating NUL byte, if `size` was sufficiently
+ * large; `SIZE_MAX` is returned on failure
+ *
+ * @throws EOVERFLOW The for value `*len_out` cannot be represented
+ * because it is greater than `SIZE_MAX` (`len` is
+ * too great)
+ *
+ * Despite being used as the failure indicator, `SIZE_MAX`
+ * is a legal return value on successful, is and returned
+ * (without modifying `errno`) when `pad` is the NUL byte
+ * and `len` is (`SIZE_MAX` - 3) / 4 * 3 + 2
*
* On successful completion, the N bytes is written to
* `out_buffer` where N is the lesser of `size` and 1 in
@@ -607,18 +617,19 @@ ssize_t librecrypt_make_settings(char *out_buffer, size_t size, const char *algo
* @return The number of bytes that would have been written to `out_buffer`
* if `size` was sufficiently large; -1 on failure
*
- * @throws EINVAL `reserved` is non-`NULL` (this case will be removed
- * once `reserved` as being used by the library)
- * @throws EINVAL `settings` is invalid (invalid algorithm configuration,
- * invalid configuration syntax, or the output from one
- * chained hash algorithm cannot be input the next algorithm
- * in the chain (either because of format or length issues))
- * @throws EINVAL `settings` uses asterisk-encoding to specify random salts
- * @throws ERANGE `len` is too large or too small for the the selected
- * initial algorithm in the algorithm chain
- * @throws ENOMEM Failed to allocate internal scratch memory
- * @throws ENOSYS A selected hash algorithm is either not recognised
- * disabled at compile-time
+ * @throws EINVAL `reserved` is non-`NULL` (this case will be removed
+ * once `reserved` as being used by the library)
+ * @throws EINVAL `settings` is invalid (invalid algorithm configuration,
+ * invalid configuration syntax, or the output from one
+ * chained hash algorithm cannot be input the next algorithm
+ * in the chain (either because of format or length issues))
+ * @throws EINVAL `settings` uses asterisk-encoding to specify random salts
+ * @throws ERANGE `len` is too large or too small for the the selected
+ * initial algorithm in the algorithm chain
+ * @throws EOVERFLOW The expected return value is greater than {SSIZE_MAX}
+ * @throws ENOMEM Failed to allocate internal scratch memory
+ * @throws ENOSYS A selected hash algorithm is either not recognised
+ * disabled at compile-time
*
* Any encountered `EINTR` is ignored
*
@@ -659,18 +670,19 @@ ssize_t librecrypt_hash_binary(char *restrict out_buffer, size_t size, const cha
* if `size` was sufficiently large, excluding a terminating
* NUL byte; -1 on failure
*
- * @throws EINVAL `reserved` is non-`NULL` (this case will be removed
- * once `reserved` as being used by the library)
- * @throws EINVAL `settings` is invalid (invalid algorithm configuration,
- * invalid configuration syntax, or the output from one
- * chained hash algorithm cannot be input the next algorithm
- * in the chain (either because of format or length issues))
- * @throws EINVAL `settings` uses asterisk-encoding to specify random salts
- * @throws ERANGE `len` is too large or too small for the the selected
- * initial algorithm in the algorithm chain
- * @throws ENOMEM Failed to allocate internal scratch memory
- * @throws ENOSYS A selected hash algorithm is either not recognised
- * disabled at compile-time
+ * @throws EINVAL `reserved` is non-`NULL` (this case will be removed
+ * once `reserved` as being used by the library)
+ * @throws EINVAL `settings` is invalid (invalid algorithm configuration,
+ * invalid configuration syntax, or the output from one
+ * chained hash algorithm cannot be input the next algorithm
+ * in the chain (either because of format or length issues))
+ * @throws EINVAL `settings` uses asterisk-encoding to specify random salts
+ * @throws ERANGE `len` is too large or too small for the the selected
+ * initial algorithm in the algorithm chain
+ * @throws EOVERFLOW The expected return value is greater than {SSIZE_MAX}
+ * @throws ENOMEM Failed to allocate internal scratch memory
+ * @throws ENOSYS A selected hash algorithm is either not recognised
+ * disabled at compile-time
*
* Any encountered `EINTR` is ignored
*
@@ -716,17 +728,18 @@ ssize_t librecrypt_hash(char *restrict out_buffer, size_t size, const char *phra
* if `size` was sufficiently large, excluding a terminating
* NUL byte; -1 on failure
*
- * @throws EINVAL `reserved` is non-`NULL` (this case will be removed
- * once `reserved` as being used by the library)
- * @throws EINVAL `settings` is invalid (invalid algorithm configuration,
- * invalid configuration syntax, or the output from one
- * chained hash algorithm cannot be input the next algorithm
- * in the chain (either because of format or length issues))
- * @throws ERANGE `len` is too large or too small for the the selected
- * initial algorithm in the algorithm chain
- * @throws ENOMEM Failed to allocate internal scratch memory
- * @throws ENOSYS A selected hash algorithm is either not recognised
- * disabled at compile-time
+ * @throws EINVAL `reserved` is non-`NULL` (this case will be removed
+ * once `reserved` as being used by the library)
+ * @throws EINVAL `settings` is invalid (invalid algorithm configuration,
+ * invalid configuration syntax, or the output from one
+ * chained hash algorithm cannot be input the next algorithm
+ * in the chain (either because of format or length issues))
+ * @throws ERANGE `len` is too large or too small for the the selected
+ * initial algorithm in the algorithm chain
+ * @throws EOVERFLOW The expected return value is greater than {SSIZE_MAX}
+ * @throws ENOMEM Failed to allocate internal scratch memory
+ * @throws ENOSYS A selected hash algorithm is either not recognised
+ * disabled at compile-time
*
* Any encountered `EINTR` is ignored
*
@@ -813,17 +826,18 @@ int librecrypt_test_supported(const char *phrase, size_t len, int text, const ch
* if `size` was sufficiently large, excluding a terminating
* NUL byte; -1 on failure
*
- * @throws EINVAL `reserved` is non-`NULL` (this case will be removed
- * once `reserved` as being used by the library)
- * @throws EINVAL `settings` is invalid (invalid algorithm configuration,
- * invalid configuration syntax, or the output from one
- * chained hash algorithm cannot be input the next algorithm
- * in the chain (either because of format or length issues))
- * @throws ERANGE `len` is too large or too small for the the selected
- * initial algorithm in the algorithm chain
- * @throws ENOMEM Failed to allocate internal scratch memory
- * @throws ENOSYS A selected hash algorithm is either not recognised
- * disabled at compile-time
+ * @throws EINVAL `reserved` is non-`NULL` (this case will be removed
+ * once `reserved` as being used by the library)
+ * @throws EINVAL `settings` is invalid (invalid algorithm configuration,
+ * invalid configuration syntax, or the output from one
+ * chained hash algorithm cannot be input the next algorithm
+ * in the chain (either because of format or length issues))
+ * @throws ERANGE `len` is too large or too small for the the selected
+ * initial algorithm in the algorithm chain
+ * @throws EOVERFLOW The expected return value is greater than {SSIZE_MAX}.
+ * @throws ENOMEM Failed to allocate internal scratch memory
+ * @throws ENOSYS A selected hash algorithm is either not recognised
+ * disabled at compile-time
*
* On successful completion, the N bytes is written to
* `out_buffer` where N is the lesser of `size` and 1 in
diff --git a/librecrypt_add_algorithm.3 b/librecrypt_add_algorithm.3
index 2c7b365..8eaabfd 100644
--- a/librecrypt_add_algorithm.3
+++ b/librecrypt_add_algorithm.3
@@ -97,6 +97,9 @@ is invalid.
.B ERANGE
The selected initial algorithm requires a different password length.
.TP
+.B EOVERFLOW
+The expected return value is greater than {SSIZE_MAX}.
+.TP
.B ENOMEM
Failed to allocate internal scratch memory.
.TP
diff --git a/librecrypt_add_algorithm.c b/librecrypt_add_algorithm.c
index 4c6520e..ad21ec4 100644
--- a/librecrypt_add_algorithm.c
+++ b/librecrypt_add_algorithm.c
@@ -62,6 +62,8 @@ librecrypt_add_algorithm(char *out_buffer, size_t size, const char *augend, cons
r_int = snprintf(out_buffer, size + 1u, "*%zu", hashsize2);
if (r_int < 2)
abort(); /* $covered$ (impossible reliably) */
+ if (ret > SIZE_MAX - (size_t)r_int)
+ abort(); /* $covered$ (impossible) */
ret += (size_t)r_int;
} else {
out_buffer[0u] = '\0';
@@ -79,11 +81,19 @@ librecrypt_add_algorithm(char *out_buffer, size_t size, const char *augend, cons
r_int = snprintf(NULL, 0u, "*%zu", hashsize2);
if (r_int < 2)
abort(); /* $covered$ (impossible reliably) */
+ if (ret > SIZE_MAX - (size_t)r_int)
+ abort(); /* $covered$ (impossible) */
ret += (size_t)r_int;
out:
if (nul_term)
out_buffer[0u] = '\0';
}
+ if (ret > (size_t)SSIZE_MAX) {
+ /* $covered{$ (manually) */
+ errno = EOVERFLOW;
+ return -1;
+ /* $covered}$ */
+ }
return (ssize_t)ret;
}
@@ -96,7 +106,9 @@ librecrypt_add_algorithm(char *out_buffer, size_t size, const char *augend, cons
r_int = 0;
}
- /* Measure `augent` and '>' in output */
+ /* Measure `augend` and '>' in output */
+ if (prefix1 > SIZE_MAX - 1u - (size_t)r_int)
+ abort(); /* $covered$ (impossible) */
ret = prefix1 + (size_t)r_int + 1u;
/* Decode the hash from base-64 to binary */
@@ -160,6 +172,14 @@ librecrypt_add_algorithm(char *out_buffer, size_t size, const char *augend, cons
abort(); /* $covered$ (impossible) */
return -1;
}
+ if (ret > (size_t)(SSIZE_MAX - r)) {
+ /* $covered{$ (manually) */
+ librecrypt_wipe(phrase, phraselen);
+ free(phrase);
+ errno = EOVERFLOW;
+ return -1;
+ /* $covered}$ */
+ }
ret += (size_t)r;
librecrypt_wipe(phrase, phraselen);
diff --git a/librecrypt_chain_length.c b/librecrypt_chain_length.c
index e43aaf9..364186a 100644
--- a/librecrypt_chain_length.c
+++ b/librecrypt_chain_length.c
@@ -7,6 +7,7 @@ extern inline size_t librecrypt_chain_length(const char *hash);
#else
+# ifndef FUZZ
int
@@ -29,4 +30,20 @@ main(void)
}
+# else
+
+
+extern volatile size_t discarded_return_value;
+volatile size_t discarded_return_value;
+
+int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ (void) size;
+ discarded_return_value = librecrypt_chain_length((const void *)data);
+ return 0;
+}
+
+
+# endif
#endif
diff --git a/librecrypt_check_settings_.c b/librecrypt_check_settings_.c
index 2c544b2..0cfed69 100644
--- a/librecrypt_check_settings_.c
+++ b/librecrypt_check_settings_.c
@@ -75,7 +75,7 @@ check_uint(const char *settings, size_t *off, size_t len, char min_first_digit,
* @param max The most allowed number of bytes
* @param allow_empty Whether the empty string is allowed
* (no encoded bytes and no asterisk-notation)
- * @param lut Alphabet reverse lookup table, shall map any valid
+ * @param dlut Alphabet reverse lookup table, shall map any valid
* character (except the padding character) to the value
* of that character in the encoding alphabet, and map
* any other character to the value `0xFF`
diff --git a/librecrypt_crypt.3 b/librecrypt_crypt.3
index 0237862..fd4b03a 100644
--- a/librecrypt_crypt.3
+++ b/librecrypt_crypt.3
@@ -93,6 +93,9 @@ is invalid.
is too large or too small for the selected
initial algorithm.
.TP
+.B EOVERFLOW
+The expected return value is greater than {SSIZE_MAX}.
+.TP
.B ENOMEM
Failed to allocate internal scratch memory.
.TP
diff --git a/librecrypt_crypt.c b/librecrypt_crypt.c
index d116f87..893c96f 100644
--- a/librecrypt_crypt.c
+++ b/librecrypt_crypt.c
@@ -98,7 +98,7 @@ check(const char *phrase, const char *settings, const char *chain, size_t chain_
int
main(void)
{
- char buf[1024], buf2[1024];
+ char buf[1024], buf2[1024], conf[256];
ssize_t r;
SET_UP_ALARM();
@@ -111,6 +111,39 @@ main(void)
#define GET_SCRATCH_SIZE(HASHLEN) ((HASHLEN) > 64u ? ((HASHLEN) + 63u) & ~31u : (HASHLEN))
#if defined(SUPPORT_ARGON2I)
+ r = snprintf(conf, sizeof(conf), "$argon2i$m=256,t=8,p=1$AAAABBBBCCCC$*%zu", SIZE_MAX / 4u * 3u + 3u);
+ assert(r > 0 && (size_t)r < sizeof(conf));
+ errno = 0;
+ EXPECT(librecrypt_crypt(NULL, 0u, NULL, 0u, conf, NULL) == -1);
+# if SIZE_MAX > UINT32_MAX
+ EXPECT(errno == EINVAL);
+# else
+ EXPECT(errno == EOVERFLOW);
+ if (libtest_have_custom_malloc()) {
+ libtest_pretend_allocation_successful = 1;
+ errno = 0;
+ EXPECT(librecrypt_crypt(buf, sizeof(buf), NULL, 0u, conf, NULL) == -1);
+ libtest_pretend_allocation_successful = 0;
+ EXPECT(errno == EOVERFLOW);
+ }
+# endif
+
+# if SIZE_MAX == UINT32_MAX
+ r = snprintf(conf, sizeof(conf), "$argon2i$m=256,t=8,p=1$AAAABBBBCCCC$*%zu", (SIZE_MAX / 4u * 3u) / 2u);
+ assert(r > 0 && (size_t)r < sizeof(conf));
+ errno = 0;
+ EXPECT(librecrypt_crypt(NULL, 0u, NULL, 0u, conf, NULL) == -1);
+ EXPECT(errno == EOVERFLOW);
+# endif
+
+# if SIZE_MAX == UINT32_MAX
+ r = snprintf(conf, sizeof(conf), "$argon2i$m=256,t=8,p=1$AAAABBBBCCCC$*%zu", SIZE_MAX / 4u * 3u);
+ assert(r > 0 && (size_t)r < sizeof(conf));
+ errno = 0;
+ EXPECT(librecrypt_crypt(NULL, 0u, NULL, 0u, conf, NULL) == -1);
+ EXPECT(errno == EOVERFLOW);
+# endif
+
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$");
diff --git a/librecrypt_decompose_chain.c b/librecrypt_decompose_chain.c
index 9db1d6c..aa69b85 100644
--- a/librecrypt_decompose_chain.c
+++ b/librecrypt_decompose_chain.c
@@ -7,6 +7,7 @@ extern inline size_t librecrypt_decompose_chain(char *hash, char **chain_out_arr
#else
+# ifndef FUZZ
#define HASH_1 "a$b"
@@ -182,4 +183,37 @@ main(void)
}
+# else
+
+
+extern volatile size_t discarded_return_value;
+volatile size_t discarded_return_value;
+
+int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ char **chain, *hash;
+ size_t chain_size;
+ if (!size)
+ return 0;
+ chain_size = (size_t)*data++;
+ size -= 1u;
+ if (chain_size) {
+ chain = malloc(chain_size * sizeof(*chain));
+ assert(chain);
+ } else {
+ chain = NULL;
+ }
+ hash = malloc(size + 1u);
+ assert(hash);
+ memcpy(hash, data, size);
+ hash[size] = '\0';
+ discarded_return_value = librecrypt_decompose_chain(hash, chain, chain_size);
+ free(hash);
+ free(chain);
+ return 0;
+}
+
+
+# endif
#endif
diff --git a/librecrypt_decompose_chain1.c b/librecrypt_decompose_chain1.c
index 6fc8a63..a084c13 100644
--- a/librecrypt_decompose_chain1.c
+++ b/librecrypt_decompose_chain1.c
@@ -7,6 +7,7 @@ extern inline size_t librecrypt_decompose_chain1(char *hash);
#else
+# ifndef FUZZ
#define CHECK(IN, OUT, N)\
@@ -43,4 +44,25 @@ main(void)
}
+# else
+
+
+extern volatile size_t discarded_return_value;
+volatile size_t discarded_return_value;
+
+int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ char *hash;
+ hash = malloc(size + 1u);
+ assert(hash);
+ memcpy(hash, data, size);
+ hash[size] = '\0';
+ discarded_return_value = librecrypt_decompose_chain1(hash);
+ free(hash);
+ return 0;
+}
+
+
+# endif
#endif
diff --git a/librecrypt_encode.3 b/librecrypt_encode.3
index 95862f8..c38b59a 100644
--- a/librecrypt_encode.3
+++ b/librecrypt_encode.3
@@ -57,10 +57,23 @@ function returns the number of bytes that
would have been written to
.IR out_buffer ,
excluding the terminating null byte.
+On failure,
+.I SIZE_MAX
+is returned and
+.IR errno
+is set to describe the error.
.SH ERRORS
The
.BR librecrypt_encode ()
+function will fail if:
+.TP
+.B EOVERFLOW
+.I ascii
+uses an invalid encoding.
+
+The
+.BR librecrypt_encode ()
function cannot fail.
.SH ATTRIBUTES
@@ -81,6 +94,18 @@ T} Async-signal safety AS-Safe
.TE
.sp
+.SH NOTES
+The value
+.I SIZE_MAX
+can be returned on both success and failure.
+.I errno
+is only modified on failure. For practical
+purposes, applications can treat
+.I SIZE_MAX
+as indicating an
+.I ENOMEM
+failure.
+
.SH HISTORY
The
.BR librecrypt_encode ()
diff --git a/librecrypt_encode.c b/librecrypt_encode.c
index ec371cc..df16895 100644
--- a/librecrypt_encode.c
+++ b/librecrypt_encode.c
@@ -27,7 +27,12 @@ librecrypt_encode(char *out_buffer, size_t size, const void *binary, size_t len,
* 3 bytes (24 bits) requires 4 base-64 characters (24 bits) exactly */
q = len / 3u;
r = len % 3u;
- n = q * 4u + (!r ? 0u : pad ? 4u : r + 1u);
+ n = !r ? 0u : pad ? 4u : r + 1u;
+ if (q > (SIZE_MAX - n) / 4u) {
+ errno = EOVERFLOW;
+ return SIZE_MAX;
+ }
+ n += q * 4u;
/* Just return the encoding length if no output is requested */
if (!size)
@@ -126,6 +131,11 @@ librecrypt_encode(char *out_buffer, size_t size, const void *binary, size_t len,
NONSTRING static const char lut[256u] = MAKE_ENCODING_LUT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
+#define phony librecrypt_test_phony__
+extern void *volatile librecrypt_test_phony__;
+void *volatile librecrypt_test_phony__ = (void *)(uintptr_t)4096u;
+
+
#define CHECK(BINARY, ASCII)\
check((BINARY), sizeof(BINARY) - 1u, (ASCII), sizeof(ASCII) - 1u)
@@ -193,6 +203,23 @@ main(void)
CHECK("zy[]y21 !", "enlbXXkyMSAh");
CHECK("{~|~}~~~\x7f\x7f", "e358fn1+fn5/fw");
+#if defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wstringop-overflow="
+#endif
+
+ EXPECT(librecrypt_encode(NULL, 0u, phony, (SIZE_MAX - 3u) / 4u * 3u + 1u, lut, '\0') == SIZE_MAX - 1u);
+ errno = 0;
+ EXPECT(librecrypt_encode(NULL, 0u, phony, (SIZE_MAX - 3u) / 4u * 3u + 2u, lut, '\0') == SIZE_MAX);
+ EXPECT(errno == 0);
+ errno = 0;
+ EXPECT(librecrypt_encode(NULL, 0u, phony, (SIZE_MAX - 3u) / 4u * 3u + 3u, lut, '\0') == SIZE_MAX);
+ EXPECT(errno == EOVERFLOW);
+
+#if defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif
+
STOP_RESOURCE_TEST();
return 0;
}
diff --git a/librecrypt_hash.3 b/librecrypt_hash.3
index 5bf32ea..9c864fe 100644
--- a/librecrypt_hash.3
+++ b/librecrypt_hash.3
@@ -97,6 +97,9 @@ uses asterisk-encoding to specify random salts.
is too large or too small for the selected
initial algorithm.
.TP
+.B EOVERFLOW
+The expected return value is greater than {SSIZE_MAX}.
+.TP
.B ENOMEM
Failed to allocate internal scratch memory.
.TP
diff --git a/librecrypt_hash.c b/librecrypt_hash.c
index 69b8dcf..050e0c1 100644
--- a/librecrypt_hash.c
+++ b/librecrypt_hash.c
@@ -119,11 +119,32 @@ check(const char *phrase, const char *settings, const char *chain, const char *h
int
main(void)
{
+ char conf[256];
+ int r;
+
SET_UP_ALARM();
INIT_RESOURCE_TEST();
#define GET_SCRATCH_SIZE(HASHLEN) ((HASHLEN) > 64u ? ((HASHLEN) + 63u) & ~31u : (HASHLEN))
#if defined(SUPPORT_ARGON2I)
+ r = snprintf(conf, sizeof(conf), "$argon2i$m=256,t=8,p=1$AAAABBBBCCCC$*%zu", SIZE_MAX / 4u * 3u + 3u);
+ assert(r > 0 && (size_t)r < sizeof(conf));
+ errno = 0;
+ EXPECT(librecrypt_hash(NULL, 0u, NULL, 0u, conf, NULL) == -1);
+# if SIZE_MAX > UINT32_MAX
+ EXPECT(errno == EINVAL);
+# else
+ EXPECT(errno == EOVERFLOW);
+ if (libtest_have_custom_malloc()) {
+ char buf[1024];
+ libtest_pretend_allocation_successful = 1;
+ errno = 0;
+ EXPECT(librecrypt_hash(buf, sizeof(buf), NULL, 0u, conf, NULL) == -1);
+ libtest_pretend_allocation_successful = 0;
+ EXPECT(errno == EOVERFLOW);
+ }
+# endif
+
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$");
diff --git a/librecrypt_hash_.c b/librecrypt_hash_.c
index 473b738..cc1ae93 100644
--- a/librecrypt_hash_.c
+++ b/librecrypt_hash_.c
@@ -142,8 +142,7 @@ next:
digit = (size_t)(settings[i] - '0');
if (hash_size > (SIZE_MAX - digit) / 10u)
goto einval;
- hash_size *= 10u;
- hash_size += digit;
+ hash_size = hash_size * 10u + digit;
}
if (!hash_size)
goto einval;
@@ -173,7 +172,9 @@ next:
remainder = hash_size % 4u;
if (remainder == 1u)
goto einval;
- hash_size = quotient * 3u + (remainder ? remainder - 1u : 0u);
+ hash_size = quotient * 3u;
+ if (remainder)
+ hash_size += remainder - 1u;
}
/* For `librecrypt_crypt`: copy hash configurations to output */
@@ -187,6 +188,8 @@ next:
if (min)
memcpy(out_buffer, settings, min);
out_buffer = &out_buffer[min];
+ if (ret > SIZE_MAX - prefix)
+ abort(); /* $covered$ (impossible) */
ret += prefix;
}
@@ -198,6 +201,7 @@ next:
free(phrase_scratches[phrase_scratch_i]);
phrase_scratches[phrase_scratch_i] = NULL;
phrase_scratch_sizes[phrase_scratch_i] = 0u;
+ errno = ENOMEM;
goto fail;
}
phrase_scratches[phrase_scratch_i] = new;
@@ -233,6 +237,8 @@ next:
if (action == BINARY_HASH) {
/* Binary output: we already have the has in binary,
* so yes add the length to the return value */
+ if (ret != 0u)
+ abort(); /* $covered$ (impossible) */
ret += hash_size;
} else if (!size) {
/* ASCII hash but not output: just calculate the
@@ -244,6 +250,8 @@ next:
else
ascii_len += 1u; /* 3n+m bytes: 4n+m+1 chars, unless m=0 */
}
+ if (hash_size / 3u > (SIZE_MAX - ascii_len) / 4u)
+ goto eoverflow; /* $covered$ (on 32-bit, impossible on wider) */
ascii_len += hash_size / 3u * 4u;
goto include_ascii;
} else {
@@ -252,10 +260,18 @@ next:
ascii_len = librecrypt_encode(out_buffer, size,
size < hash_size ? phrase_scratches[phrase_scratch_i] : out_buffer,
hash_size, algo->encoding_lut, algo->strict_pad ? algo->pad : '\0');
+ /* SIZE_MAX could mean success, however we will
+ * fail when convert `ret` from size_t to ssize_t,
+ * so we can treat SIZE_MAX as failure even when
+ * it's a success */
+ if (ascii_len == SIZE_MAX)
+ goto eoverflow; /* $covered$ (manually) */
include_ascii:
min = size ? MIN(size - 1u, ascii_len) : 0u;
out_buffer = &out_buffer[min];
size -= min;
+ if (ret > SIZE_MAX - ascii_len)
+ goto eoverflow; /* $covered$ (on 32-bit) */
ret += ascii_len;
}
} else {
@@ -275,6 +291,8 @@ next:
/* For `librecrypt_crypt`: add '>' to the password hash string */
if (action == ASCII_CRYPT) {
+ if (ret == SIZE_MAX)
+ abort(); /* $covered$ (impossible) */
ret += 1u;
if (size > 1u) {
*out_buffer++ = LIBRECRYPT_ALGORITHM_LINK_DELIMITER;
@@ -304,8 +322,19 @@ next:
if (size && action != BINARY_HASH)
out_buffer[0] = '\0';
+ if (ret > (size_t)SSIZE_MAX) {
+ /* $covered{$ (manually) */
+ errno = EOVERFLOW;
+ return -1;
+ /* $covered}$ */
+ }
return (ssize_t)ret;
+ /* $covered{$ (since we have covered gotos to this label) */
+eoverflow:
+ errno = EOVERFLOW;
+ goto fail;
+ /* $covered}$ */
einval:
errno = EINVAL;
fail:
diff --git a/librecrypt_hash_binary.3 b/librecrypt_hash_binary.3
index 720b82d..855906a 100644
--- a/librecrypt_hash_binary.3
+++ b/librecrypt_hash_binary.3
@@ -100,6 +100,9 @@ uses asterisk-encoding to specify random salts.
is too large or too small for the selected
initial algorithm.
.TP
+.B EOVERFLOW
+The expected return value is greater than {SSIZE_MAX}.
+.TP
.B ENOMEM
Failed to allocate internal scratch memory.
.TP
diff --git a/librecrypt_next_algorithm.c b/librecrypt_next_algorithm.c
index 937a2f8..831f52e 100644
--- a/librecrypt_next_algorithm.c
+++ b/librecrypt_next_algorithm.c
@@ -7,6 +7,7 @@ extern inline char *librecrypt_next_algorithm(char **hash);
#else
+# ifndef FUZZ
static void
@@ -85,4 +86,28 @@ main(void)
}
+# else
+
+
+int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ char *hash, *r;
+ size_t sum = 0u;
+ hash = malloc(size + 1u);
+ assert(hash);
+ memcpy(hash, data, size);
+ hash[size] = '\0';
+ for (;;) {
+ r = librecrypt_next_algorithm(&hash);
+ if (!r)
+ break;
+ sum += strlen(r) + 1u;
+ }
+ EXPECT(sum == size + 1u);
+ return 0;
+}
+
+
+# endif
#endif
diff --git a/librecrypt_settings_prefix.c b/librecrypt_settings_prefix.c
index a2a75f9..0d78c23 100644
--- a/librecrypt_settings_prefix.c
+++ b/librecrypt_settings_prefix.c
@@ -55,6 +55,7 @@ out:
#else
+# ifndef FUZZ
#define CHECK_NULL(PREFIX, SUFFIX)\
@@ -154,4 +155,24 @@ main(void)
}
+# else
+
+
+int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ char *hash;
+ size_t r;
+ hash = malloc(size + 1u);
+ assert(hash);
+ memcpy(hash, data, size);
+ hash[size] = '\0';
+ r = librecrypt_settings_prefix(hash, &(size_t){0u});
+ EXPECT(librecrypt_settings_prefix(hash, NULL) == r);
+ free(hash);
+ return 0;
+}
+
+
+# endif
#endif
diff --git a/libtest/common.h b/libtest/common.h
index ab59bab..d6557b1 100644
--- a/libtest/common.h
+++ b/libtest/common.h
@@ -160,6 +160,8 @@ extern struct meminfo libtest_allocs_head;
extern struct meminfo libtest_allocs_tail;
extern int libtest_allocs_list_inited;
extern atomic_flag libtest_allocs_list_spinlock;
+extern void *libtest_pretend_list[128];
+extern size_t libtest_npretends;
extern _Thread_local int libtest_zero_on_alloc;
extern _Thread_local int libtest_expect_zeroed;
diff --git a/libtest/globals.c b/libtest/globals.c
index e5229a1..6e79402 100644
--- a/libtest/globals.c
+++ b/libtest/globals.c
@@ -25,10 +25,14 @@ volatile int libtest_mmap_is_custom = -1;
volatile int libtest_munmap_is_custom = -1;
volatile int libtest_mremap_is_custom = -1;
+volatile int libtest_pretend_allocation_successful = 0;
+
struct meminfo libtest_allocs_head;
struct meminfo libtest_allocs_tail;
int libtest_allocs_list_inited = 0;
atomic_flag libtest_allocs_list_spinlock = ATOMIC_FLAG_INIT;
+void *libtest_pretend_list[128];
+size_t libtest_npretends = 0u;
_Thread_local int libtest_zero_on_alloc = 0;
_Thread_local int libtest_expect_zeroed = 0;
diff --git a/libtest/libtest.h b/libtest/libtest.h
index e4b4098..6eadd6f 100644
--- a/libtest/libtest.h
+++ b/libtest/libtest.h
@@ -437,6 +437,8 @@ size_t libtest_get_alloc_failure_in(void);
void libtest_set_alloc_failure_in(size_t n);
+extern volatile int libtest_pretend_allocation_successful;
+
extern const unsigned char *volatile libtest_random_pattern;
extern volatile size_t libtest_random_pattern_length;
extern volatile size_t libtest_random_pattern_offset;
diff --git a/libtest/libtest_alloc.c b/libtest/libtest_alloc.c
index 96aa132..022881a 100644
--- a/libtest/libtest_alloc.c
+++ b/libtest/libtest_alloc.c
@@ -53,8 +53,8 @@ mmap_anon(size_t size)
}
-void *
-libtest_alloc(struct meminfo *meminfo)
+static void *
+try_alloc(struct meminfo *meminfo)
{
static _Thread_local int recursion_guard = 0;
void *base_ptr, *ret_ptr;
@@ -206,6 +206,60 @@ libtest_alloc(struct meminfo *meminfo)
}
+void *
+libtest_alloc(struct meminfo *meminfo)
+{
+ void *ptr;
+ size_t reqsize;
+ ptr = try_alloc(meminfo);
+ if (!ptr && libtest_pretend_allocation_successful) {
+ reqsize = meminfo->requested_alloc_size;
+ meminfo->requested_alloc_size = 0u;
+ ptr = try_alloc(meminfo);
+ assert(ptr != NULL);
+ meminfo->requested_alloc_size = reqsize;
+ GET_MEMINFO(ptr)->requested_alloc_size = reqsize;
+ SPINLOCK(libtest_allocs_list_spinlock);
+ if (libtest_npretends >= ELEMSOF(libtest_pretend_list))
+ SPINUNLOCK(libtest_allocs_list_spinlock);
+ assert(libtest_npretends < ELEMSOF(libtest_pretend_list));
+ libtest_pretend_list[libtest_npretends++] = ptr;
+ SPINUNLOCK(libtest_allocs_list_spinlock);
+ }
+ return ptr;
+}
+
+
+void *
+(memset)(void *s, int c, size_t n)
+{
+ unsigned char *us = s, uc = (unsigned char)c;
+ size_t i;
+
+ SPINLOCK(libtest_allocs_list_spinlock);
+ for (i = 0u; i < libtest_npretends; i++) {
+ if (libtest_pretend_list[i] == s) {
+ /* We only do this for pointers in the pretend list
+ * because we must know that it is actually a
+ * pointer created with libtest_alloc, and not
+ * mmap(2) or stack-allocated or statically
+ * allocated buffer; and that's why libtest_pretend_list
+ * exist instead of being inferred or a flag. */
+ if (n > GET_MEMINFO(s)->usable_alloc_size)
+ n = GET_MEMINFO(s)->usable_alloc_size;
+ break;
+ }
+ }
+ SPINUNLOCK(libtest_allocs_list_spinlock);
+
+ for (i = 0u; i < n; i++)
+ us[i] = uc;
+
+ return us;
+}
+
+
+
#else
diff --git a/libtest/libtest_free.c b/libtest/libtest_free.c
index 5592e0a..d46218e 100644
--- a/libtest/libtest_free.c
+++ b/libtest/libtest_free.c
@@ -39,12 +39,18 @@ libtest_free(void *ptr, enum libtest_zero_check zero_checking)
assert(mem->origin != FROM_MMAP_ANON);
/* Delist allocation */
+ SPINLOCK(libtest_allocs_list_spinlock);
if (!libtest_kill_malloc_tracking) {
- SPINLOCK(libtest_allocs_list_spinlock);
mem->prev->next = mem->next;
mem->next->prev = mem->prev;
- SPINUNLOCK(libtest_allocs_list_spinlock);
}
+ for (i = 0u; i < libtest_npretends; i++) {
+ if (libtest_pretend_list[i] == ptr) {
+ libtest_pretend_list[i] = libtest_pretend_list[--libtest_npretends];
+ break;
+ }
+ }
+ SPINUNLOCK(libtest_allocs_list_spinlock);
/* Check memory is zeroed */
if (zero_checking && libtest_expect_zeroed && !mem->accept_leakage) {
diff --git a/libtest/mmap.c b/libtest/mmap.c
index c3b3067..3bd3cb3 100644
--- a/libtest/mmap.c
+++ b/libtest/mmap.c
@@ -8,7 +8,7 @@
#ifdef SYS_mmap2
-# define IF_MMAP2(A) (A)
+# define IF_MMAP2(A) do { A; } while (0)
#else
# define IF_MMAP2(A) ((void)0)
#endif