Commit 17a0855
authored
fix(live-debugger): fix use-after-free, tags leak, and Windows 7.x crash on probe removal (#4036)
* fix(live-debugger): key spans_map by config_id to prevent probe hook use-after-free
The live debugger tracks installed probe hooks in `spans_map`, but keyed it
inconsistently: apply_config and remove_config used the probe's `id` field,
while the DI-enable reinstall path used the `active` map key (the config_id).
`active` is keyed by config_id, which is unique per remote-config path, whereas
probe.id is not: two distinct configs can carry the same probe id. When they do
(or when a probe is installed via different paths), the second install overwrites
the probe.id-keyed spans_map entry, orphaning the first hook. Removing a config
then tears down the wrong hook (or none) and frees the parsed-config box while an
orphaned hook still borrows its strings. The orphan's first fire afterwards reads
the freed probe id in ddog_debugger_diagnostics_create_unboxed -> use-after-free.
Only valgrind observes it (plain runs read freed-but-intact bytes), which is why
it surfaced intermittently as a LEAKED test in test_extension_ci.
Key spans_map by config_id everywhere (apply_config, remove_config, and the
already-correct reinstall path) so every config's hook is tracked and removed
independently. Adds tests/ext/live-debugger/debugger_remove_shared_probe_id.phpt,
which reproduces the UAF (two configs sharing a probe id) and is clean with the fix.
Verified under valgrind on PHP 7.1 and 8.3: the reproducer leaks before the fix
and passes after; the whole live-debugger suite is clean.
* test(live-debugger): distinguish configs by an ignored field, not tags
The regression test used a distinct `tags` entry per config only to give the two
configs different remote-config paths. But a non-empty `tags` makes probe.into()
allocate an FFI CharSliceVec that the C side never frees (a separate latent leak
for tagged probes), which LeakSanitizer flags in the ASAN 'multiple observers'
job (32 bytes / 2 objects) and breaks the test's expected output.
Use an ignored unknown field instead: same two distinct config paths, same probe
id, no per-config allocation -> no unrelated LSan noise. Verified under valgrind
(clean) and the two configs still install at distinct paths with the shared id.
* fix(live-debugger): free the probe tags CharSliceVec on uninstall
probe.into() heap-allocates a CharSliceVec for the probe `tags`, but the C side
(def->probe) only freed the nested span-decoration / log allocations in
dd_probe_dtor -- never the tags vec -- so every probe carrying tags leaked it
(LeakSanitizer: 32 bytes / 2 objects in the ASAN 'multiple observers' job).
Add ddog_drop_probe, which consumes the FFI probe by value so its drop glue
frees the tags CharSliceVec together with the nested span-decoration / log
allocations, and call it from dd_probe_dtor in place of the piecemeal drops.
Cleanup is now a single, consistent operation covering every FFI-owned field.
Restore the shared-probe-id regression test to distinguish its two configs by
`tags`, so it also exercises this path (a tags leak would resurface under LSan).
Verified on PHP 8.3: the live-debugger suite is clean under valgrind (no UAF,
no double-free), and valgrind --leak-check=full on a tagged probe shows 0 bytes
definitely lost with no CharSliceVec leak record.
* fix(live-debugger): remove stale hook on re-add; wait for both probes; trim comments
Address Codex review:
- apply_config: remove any hook already installed for a config id before
installing the replacement, so an in-place Add (Occupied entry) can't orphan
the previous hook into the dropped parsed config.
- regression test: await both colliding probes (2) before removing, so it
actually exercises the shared-probe-id collision.
Also condense the comments added in this PR.
* fix(live-debugger): fix PHP 7.x Windows crash and drop stale removal map entry
The regression test exposed a PHP 7.x Windows-only crash (0xC0000005) in
dd_remove_live_debugger_probe: MSVC 2017 if-converts the inline ternary that
builds zai_hook_remove's scope/function args into an unconditional ZSTR_LEN
load, a NULL deref for global functions (scope == NULL). Build the zai_str
views with explicit if-guards, as the install path already does. PHP 8.x
(MSVC 2019) and Linux were unaffected.
Also drop the stale active_live_debugger_hooks entry on removal so a later
lookup of the same id can't use-after-free the already-freed def.1 parent a70313e commit 17a0855
4 files changed
Lines changed: 107 additions & 18 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
189 | 189 | | |
190 | 190 | | |
191 | 191 | | |
| 192 | + | |
| 193 | + | |
192 | 194 | | |
193 | 195 | | |
194 | 196 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
392 | 392 | | |
393 | 393 | | |
394 | 394 | | |
| 395 | + | |
395 | 396 | | |
396 | 397 | | |
397 | 398 | | |
| |||
405 | 406 | | |
406 | 407 | | |
407 | 408 | | |
408 | | - | |
| 409 | + | |
409 | 410 | | |
410 | 411 | | |
411 | 412 | | |
| |||
440 | 441 | | |
441 | 442 | | |
442 | 443 | | |
443 | | - | |
| 444 | + | |
444 | 445 | | |
445 | 446 | | |
446 | 447 | | |
| |||
472 | 473 | | |
473 | 474 | | |
474 | 475 | | |
| 476 | + | |
475 | 477 | | |
476 | 478 | | |
477 | 479 | | |
| |||
480 | 482 | | |
481 | 483 | | |
482 | 484 | | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
483 | 492 | | |
484 | 493 | | |
| 494 | + | |
| 495 | + | |
485 | 496 | | |
486 | 497 | | |
487 | 498 | | |
488 | | - | |
| 499 | + | |
489 | 500 | | |
490 | 501 | | |
491 | 502 | | |
| |||
522 | 533 | | |
523 | 534 | | |
524 | 535 | | |
525 | | - | |
| 536 | + | |
526 | 537 | | |
527 | 538 | | |
528 | 539 | | |
529 | | - | |
530 | | - | |
| 540 | + | |
| 541 | + | |
531 | 542 | | |
532 | 543 | | |
533 | 544 | | |
| |||
719 | 730 | | |
720 | 731 | | |
721 | 732 | | |
722 | | - | |
723 | | - | |
| 733 | + | |
| 734 | + | |
724 | 735 | | |
725 | 736 | | |
726 | 737 | | |
727 | 738 | | |
728 | 739 | | |
729 | 740 | | |
730 | | - | |
| 741 | + | |
731 | 742 | | |
732 | 743 | | |
733 | 744 | | |
| |||
750 | 761 | | |
751 | 762 | | |
752 | 763 | | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
753 | 770 | | |
754 | 771 | | |
755 | 772 | | |
| |||
Lines changed: 61 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
128 | 128 | | |
129 | 129 | | |
130 | 130 | | |
131 | | - | |
132 | | - | |
133 | | - | |
134 | | - | |
135 | | - | |
| 131 | + | |
| 132 | + | |
136 | 133 | | |
137 | 134 | | |
138 | 135 | | |
| |||
854 | 851 | | |
855 | 852 | | |
856 | 853 | | |
857 | | - | |
858 | | - | |
859 | | - | |
860 | | - | |
| 854 | + | |
| 855 | + | |
| 856 | + | |
| 857 | + | |
| 858 | + | |
| 859 | + | |
| 860 | + | |
| 861 | + | |
| 862 | + | |
| 863 | + | |
| 864 | + | |
| 865 | + | |
| 866 | + | |
861 | 867 | | |
862 | 868 | | |
863 | 869 | | |
864 | 870 | | |
865 | 871 | | |
866 | 872 | | |
| 873 | + | |
| 874 | + | |
| 875 | + | |
867 | 876 | | |
868 | 877 | | |
869 | 878 | | |
| |||
0 commit comments