Repository navigation
ci: reproduce intermittent PHP crash during Behat - #8913
Closed
vitormattos wants to merge 55 commits into
Closed
vitormattos wants to merge 55 commits into
vitormattos wants to merge 55 commits into
Conversation
Signed-off-by: LibreSign maintainers <contact@librecode.coop>
Signed-off-by: LibreSign maintainers <contact@librecode.coop>
Signed-off-by: Vitor Mattos <vitor@php.rio>
Signed-off-by: Vitor Mattos <vitor@php.rio>
Signed-off-by: Vitor Mattos <vitor@php.rio>
Signed-off-by: Vitor Mattos <vitor@php.rio>
Signed-off-by: Vitor Mattos <vitor@php.rio>
Signed-off-by: Vitor Mattos <vitor@php.rio>
Signed-off-by: Vitor Mattos <vitor@php.rio>
Signed-off-by: Vitor Mattos <vitor@php.rio>
Signed-off-by: Vitor Mattos <vitor@php.rio>
Signed-off-by: Vitor Mattos <vitor@php.rio>
Signed-off-by: Vitor Mattos <vitor@php.rio>
Signed-off-by: Vitor Mattos <vitor@php.rio>
Signed-off-by: Vitor Mattos <vitor@php.rio>
Signed-off-by: Vitor Mattos <vitor@php.rio>
Signed-off-by: Vitor Mattos <vitor@php.rio>
Signed-off-by: Vitor Mattos <vitor@php.rio>
Signed-off-by: Vitor Mattos <vitor@php.rio>
2 tasks done
Member
Author
|
Fixed by: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
LibreSign has an intermittent Behat infrastructure failure tracked in #8422.
The visible symptom is usually:
The diagnostic work in this PR proved that the HTTP error is a consequence of a native PHP crash, not the root cause.
What is already proven
We have direct reproductions with PHP 8.3.35 showing:
PHP_CLI_SERVER_WORKERS=0;USE_ZEND_ALLOC=0, so the Zend allocator is not required.Two independent cores stopped at the same instruction:
The object pointer inspected by GDB does not look like a valid live
zend_object. The current evidence is consistent with an object that was freed, overwritten, or otherwise corrupted beforezend_objects_store_del()became the visible crash point.That still does not prove that the original bug is in Zend itself. The corruption may happen earlier in PHP core, a shared extension, or code exercised by the application.
Strongest reproducer
The strongest application-level signal is still the request-signature path.
Failures have repeatedly appeared around:
and the original Behat scenario:
The diagnostic workflow therefore focuses on this path instead of repeatedly running the whole suite without isolation.
Controlled application-path experiments
The focused workload separates:
notify=0;notify=0;validate-uuidcalls;Return a list with 3 pagesscenario repeated in one long-lived PHP process.The exact scenario is extracted from the repository feature file instead of being reimplemented manually.
Test-history / contamination experiments
The crash has happened after a long-lived PHP process had already served many scenarios, so previous tests may influence heap layout or object lifetime.
To test this without running the whole suite, the same PHP process now runs these progressively larger sequences:
file/list.featureonly;file/envelope.feature + file/list.feature;build/sign.feature + file/envelope.feature + file/list.feature;account + admin + build/sign + file/envelope + file/list.This lets us identify whether a specific prelude is needed to reproduce the corruption.
Process-lifetime isolation
The same request-signature workload is tested with:
php -Sprocess;If only the long-lived case fails, delayed destruction, accumulated process state, or reuse of previously corrupted memory becomes much more likely.
PHP runtime isolation
The workflow compares:
MALLOC_CHECK_/MALLOC_PERTURB_;The effective ini files and loaded module list are recorded for every case. Results must be interpreted from the effective runtime artifact, not from the requested setup alone.
PHP debug + sanitizers
A dedicated job now builds PHP 8.3.35 from php-src with:
--enable-debug;-g3;The focused request-signature workload then runs with:
This is the most important new diagnostic path. If the visible SIGSEGV is caused by an earlier invalid read, invalid write, use-after-free, double-free, or undefined behavior, ASan/UBSan should report the first invalid operation closer to its origin instead of only showing the later crash in
zend_objects_store_del().Sanitizer logs are uploaded with the normal core/GDB artifacts.
Valgrind
A smaller focused case runs under Valgrind Memcheck with:
USE_ZEND_ALLOC=0;Valgrind is slower than ASan and changes timing significantly, so it is supporting evidence rather than the primary reproducer.
Xdebug trace
Xdebug is intentionally isolated in its own case.
It is not used as proof of the native root cause because Xdebug changes the executor, installs observer hooks, and changes memory layout.
If the crash still reproduces with Xdebug loaded, function tracing gives us a PHP-level timeline immediately before the native failure and can identify the last controller/service path executed.
Crash diagnostics
For a direct core, the artifact records:
zend_objectfields;eu-stackoutput when available;zend_objects_store_del().For a live hang, the watchdog records:
/proc/<pid>/status;wchan;strace.Every artifact also records the diagnostic case id, effective PHP runtime and failure classification so a timeout cannot be confused with a SIGSEGV.
Relevant upstream PHP reports
None of the reports found so far exactly matches the LibreSign reproducer.
php-src #10593
zend_objects_store_delsees invalid object-store state. A PHP maintainer points to an object that may already have been freed / freed twice.This is currently the closest mechanism-level similarity, but it is not a reproducer for LibreSign.
php-src #20818
Verified SIGSEGV in
zend_objects_store_del, caused by tracing JIT passing the wrong value type to object destruction.Our crash already reproduces without tracing JIT, so this specific bug does not explain it.
php-src #9400
Known
PHP_CLI_SERVER_WORKERSbug in the built-in server that can hang requests.It does not report this SIGSEGV, and our crash also reproduces with workers disabled, so it is not sufficient to explain the current failure.
php-src #21995
Memory-layout-sensitive shutdown UAF in PHP 8.4.
It is useful evidence that extension layout can expose latent UAFs, but the affected path/version is different from the current PHP 8.3 request-time crash.
php-src #23355
OPcache/map-pointer corruption involving different FPM pools and pool-local extensions.
The mechanism is interesting, but the reproducer requires FPM + shared OPcache state. Our current crash occurs in CLI server mode with OPcache not required.
php-src #23352 / #23353
Open UAF reports in ext/dom for PHP 8.3/8.4 involving
DOMDocument::adoptNode().There is currently no evidence that the LibreSign request-signature path executes that operation. The sanitizer build is a better discriminator than assuming DOM is responsible.
php-src #22061
Double-free through re-entrant destruction in Zend hash cleanup.
Again, this is a useful mechanism-level comparison because it can produce an already-destroyed object at
zend_objects_store_del(), but there is no evidence yet that LibreSign executes the required destructor/re-entrancy pattern.How to interpret the next run
ASan/UBSan reports an earlier invalid access:
exact list scenario fails without any prelude:
exact list scenario passes but one prelude sequence fails:
draft request fails:
email notify fails while email no-notify passes repeatedly:
long-lived fails while fresh-process passes repeatedly:
controlled runtime fails:
one extension group restores the failure:
Xdebug case reproduces:
A passing single run is evidence, not proof. Any discriminator must be repeated before changing production code.
Regular CI
The normal LibreSign Behat workflows remain unchanged.
All crash-specific instrumentation stays isolated in this draft diagnostic workflow.
Out of scope
Do not:
cURL error 52as the root cause;zend_objects_store_del()is where the corruption starts.Refs #8422