]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test: build the crashing test binary outside of the test
authorFrantisek Sumsal <frantisek@sumsal.cz>
Thu, 9 Oct 2025 21:08:19 +0000 (23:08 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Fri, 7 Nov 2025 08:49:11 +0000 (09:49 +0100)
So we don't have to pull in gcc and other stuff into it.

Also, make the test itself a bit more robust and debug-able.

(cherry picked from commit 937f609b41b9e27eba69c5ddbab4df2232e5a37b)

src/test/meson.build
src/test/test-coredump-stacktrace.c [new file with mode: 0644]
test/units/TEST-87-AUX-UTILS-VM.coredump.sh

index da04b82d47691f2160d3509087ccad578abf872c..cfd15d118b403ce232a98fc6dc2aa845e0694bbe 100644 (file)
@@ -276,6 +276,17 @@ executables += [
                 'sources' : files('test-compress.c'),
                 'link_with' : [libshared],
         },
+        test_template + {
+                'sources' : files('test-coredump-stacktrace.c'),
+                'type' : 'manual',
+                # This test intentionally crashes with SIGSEGV by dereferencing a NULL pointer
+                # to generate a coredump with a predictable stack trace. To prevent sanitizers
+                # from catching the error first let's disable them explicitly, and also always
+                # build with minimal optimizations to make the stack trace predictable no matter
+                # what we build the rest of systemd with
+                'override_options' : ['b_sanitize=none', 'strip=false', 'debug=true'],
+                'c_args' : ['-fno-sanitize=all', '-fno-optimize-sibling-calls', '-O1'],
+        },
         test_template + {
                 'sources' : files('test-cryptolib.c'),
                 'dependencies' : libopenssl,
diff --git a/src/test/test-coredump-stacktrace.c b/src/test/test-coredump-stacktrace.c
new file mode 100644 (file)
index 0000000..334a155
--- /dev/null
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+/* This is a test program that intentionally segfaults so we can generate a
+ * predictable-ish stack trace in tests. */
+
+#include <stdlib.h>
+
+__attribute__((noinline))
+static void baz(int *x) {
+        *x = rand();
+}
+
+__attribute__((noinline))
+static void bar(void) {
+        int * volatile x = NULL;
+
+        baz(x);
+}
+
+__attribute__((noinline))
+static void foo(void) {
+        bar();
+}
+
+int main(void) {
+        foo();
+
+        return 0;
+}
index 7384784b21e4da8a9a60865da049b14596a4fe83..354498a5ff61aa24feca1fcca32f3818853bd931 100755 (executable)
@@ -8,15 +8,13 @@ set -o pipefail
 
 # Make sure the binary name fits into 15 characters
 CORE_TEST_BIN="/tmp/test-dump"
-CORE_STACKTRACE_TEST_BIN="/tmp/test-stacktrace-dump"
-MAKE_STACKTRACE_DUMP="/tmp/make-stacktrace-dump"
 CORE_TEST_UNPRIV_BIN="/tmp/test-usr-dump"
 MAKE_DUMP_SCRIPT="/tmp/make-dump"
 # Unset $PAGER so we don't have to use --no-pager everywhere
 export PAGER=
 
 at_exit() {
-    rm -fv -- "$CORE_TEST_BIN" "$CORE_TEST_UNPRIV_BIN" "$MAKE_DUMP_SCRIPT" "$MAKE_STACKTRACE_DUMP"
+    rm -fv -- "$CORE_TEST_BIN" "$CORE_TEST_UNPRIV_BIN" "$MAKE_DUMP_SCRIPT"
 }
 
 (! systemd-detect-virt -cq)
@@ -251,30 +249,66 @@ systemd-run -t --property CoredumpFilter=default ls /tmp
 (! coredumpctl debug --debugger=/bin/true --debugger-arguments='"')
 
 # Test for EnterNamespace= feature
-if pkgconf --atleast-version 0.192 libdw ; then
-    # dwfl_set_sysroot() is supported only in libdw-0.192 or newer.
-    cat >"$MAKE_STACKTRACE_DUMP" <<END
-#!/usr/bin/env bash
-mount -t tmpfs tmpfs /tmp
-gcc -xc -O0 -g -o $CORE_STACKTRACE_TEST_BIN - <<EOF
-void baz(void) { int *x = 0; *x = 42; }
-void bar(void) { baz(); }
-void foo(void) { bar(); }
-int main(void) { foo(); return 0;}
+#
+# dwfl_set_sysroot() is supported only in libdw-0.192 or newer.
+if pkgconf --atleast-version 0.192 libdw; then
+    MAKE_STACKTRACE_DUMP="/tmp/make-stacktrace-dump"
+
+    # Simple script that mounts tmpfs on /tmp/ and copies the crashing test binary there, which in
+    # combination with `unshare --mount` ensures the "outside" systemd-coredump process won't be able to
+    # access the crashed binary (and hence won't be able to symbolize its stacktrace) unless
+    # EnterNamespace=yes is used
+    cat >"$MAKE_STACKTRACE_DUMP" <<\EOF
+#!/usr/bin/bash -eux
+
+TARGET="/tmp/${1:?}"
+EC=0
+
+# "Unhide" debuginfo in the namespace (see the comment below)
+test -d /usr/lib/debug/ && umount /usr/lib/debug/
+
+mount -t tmpfs tmpfs /tmp/
+cp /usr/lib/systemd/tests/unit-tests/manual/test-coredump-stacktrace "$TARGET"
+
+$TARGET || EC=$?
+if [[ $EC -ne 139 ]]; then
+    echo >&2 "$TARGET didn't crash, this shouldn't happen"
+    exit 1
+fi
+
+exit 0
 EOF
-$CORE_STACKTRACE_TEST_BIN
-END
     chmod +x "$MAKE_STACKTRACE_DUMP"
 
+    # Since the test-coredump-stacktrace binary is built together with rest of the systemd its debug symbols
+    # might be part of debuginfo packages (if supported & built), and libdw will then use them to symbolize
+    # the stacktrace even if it doesn't have access to the original crashing binary. Let's make the test
+    # simpler and just "hide" the debuginfo data, so libdw is forced to access the target namespace to get
+    # the necessary symbols
+    test -d /usr/lib/debug/ && mount -t tmpfs tmpfs /usr/lib/debug/
+
     mkdir -p /run/systemd/coredump.conf.d/
     printf '[Coredump]\nEnterNamespace=no' >/run/systemd/coredump.conf.d/99-enter-namespace.conf
 
-    unshare --pid --fork --mount-proc --mount --uts --ipc --net bash -c "$MAKE_STACKTRACE_DUMP" || :
-    timeout 30 bash -c "until coredumpctl -1 info $CORE_STACKTRACE_TEST_BIN | grep -zvqE 'baz.*bar.*foo'; do sleep .2; done"
+    unshare --pid --fork --mount-proc --mount --uts --ipc --net "$MAKE_STACKTRACE_DUMP" "test-stacktrace-not-symbolized"
+    timeout 30 bash -c "until coredumpctl list -q --no-legend /tmp/test-stacktrace-not-symbolized; do sleep .2; done"
+    coredumpctl info /tmp/test-stacktrace-not-symbolized | tee /tmp/not-symbolized.log
+    (! grep -E "#[0-9]+ .* main " /tmp/not-symbolized.log)
+    (! grep -E "#[0-9]+ .* foo " /tmp/not-symbolized.log)
+    (! grep -E "#[0-9]+ .* bar " /tmp/not-symbolized.log)
+    (! grep -E "#[0-9]+ .* baz " /tmp/not-symbolized.log)
 
     printf '[Coredump]\nEnterNamespace=yes' >/run/systemd/coredump.conf.d/99-enter-namespace.conf
-    unshare --pid --fork --mount-proc --mount --uts --ipc --net bash -c "$MAKE_STACKTRACE_DUMP" || :
-    timeout 30 bash -c "until coredumpctl -1 info $CORE_STACKTRACE_TEST_BIN | grep -zqE 'baz.*bar.*foo'; do sleep .2; done"
+    unshare --pid --fork --mount-proc --mount --uts --ipc --net "$MAKE_STACKTRACE_DUMP" "test-stacktrace-symbolized"
+    timeout 30 bash -c "until coredumpctl list -q --no-legend /tmp/test-stacktrace-symbolized; do sleep .2; done"
+    coredumpctl info /tmp/test-stacktrace-symbolized | tee /tmp/symbolized.log
+    grep -E "#[0-9]+ .* main " /tmp/symbolized.log
+    grep -E "#[0-9]+ .* foo " /tmp/symbolized.log
+    grep -E "#[0-9]+ .* bar " /tmp/symbolized.log
+    grep -E "#[0-9]+ .* baz " /tmp/symbolized.log
+
+    test -d /usr/lib/debug/ && umount /usr/lib/debug/
+    rm -f "$MAKE_STACKTRACE_DUMP" /run/systemd/coredump.conf.d/99-enter-namespace.conf /tmp/{not-,}symbolized.log
 else
     echo "libdw doesn't not support setting sysroot, skipping EnterNamespace= test"
 fi