diff --git a/src/lib/malloc/map_anon.c b/src/lib/malloc/map_anon.c
index 2fc6e89ea2..5dac5256a6 100644
--- a/src/lib/malloc/map_anon.c
+++ b/src/lib/malloc/map_anon.c
@@ -107,6 +107,29 @@ nodump_mem(void *mem, size_t sz)
#endif
}
+#ifdef TOR_UNIT_TESTS
+static unsigned last_anon_map_noinherit = ~0;
+/* Testing helper: return the outcome of the last call to noinherit_mem():
+ * 0 if it did no good; 1 if it caused the memory not to be inherited, and
+ * 2 if it caused the memory to be cleared on fork */
+unsigned
+get_last_anon_map_noinherit(void)
+{
+ return last_anon_map_noinherit;
+}
+static void
+set_last_anon_map_noinherit(unsigned f)
+{
+ last_anon_map_noinherit = f;
+}
+#else
+static void
+set_last_anon_map_noinherit(unsigned f)
+{
+ (void)f;
+}
+#endif
+
/**
* Helper: try to prevent the sz bytes at mem from being
* accessible in child processes -- ideally by having them set to 0 after a
@@ -117,13 +140,20 @@ nodump_mem(void *mem, size_t sz)
static int
noinherit_mem(void *mem, size_t sz)
{
+ set_last_anon_map_noinherit(0);
#ifdef FLAG_ZERO
int r = MINHERIT(mem, sz, FLAG_ZERO);
- if (r == 0)
+ if (r == 0) {
+ set_last_anon_map_noinherit(2);
return 0;
+ }
#endif
#ifdef FLAG_NOINHERIT
- return MINHERIT(mem, sz, FLAG_NOINHERIT);
+ int r2 = MINHERIT(mem, sz, FLAG_NOINHERIT);
+ if (r2 == 0) {
+ set_last_anon_map_noinherit(1);
+ }
+ return r2;
#else
(void)mem;
(void)sz;
diff --git a/src/lib/malloc/map_anon.h b/src/lib/malloc/map_anon.h
index cc5797e4ec..395145bd71 100644
--- a/src/lib/malloc/map_anon.h
+++ b/src/lib/malloc/map_anon.h
@@ -34,4 +34,8 @@
void *tor_mmap_anonymous(size_t sz, unsigned flags);
void tor_munmap_anonymous(void *mapping, size_t sz);
+#ifdef TOR_UNIT_TESTS
+unsigned get_last_anon_map_noinherit(void);
+#endif
+
#endif /* !defined(TOR_MAP_ANON_H) */
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 7a2708c541..4990aa709a 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -6165,8 +6165,8 @@ static void
test_util_map_anon_nofork(void *arg)
{
(void)arg;
-#if !defined(HAVE_MADVISE) && !defined(HAVE_MINHERIT)
- /* The operating system doesn't support this. */
+#ifdef _WIN32
+ /* The operating system doesn't support forking. */
tt_skip();
done:
;
@@ -6182,6 +6182,7 @@ test_util_map_anon_nofork(void *arg)
tor_munmap_anonymous(ptr, sz);
ptr = tor_mmap_anonymous(sz, ANONMAP_NOINHERIT);
+ int outcome = get_last_anon_map_noinherit();
tt_ptr_op(ptr, OP_NE, 0);
memset(ptr, 0xd0, sz);
@@ -6202,15 +6203,30 @@ test_util_map_anon_nofork(void *arg)
pipefd[1] = -1;
char buf[1];
ssize_t r = read(pipefd[0], buf, 1);
-#if defined(INHERIT_ZERO) || defined(MADV_WIPEONFORK)
- tt_int_op((int)r, OP_EQ, 1); // child should send us a byte.
- tt_int_op(buf[0], OP_EQ, 0);
-#else
- tt_int_op(r, OP_LE, 0); // child said nothing; it should have crashed.
-#endif
+
+ if (outcome == 2) {
+ // We should be seeing clear-on-fork behavior.
+ tt_int_op((int)r, OP_EQ, 1); // child should send us a byte.
+ tt_int_op(buf[0], OP_EQ, 0); // that byte should be zero.
+ } else if (outcome == 1) {
+ // We should be seeing noinherit behavior.
+ tt_int_op(r, OP_LE, 0); // child said nothing; it should have crashed.
+ } else {
+ // noinherit isn't implemented.
+ tt_int_op(outcome, OP_EQ, 0);
+ tt_int_op((int)r, OP_EQ, 1); // child should send us a byte.
+ tt_int_op(buf[0], OP_EQ, 0xd0); // that byte should what we set it to.
+ }
+
int ws;
waitpid(child, &ws, 0);
+ if (outcome == 0) {
+ /* Call this test "skipped", not "passed", since noinherit wasn't
+ * implemented. */
+ tt_skip();
+ }
+
done:
tor_munmap_anonymous(ptr, sz);
if (pipefd[0] >= 0) {
@@ -6360,6 +6376,6 @@ struct testcase_t util_tests[] = {
UTIL_TEST(get_unquoted_path, 0),
UTIL_TEST(log_mallinfo, 0),
UTIL_TEST(map_anon, 0),
- UTIL_TEST(map_anon_nofork, TT_SKIP /* See bug #29535 */),
+ UTIL_TEST(map_anon_nofork, 0),
END_OF_TESTCASES
};