summaryrefslogtreecommitdiff
path: root/lib/replace
diff options
context:
space:
mode:
authorDouglas Bagnall <douglas.bagnall@catalyst.net.nz>2025-10-30 16:01:36 +0100
committerAndreas Schneider <asn@cryptomilk.org>2025-11-11 13:46:42 +0000
commit2b17c9816d4373eba365de803eec10435ea038d4 (patch)
treedfaad16ccaf8eda64b587d38e97bebe8aaa77324 /lib/replace
parentef08be24e9114b4477cc2b3f7a28a816ec66802c (diff)
downloadsamba-2b17c9816d4373eba365de803eec10435ea038d4.tar.gz
samba-2b17c9816d4373eba365de803eec10435ea038d4.tar.bz2
samba-2b17c9816d4373eba365de803eec10435ea038d4.zip
lib:replace: Add test for memset_explicit()
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> Reviewed-by: Andreas Schneider <asn@samba.org>
Diffstat (limited to 'lib/replace')
-rw-r--r--lib/replace/tests/test_memset_explicit.c99
-rw-r--r--lib/replace/wscript5
2 files changed, 104 insertions, 0 deletions
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 <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);
+}
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}',