From: Douglas Bagnall Date: Thu, 30 Oct 2025 15:01:36 +0000 (+0100) Subject: lib:replace: Add test for memset_explicit() X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2b17c9816d4373eba365de803eec10435ea038d4;p=thirdparty%2Fsamba.git lib:replace: Add test for memset_explicit() Signed-off-by: Douglas Bagnall Reviewed-by: Andreas Schneider --- diff --git a/lib/replace/tests/test_memset_explicit.c b/lib/replace/tests/test_memset_explicit.c new file mode 100644 index 00000000000..4e56d7a9aee --- /dev/null +++ b/lib/replace/tests/test_memset_explicit.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include + +#include "lib/replace/replace.h" + + +/* + * To check that a memset_explicit string is being memset when it + * appears unused, we meed to be sneaky in our check -- otherwise the + * check counts as a use. + * + * We are sneaky by using a function that seens to take an int + * argument which is really a pointer, and we hide that it is a + * pointer by masking it. + * + * For these tests we don't use talloc because the talloc magic gets + * in the way a little bit. + */ + +#define MASK 0x12345678 + +__attribute__((noinline)) +static void check_memset_explicit(intmax_t p, const char *expected, size_t len) +{ + size_t i; + char *secret = (char *) (p ^ MASK); + for (i = 0; i < len; i++) { + assert_int_equal(secret[i], expected[i]); + } +} + + +__attribute__((noinline)) +static char *get_secret(off_t offset) +{ + char * secret = malloc(7 + offset); + memset(secret, 0, 7 + offset); + memcpy(secret + offset, "secret", 7); + /* avoiding *this* being elided */ + print_message("secret is '%s'\n", secret); + asm(""); + return secret; +} + + +static void test_memset_explicit(void ** state) +{ + uintptr_t p; + char zeros[7] = {0}; + char *secret = get_secret(0); + p = ((uintptr_t)secret) ^ MASK; + memset_explicit(secret, 'o', 3); + check_memset_explicit(p, "oooret", 7); + memset_explicit(secret, 0, 7); + check_memset_explicit(p, zeros, 7); + free(secret); +} + +static void test_memset_explicit_double_alloc(void ** state) +{ + size_t i, found; + uintptr_t p, q; + char *secret = get_secret(20); + p = (uintptr_t)secret ^ MASK; + memset_explicit(secret, 'x', 23); + free(secret); + /* + * Now we malloc the same size again, and hope we got the + * block we just freed. + */ + found = 0; + for (i = 0; i < 1000; i++) { + secret = malloc(27); + q = (uintptr_t)secret ^ MASK; + if (q == p) { + q = (uintptr_t)(secret + 20) ^ MASK; + check_memset_explicit(q, "xxxret", 7); + found ++; + } + free(secret); + } + print_message("found freed pointer %zu/1000 times \n", + found); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_memset_explicit), + cmocka_unit_test(test_memset_explicit_double_alloc), + }; + if (! isatty(1)) { + cmocka_set_message_output(CM_OUTPUT_SUBUNIT); + } + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/lib/replace/wscript b/lib/replace/wscript index 1a78bf55f2a..1318f887c77 100644 --- a/lib/replace/wscript +++ b/lib/replace/wscript @@ -973,6 +973,11 @@ def build(bld): deps='replace replace-test', install=False) + bld.SAMBA_BINARY('test_memset_explicit', + source='tests/test_memset_explicit.c', + deps='cmocka replace', + for_selftest=True) + # build replacements for stdint.h and stdbool.h if needed bld.SAMBA_GENERATOR('replace_stdint_h', rule='cp ${SRC} ${TGT}', diff --git a/selftest/tests.py b/selftest/tests.py index f466dbb9fee..4838bfd176f 100644 --- a/selftest/tests.py +++ b/selftest/tests.py @@ -543,6 +543,12 @@ plantestsuite("samba.unittests.smb1cli_session", "none", plantestsuite("samba.unittests.smb_util_translate", "none", [os.path.join(bindir(), "default/libcli/smb/test_util_translate")]) +plantestsuite( + "samba.unittests.memset_explicit", + "none", + [os.path.join(bindir(), "default/lib/replace/test_memset_explicit")], +) + plantestsuite("samba.unittests.talloc_keep_secret", "none", [os.path.join(bindir(), "default/lib/util/test_talloc_keep_secret")])