Skip to content

Commit d02de5f

Browse files
committed
unit_tests: cover md5/sha1 hashing and EVP_MD cache thread-safety
Add coverage for the shared EVP_MD cache introduced by the per-hash provider-fetch fix: - test_hash_md5_sha1 verifies cl_hash_init/cl_update_hash/cl_finish_hash returns correct MD5 and SHA1 digests, skipping an algorithm that is unavailable under a strict FIPS policy rather than failing. - test_hash_cache_threadsafe hammers cl_hash_init() from multiple threads to exercise the cache and its mutex under contention.
1 parent a980176 commit d02de5f

1 file changed

Lines changed: 95 additions & 0 deletions

File tree

unit_tests/check_clamav.c

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@
3535

3636
#include "checks.h"
3737

38+
#ifdef CL_THREAD_SAFE
39+
#include <pthread.h>
40+
#endif
41+
3842
static int fpu_words = FPU_ENDIAN_INITME;
3943
#define NO_FPU_ENDIAN (fpu_words == FPU_ENDIAN_UNKNOWN)
4044
#define EA06_SCAN strstr(file, "clam.ea06.exe")
@@ -1693,6 +1697,93 @@ START_TEST(test_sha2_256)
16931697
}
16941698
END_TEST
16951699

1700+
START_TEST(test_hash_md5_sha1)
1701+
{
1702+
/* The incremental cl_hash_* API is routed through the shared EVP_MD cache
1703+
* by this change; verify it still returns correct MD5/SHA1 digests for the
1704+
* "abc" vectors. An algorithm unavailable under a strict FIPS policy (e.g.
1705+
* MD5) yields a NULL context and is skipped rather than failing the test. */
1706+
static const uint8_t abc[] = {'a', 'b', 'c'};
1707+
static const uint8_t md5_abc[MD5_HASH_SIZE] = {
1708+
0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0,
1709+
0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72};
1710+
static const uint8_t sha1_abc[SHA1_HASH_SIZE] = {
1711+
0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e,
1712+
0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d};
1713+
uint8_t out[SHA256_HASH_SIZE];
1714+
void *ctx;
1715+
1716+
ctx = cl_hash_init("md5");
1717+
if (NULL != ctx) {
1718+
cl_update_hash(ctx, abc, sizeof(abc));
1719+
cl_finish_hash(ctx, out);
1720+
ck_assert_msg(!memcmp(out, md5_abc, MD5_HASH_SIZE), "md5(\"abc\") mismatch");
1721+
}
1722+
1723+
ctx = cl_hash_init("sha1");
1724+
if (NULL != ctx) {
1725+
cl_update_hash(ctx, abc, sizeof(abc));
1726+
cl_finish_hash(ctx, out);
1727+
ck_assert_msg(!memcmp(out, sha1_abc, SHA1_HASH_SIZE), "sha1(\"abc\") mismatch");
1728+
}
1729+
}
1730+
END_TEST
1731+
1732+
#ifdef CL_THREAD_SAFE
1733+
#define HASH_TS_THREADS 8
1734+
#define HASH_TS_ITERS 2000
1735+
struct hash_ts_arg {
1736+
const uint8_t *expect; /* sha2-256("abc") == res256[0] */
1737+
int failed;
1738+
};
1739+
static void *hash_ts_worker(void *arg)
1740+
{
1741+
struct hash_ts_arg *a = (struct hash_ts_arg *)arg;
1742+
static const uint8_t abc[] = {'a', 'b', 'c'};
1743+
uint8_t out[SHA256_HASH_SIZE];
1744+
unsigned i;
1745+
1746+
for (i = 0; i < HASH_TS_ITERS; i++) {
1747+
void *ctx = cl_hash_init("sha2-256");
1748+
if (NULL == ctx) {
1749+
a->failed = 1;
1750+
return NULL;
1751+
}
1752+
cl_update_hash(ctx, abc, sizeof(abc));
1753+
cl_finish_hash(ctx, out);
1754+
if (0 != memcmp(out, a->expect, SHA256_HASH_SIZE)) {
1755+
a->failed = 1;
1756+
return NULL;
1757+
}
1758+
}
1759+
return NULL;
1760+
}
1761+
1762+
START_TEST(test_hash_cache_threadsafe)
1763+
{
1764+
/* Concurrently hammer cl_hash_init() to exercise the process-global EVP_MD
1765+
* cache added by the per-hash provider-fetch fix, catching races on the
1766+
* cache and its mutex (run under Valgrind/TSan in CI). */
1767+
pthread_t th[HASH_TS_THREADS];
1768+
struct hash_ts_arg args[HASH_TS_THREADS];
1769+
int i, failed = 0;
1770+
1771+
for (i = 0; i < HASH_TS_THREADS; i++) {
1772+
args[i].expect = res256[0];
1773+
args[i].failed = 0;
1774+
ck_assert_msg(0 == pthread_create(&th[i], NULL, hash_ts_worker, &args[i]),
1775+
"pthread_create failed");
1776+
}
1777+
for (i = 0; i < HASH_TS_THREADS; i++) {
1778+
pthread_join(th[i], NULL);
1779+
if (args[i].failed)
1780+
failed = 1;
1781+
}
1782+
ck_assert_msg(0 == failed, "concurrent cl_hash_init(sha2-256) returned a wrong digest or NULL context");
1783+
}
1784+
END_TEST
1785+
#endif
1786+
16961787
START_TEST(test_sanitize_path)
16971788
{
16981789
const char *unsanitized = NULL;
@@ -2041,6 +2132,10 @@ static Suite *test_cli_suite(void)
20412132
suite_add_tcase(s, tc_cli_dsig);
20422133
tcase_add_loop_test(tc_cli_dsig, test_cli_dsig, 0, dsig_tests_cnt);
20432134
tcase_add_test(tc_cli_dsig, test_sha2_256);
2135+
tcase_add_test(tc_cli_dsig, test_hash_md5_sha1);
2136+
#ifdef CL_THREAD_SAFE
2137+
tcase_add_test(tc_cli_dsig, test_hash_cache_threadsafe);
2138+
#endif
20442139

20452140
suite_add_tcase(s, tc_cli_assorted);
20462141
tcase_add_test(tc_cli_assorted, test_sanitize_path);

0 commit comments

Comments
 (0)