]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
lib:replace: Add test for memset_explicit()
authorDouglas Bagnall <douglas.bagnall@catalyst.net.nz>
Thu, 30 Oct 2025 15:01:36 +0000 (16:01 +0100)
committerAndreas Schneider <asn@cryptomilk.org>
Tue, 11 Nov 2025 13:46:42 +0000 (13:46 +0000)
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andreas Schneider <asn@samba.org>
lib/replace/tests/test_memset_explicit.c [new file with mode: 0644]
lib/replace/wscript
selftest/tests.py

diff --git a/lib/replace/tests/test_memset_explicit.c b/lib/replace/tests/test_memset_explicit.c
new file mode 100644 (file)
index 0000000..4e56d7a
--- /dev/null
@@ -0,0 +1,99 @@
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#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);
+}
index 1a78bf55f2a22bd40bb3300f1a08bfbfc8b17cb3..1318f887c77b7604dce8ae1370305713f860d8fe 100644 (file)
@@ -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}',
index f466dbb9feeff22756643e04d33d41543c743e81..4838bfd176f3d6c5b39f68587434f9b3029d5171 100644 (file)
@@ -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")])