Skip to content

Commit 5682c31

Browse files
committed
Try to prevent dangling tracked_streams
We sometimes find corrupt tracked_streams pointers: Crash stacktrace (abbreviated): zend_hash_str_find ← SIGSEGV here dd_php_stdiop_close_wrapper ← exec_integration.c:89 _php_stream_free zend_file_handle_dtor compile_filename dd_execute_php_file ← autoload triggered by end hook zai_sandbox_call / dd_uhook_end ← post-hook for Slim\App::__construct execute_ex / php_execute_script While the reason why this happens is not entirely clear, it's possible that the previous RSHUTDOWN was not completely executed. This can happen if there are bailouts during RSHUTDOWN. Fix: wrap the rshutdown tracked_streams iteration in zend_try/zend_catch so any bailout sets tracked_streams = NULL rather than leaving it dangling. Also guard rinit against a non-NULL tracked_streams at request startup.
1 parent 988edd2 commit 5682c31

1 file changed

Lines changed: 32 additions & 15 deletions

File tree

ext/integrations/exec_integration.c

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <sys/wait.h>
66
#endif
77

8+
#include <components/log/log.h>
89
#include <ext/standard/file.h>
910
#include <ext/standard/proc_open.h>
1011

@@ -298,7 +299,11 @@ void ddtrace_exec_handlers_rinit() {
298299
// OTOH ddtrace_exec_handlers_rshutdown is not called when ddtrace is
299300
// disabled because it needs to be called earlier on upon the real rshutodown
300301

301-
if (tracked_streams) {
302+
if (PG(during_request_startup)) {
303+
if (tracked_streams) {
304+
LOG(WARN, "tracked_streams is non-NULL during request startup");
305+
}
306+
} else {
302307
dd_exec_destroy_tracked_streams();
303308
}
304309

@@ -307,21 +312,33 @@ void ddtrace_exec_handlers_rinit() {
307312

308313
void ddtrace_exec_handlers_rshutdown() {
309314
if (tracked_streams) {
310-
zend_ulong h;
311-
zend_string *key;
312-
zval *val;
313-
ZEND_HASH_REVERSE_FOREACH_KEY_VAL(tracked_streams, h, key, val) {
314-
(void)h;
315-
(void)val;
316-
php_stream *stream;
317-
memcpy(&stream, ZSTR_VAL(key), sizeof stream);
318-
// manually close the tracked stream on rshutdown in case they
319-
// lived till the end of the request so we can finish the span
320-
zend_list_close(stream->res);
321-
}
322-
ZEND_HASH_FOREACH_END();
315+
// generally, a bailout during rshutdown would prevent the rest of the
316+
// shutdown from that extension from running.
317+
// Here, we're force-closing streams (the keys of tracked_streams) and
318+
// potentially destroying spans (the values of tracked_streams).
319+
// There is at least one avenue for an error to escape: when executing
320+
// onClose callbacks on spans, if there has already been a PHP timeout,
321+
// the zai sandbox will let the error escape.
322+
zend_try {
323+
zend_ulong h;
324+
zend_string *key;
325+
zval *val;
326+
ZEND_HASH_REVERSE_FOREACH_KEY_VAL(tracked_streams, h, key, val) {
327+
(void)h;
328+
(void)val;
329+
php_stream *stream;
330+
memcpy(&stream, ZSTR_VAL(key), sizeof stream);
331+
// manually close the tracked stream on rshutdown in case they
332+
// lived till the end of the request so we can finish the span
333+
zend_list_close(stream->res);
334+
}
335+
ZEND_HASH_FOREACH_END();
323336

324-
dd_exec_destroy_tracked_streams();
337+
dd_exec_destroy_tracked_streams();
338+
} zend_catch {
339+
LOG(WARN, "Bailout during tracked_streams destruction");
340+
tracked_streams = NULL;
341+
} zend_end_try();
325342
}
326343

327344
{

0 commit comments

Comments
 (0)