From 9e5b48d5b48f525062af3fe7efa4f1ceab56390c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C5=8Dan?= Date: Wed, 18 Mar 2026 23:35:52 -0600 Subject: [PATCH 1/3] ci: add Valgrind memory leak detection job Add a Valgrind CI job running on debian:bookworm (OpenSSL 3.x) to automatically detect memory leaks in the XS code across all PRs. Uses --show-leak-kinds=definite to avoid Perl's known "still reachable" false positives, and --trace-children=yes since prove forks child processes for each test file. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/testsuite.yml | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index a4c0153..1564121 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -40,6 +40,41 @@ jobs: run: perl -I$(pwd) Makefile.PL - run: make && ( make test || prove -wbvm t/*.t ) +# +# Valgrind memory leak detection on OpenSSL 3.x +# + + valgrind: + timeout-minutes: 15 + runs-on: ubuntu-latest + needs: [ubuntu] + name: "Valgrind (debian:bookworm)" + container: debian:bookworm + + steps: + - uses: actions/checkout@v6 + - name: Install dependencies + run: | + apt-get update + apt-get install -y openssl perl make gcc libssl-dev sudo curl valgrind + - name: uses install-with-cpm + uses: perl-actions/install-with-cpm@v1 + with: + cpanfile: "cpanfile" + - name: Build + run: | + perl -I$(pwd) Makefile.PL + make + - name: Run tests under Valgrind + run: | + valgrind \ + --leak-check=full \ + --show-leak-kinds=definite \ + --error-exitcode=1 \ + --trace-children=yes \ + --child-silent-after-fork=yes \ + prove -v t/*.t + openssl-matrix: timeout-minutes: 5 env: From 59e9f0e2883bca4d727892faf300a9a069f8a410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C5=8Dan?= Date: Wed, 18 Mar 2026 23:50:22 -0600 Subject: [PATCH 2/3] =?UTF-8?q?ci:=20fix=20valgrind=20OOM=20=E2=80=94=20ru?= =?UTF-8?q?n=20per-file=20instead=20of=20via=20prove?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit trace-children=yes with prove caused valgrind to wrap every child process, exhausting CI memory (exit 137). Run each test file directly under valgrind in a loop instead — one valgrind instance at a time. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/testsuite.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index 1564121..f2ed3eb 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -67,13 +67,14 @@ jobs: make - name: Run tests under Valgrind run: | - valgrind \ - --leak-check=full \ - --show-leak-kinds=definite \ - --error-exitcode=1 \ - --trace-children=yes \ - --child-silent-after-fork=yes \ - prove -v t/*.t + for t in t/*.t; do + echo "=== valgrind: $t ===" + valgrind \ + --leak-check=full \ + --show-leak-kinds=definite \ + --error-exitcode=1 \ + perl -Iblib/lib -Iblib/arch "$t" || exit 1 + done openssl-matrix: timeout-minutes: 5 From 39c08d5ac829cebb8cd9853bee125b3fcb37c97a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C5=8Dan?= Date: Wed, 18 Mar 2026 23:56:38 -0600 Subject: [PATCH 3/3] ci: filter valgrind output for RSA.xs leaks only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Perl's runtime has known "definitely lost" allocations at exit that are false positives. Instead of --error-exitcode=1 (which catches all leaks), parse valgrind's text output and only fail if a "definitely lost" block's stack trace includes RSA.so — our XS shared library. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/testsuite.yml | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index f2ed3eb..54670f9 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -67,14 +67,35 @@ jobs: make - name: Run tests under Valgrind run: | + rc=0 for t in t/*.t; do echo "=== valgrind: $t ===" valgrind \ --leak-check=full \ --show-leak-kinds=definite \ - --error-exitcode=1 \ - perl -Iblib/lib -Iblib/arch "$t" || exit 1 + --num-callers=20 \ + --log-file=valgrind.log \ + perl -Iblib/lib -Iblib/arch "$t" + # Only fail if RSA.so appears in a "definitely lost" stack trace. + # This filters out Perl-internal leaks (known false positives). + if perl -0777 -e ' + my $log = do { local $/; }; + my $found = 0; + for my $block (split /\n\n/, $log) { + if ($block =~ /definitely lost/ && $block =~ /RSA\.so/) { + print STDERR "LEAK from RSA.xs:\n$block\n\n"; + $found = 1; + } + } + exit $found; + ' < valgrind.log; then + echo " -> clean" + else + echo "Memory leak from RSA.xs in $t!" + rc=1 + fi done + exit $rc openssl-matrix: timeout-minutes: 5