diff --git a/.github/workflows/system-tests.yml b/.github/workflows/system-tests.yml index f42a80a79f1..610fb9f11e7 100644 --- a/.github/workflows/system-tests.yml +++ b/.github/workflows/system-tests.yml @@ -47,7 +47,7 @@ jobs: persist-credentials: false repository: 'DataDog/system-tests' # Automatically managed, use scripts/update-system-tests-version to update - ref: 'e13ccb562e9e060317b173e25b1e638d89f9df3b' + ref: '53608020d1f5cc57eb8c86f302d41c156bce091d' - name: Download wheels to binaries directory uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 @@ -94,7 +94,7 @@ jobs: persist-credentials: false repository: 'DataDog/system-tests' # Automatically managed, use scripts/update-system-tests-version to update - ref: 'e13ccb562e9e060317b173e25b1e638d89f9df3b' + ref: '53608020d1f5cc57eb8c86f302d41c156bce091d' - name: Build runner uses: ./.github/actions/install_runner @@ -279,7 +279,7 @@ jobs: persist-credentials: false repository: 'DataDog/system-tests' # Automatically managed, use scripts/update-system-tests-version to update - ref: 'e13ccb562e9e060317b173e25b1e638d89f9df3b' + ref: '53608020d1f5cc57eb8c86f302d41c156bce091d' - name: Download wheels to binaries directory uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7546a0f4b2e..b483e901426 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,7 +14,7 @@ variables: DD_VPA_TEMPLATE: "vpa-template-cpu-p70-10percent-2x-oom-min-cap" # CI_DEBUG_SERVICES: "true" # Automatically managed, use scripts/update-system-tests-version to update - SYSTEM_TESTS_REF: "e13ccb562e9e060317b173e25b1e638d89f9df3b" + SYSTEM_TESTS_REF: "53608020d1f5cc57eb8c86f302d41c156bce091d" default: interruptible: true diff --git a/.gitlab/benchmarks/bp-runner.microbenchmarks.fail-on-breach.yml b/.gitlab/benchmarks/bp-runner.microbenchmarks.fail-on-breach.yml index 8820a0d8738..6aebcdbcc07 100644 --- a/.gitlab/benchmarks/bp-runner.microbenchmarks.fail-on-breach.yml +++ b/.gitlab/benchmarks/bp-runner.microbenchmarks.fail-on-breach.yml @@ -726,11 +726,11 @@ experiments: - max_rss_usage < 39.00 MB - name: iastpropagation-propagation_enabled_100 thresholds: - - execution_time < 1.90 ms + - execution_time < 2.30 ms - max_rss_usage < 39.00 MB - name: iastpropagation-propagation_enabled_1000 thresholds: - - execution_time < 35.55 ms + - execution_time < 34.55 ms - max_rss_usage < 39.00 MB # otelsdkspan diff --git a/ddtrace/appsec/_iast/_taint_tracking/context/taint_engine_context.cpp b/ddtrace/appsec/_iast/_taint_tracking/context/taint_engine_context.cpp index 09a71d61e6a..397b43d7b88 100644 --- a/ddtrace/appsec/_iast/_taint_tracking/context/taint_engine_context.cpp +++ b/ddtrace/appsec/_iast/_taint_tracking/context/taint_engine_context.cpp @@ -59,6 +59,15 @@ TaintEngineContext::TaintEngineContext() { } +// Lifecycle guard. Prevent access during interpreter/module teardown. +std::atomic TaintEngineContext::shutting_down{ false }; + +void +TaintEngineContext::set_shutting_down(bool v) +{ + shutting_down.store(v, std::memory_order_release); +} + std::optional TaintEngineContext::start_request_context() { @@ -171,6 +180,9 @@ TaintEngineContext::get_tainted_object_map(PyObject* obj) TaintedObjectMapTypePtr TaintEngineContext::get_tainted_object_map_from_pyobject(PyObject* tainted_object) { + if (shutting_down.load(std::memory_order_acquire)) { + return nullptr; + } for (const auto& context_map : request_context_slots) { if (!context_map) { continue; diff --git a/ddtrace/appsec/_iast/_taint_tracking/context/taint_engine_context.h b/ddtrace/appsec/_iast/_taint_tracking/context/taint_engine_context.h index 6c2eab01deb..5a083ec0e23 100644 --- a/ddtrace/appsec/_iast/_taint_tracking/context/taint_engine_context.h +++ b/ddtrace/appsec/_iast/_taint_tracking/context/taint_engine_context.h @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -49,9 +50,15 @@ class TaintEngineContext // Parse and clamp capacity from environment static size_t assign_request_context_slots_size(); + // Global lifecycle flag to avoid use-after-destruction during interpreter/module teardown. + static std::atomic shutting_down; + public: TaintEngineContext(); + // Lifecycle control: mark the context as shutting down to prevent further access. + static void set_shutting_down(bool v); + // Fast-path: get the taint map for a known context_id (slot index). // Returns nullptr if the slot is empty or out of lifecycle. TaintedObjectMapTypePtr get_tainted_object_map_by_ctx_id(size_t ctx_id); diff --git a/ddtrace/appsec/_iast/_taint_tracking/native.cpp b/ddtrace/appsec/_iast/_taint_tracking/native.cpp index 295f188c499..61a10e2ca8e 100644 --- a/ddtrace/appsec/_iast/_taint_tracking/native.cpp +++ b/ddtrace/appsec/_iast/_taint_tracking/native.cpp @@ -72,6 +72,25 @@ PYBIND11_MODULE(_native, m) // Create a atexit callback to cleanup the Initializer before the interpreter finishes auto atexit_register = safe_import("atexit", "register"); atexit_register(py::cpp_function([]() { + // During interpreter shutdown (esp. with gevent), heavy cleanup can + // trigger refcounting or Python API calls without a valid runtime. + // If gevent monkey-patching is active, skip setting the shutdown flag + // because it interferes with greenlet scheduling at exit. + + bool gevent_active = false; + try { + auto is_patched = safe_import("gevent.monkey", "is_module_patched"); + gevent_active = + asbool(is_patched("threading")) || asbool(is_patched("socket")) || asbool(is_patched("ssl")); + } catch (const py::error_already_set&) { + PyErr_Clear(); + } + + if (!gevent_active) { + py::gil_scoped_acquire gil; // safe to touch Python-adjacent state + TaintEngineContext::set_shutting_down(true); + } + initializer.reset(); if (taint_engine_context) { taint_engine_context->clear_all_request_context_slots(); diff --git a/riotfile.py b/riotfile.py index c410d9dbc98..8dfa700a4a6 100644 --- a/riotfile.py +++ b/riotfile.py @@ -319,8 +319,7 @@ def select_pys(min_version: str = MIN_PYTHON_VERSION, max_version: str = MAX_PYT ), Venv( name="appsec_iast_default", - # TODO(avara1986): remove "-vvv --no-ddtrace --no-cov" when CI visibility errors were fixed in #14581 - command="pytest -vvv --no-ddtrace --no-cov {cmdargs} tests/appsec/iast/", + command="pytest -v {cmdargs} tests/appsec/iast/", pys=select_pys(), pkgs={ "requests": latest,