Skip to content

fix(appsec): musl ZTS load + ZTS thread-exit teardown; ci: coverage disk envelope - #4180

Merged
Leiyks merged 10 commits into
masterfrom
leiyks/fix-appsec-musl-zts-and-coverage-disk
Sep 15, 2026
Merged

Leiyks merged 10 commits into
masterfrom
leiyks/fix-appsec-musl-zts-and-coverage-disk

Conversation

@Leiyks

@Leiyks Leiyks commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Description

One commit per change; they share no code and can be reviewed separately.

fix(appsec): weakly reference __cxa_thread_atexit_impl. musl exports no such symbol (it is a glibc 2.18 internal) and resolves relocations eagerly, so the strong reference made dlopen("ddappsec.so") fail outright. Every ZTS build emits it, so the failure set is musl ∩ ZTSalready shipped in 1.25.0 and 1.25.1, where AppSec is therefore fully non-functional on Alpine/FrankenPHP ZTS, so this needs to be in the next release.

fix(appsec): pthread fallback for the thread-exit destructor. Without that symbol nothing tears non-main threads down, so GINIT falls back to a pthread_key_create destructor and a ZEND_TLS flag lets whichever of GSHUTDOWN-on-the-owning-thread or thread-exit comes first do the work. That ordering also fixes a segfault reproducible on unmodified master (_tshutdown_handlerdd_log_levelzai_config_get_value after PHP freed the thread's config; pecl parallel, 3 Runtimes then close()), which the old registered_thread_local_dtor handshake caused by leaving thread-exit as the only teardown path on glibc ZTS.

ci: disk envelope for helper-rust integration coverage. The job never set DOCKER_LOOPBACK_SIZE, so it ran on the DinD default of ~20G; the added df probes measure the volume going 3.2G → 19.9G across the build, leaving ~0.1G of headroom there, hence the alternation between No space left on device and Bus error. Raised to 50G, and the job now passes.

Test infrastructure (separate author). Portable dd_library_loader in the appsec integration tests, a runtime gnu_get_libc_version probe replacing the compile-time __MUSL__ so one loader build serves both libcs, and an Apache ZTS musl AppSec test image so this combination is covered at all; a final commit drops the now-dead __MUSL__ detection.

installer tests stays red. It installs the published tarball for $(cat VERSION) and its needs supply only *-unknown-linux-gnu bundles, so the musl-ZTS sub-test keeps pulling 1.25.1 whatever master contains; only a release carrying the first fix clears it.

…n load

The TLS destructor registration in PHP_GINIT_FUNCTION(ddappsec) declared
__cxa_thread_atexit_impl as a strong symbol and called it unconditionally on
Linux. The symbol is glibc-private and musl exports it nowhere, and musl
resolves relocations eagerly, so dlopen("ddappsec.so") fails outright with
"Error relocating ...: __cxa_thread_atexit_impl: symbol not found". AppSec is
therefore dead on musl + ZTS (FrankenPHP-Alpine, Swoole/pthreads on Alpine) in
1.25.0 and 1.25.1. ddtrace-zts.so references the same symbol weakly, which is
why the tracer is unaffected.

Declare it weak and register the destructor only when it resolves.
registered_thread_local_dtor then stays false on musl, which is the case
PHP_GSHUTDOWN_FUNCTION(ddappsec) already handles ("a platform without a
thread-exit destructor mechanism"), so tshutdown still runs on the owning
thread. glibc is unchanged: the weak reference resolves there and the
destructor is registered as before.

Verified on dunglas/frankenphp:php8.3.12-alpine (PHP 8.3.12 ZTS, musl 1.2.5),
same tree, incremental rebuild of only this file:

  before: NOTYPE GLOBAL DEFAULT UND __cxa_thread_atexit_impl
          extension_loaded("ddappsec") => false, "Error relocating" warning
  after:  NOTYPE WEAK   DEFAULT UND __cxa_thread_atexit_impl
          extension_loaded("ddappsec") => true, no relocation error
…overage

"helper-rust integration coverage" never set DOCKER_LOOPBACK_SIZE, so it ran on
the DinD default of ~20G while its sibling .appsec_integration_tests sets 30G
and "push appsec images" sets 100G. 17 consecutive master runs died in
:buildPortableLibdatadogPhp with "failed to build archive at
.../libdatadog_php.a: No space left on device (os error 28)", and the 10 runs
after that with "Bus error" from musl-clang at the final cdylib link. A
same-pipeline A/B against the sibling holds everything else equal - same image,
runner tag, CPU and memory - and differs only by -PuseHelperRustCoverage and the
loopback size.

Raise it to 50G: the coverage build is strictly larger than the sibling's 30G.
The existing rm -rf /vol/cargo-target mitigation sits after the build, so it
never runs when the build itself is what exhausts the volume.

Also report df -h /, docker system df and the php-portable-libdatadog-php volume
before and after the build. The ENOSPC is measured; the "Bus error" being the
same exhaustion one step later is still an inference, and these numbers settle
it in one cycle. The gradle exit status is preserved so a failing build still
fails the job, and the report is emitted on that path too, which is the case the
numbers are for.
@datadog-prod-us1-6

datadog-prod-us1-6 Bot commented Sep 10, 2026

Copy link
Copy Markdown

Pipelines  Tests

Unblock PR with BitsAI

⚠️ Warnings

Your PR has failed checks. Please review the issues below and take necessary action before merging.

🚦 14 Pipeline jobs failed

DataDog/apm-reliability/dd-trace-php | check libxml2 version — 🔧 Needs a code fix, caused by this PR

View more details · View in GitLab

DataDog/apm-reliability/dd-trace-php | test_extension_ci: [7.4] — 🔧 Needs a code fix, caused by this PR

View more details · View in GitLab

DataDog/apm-reliability/dd-trace-php | test_extension_ci: [8.3] — 🔧 Needs a code fix, caused by this PR

View more details · View in GitLab

View all 14 failed jobs.

ℹ️ Info

No other issues found (see more)

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 50.00%
Overall Coverage: 66.75% (+11.67%)

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 83a2260 | Docs | View more details | Give us feedback!

@pr-commenter

pr-commenter Bot commented Sep 10, 2026

Copy link
Copy Markdown

Benchmarks [ appsec ]

Benchmark execution time: 2026-09-15 10:05:04

Comparing candidate commit 83a2260 in PR branch leiyks/fix-appsec-musl-zts-and-coverage-disk with baseline commit 4622bea in branch master.

Found 0 performance improvements and 0 performance regressions! Performance is the same for 12 metrics, 0 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

Comment thread appsec/src/extension/ddappsec.c
__cxa_thread_atexit_impl is a glibc internal, so the weak reference from
8b91eda resolves to NULL on musl and no destructor was registered at all,
leaking each exited thread's entity-body buffer and cached telemetry strings.
Fall back to a pthread key, as libc++ and libstdc++ do for __cxa_thread_atexit;
unlike __cxa_thread_atexit_impl it takes no reference on the DSO, which costs
nothing since musl never unmaps one.

On its own that would trade the leak for a crash: _tshutdown_handler() reads
this thread's config (mlog_g -> dd_log_level -> zai_config_get_value), which
PHP has already freed by the time a thread-exit destructor runs, so having
GSHUTDOWN stand down whenever a destructor was registered faults as soon as
GSHUTDOWN does run on the owning thread -- as it already does on glibc at
8b91eda. Run the handler at whichever of the two fires first, guarded by a
native thread-local, so it runs exactly once per thread and always while that
thread's config is still mapped.
@cataphract

Copy link
Copy Markdown
Contributor

I like it now. Next week I'll also add an appsec integration tsts against musl zts so this doesn't recur.

@pr-commenter

pr-commenter Bot commented Sep 11, 2026

Copy link
Copy Markdown

Benchmarks [ tracer ]

Benchmark execution time: 2026-09-14 17:45:20

Comparing candidate commit f278620 in PR branch leiyks/fix-appsec-musl-zts-and-coverage-disk with baseline commit fcbed87 in branch master.

Found 0 performance improvements and 0 performance regressions! Performance is the same for 193 metrics, 1 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

@Leiyks Leiyks changed the title fix(appsec): load on musl ZTS; ci: disk envelope for helper-rust coverage fix(appsec): musl ZTS load + ZTS thread-exit teardown; ci: coverage disk envelope Sep 15, 2026
@Leiyks
Leiyks marked this pull request as ready for review September 15, 2026 09:07
@Leiyks
Leiyks requested review from a team as code owners September 15, 2026 09:07
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 15, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-15T09:16:39.383575Z dda85f0 Draft marked ready
🔒 Security Review Completed 2026-09-15T09:15:15.763436Z dda85f0 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Leiyks
Leiyks requested a review from cataphract September 15, 2026 09:08

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dda85f021b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread appsec/tests/integration/build.gradle
Comment thread .gitlab/generate-appsec.php
@Leiyks
Leiyks merged commit 517aae8 into master Sep 15, 2026
2183 of 2197 checks passed
@Leiyks
Leiyks deleted the leiyks/fix-appsec-musl-zts-and-coverage-disk branch September 15, 2026 11:13
@github-actions github-actions Bot added this to the 1.26.0 milestone Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants