Commit 3e16e51
fix(iast): guard taint queries without a context (#18996)
## Description
This fixes a recurring crash where IAST dereferences a Python type
object that has already been freed by the cyclic GC.
```
Error UnixSignal: Process terminated with SEGV_MAPERR (SIGSEGV)
#0 0x00006553759b6e73 PyType_IsSubtype (/usr/src/python/Objects/typeobject.c:2137)
#1 0x0000000000000000 PyObject_TypeCheck (/opt/python/cp312-cp312/include/python3.12/object.h:381)
#2 0x0000000000000000 is_text(_object const*) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/appsec/_iast/_taint_tracking/utils/string_utils.h:66)
#3 0x0000000000000000 is_text(_object const*) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/appsec/_iast/_taint_tracking/utils/string_utils.h:51)
#4 0x00007f20d5851de1 PyObject_TypeCheck (/opt/python/cp312-cp312/include/python3.12/object.h:381)
#5 0x00007f20d5851de1 is_text(_object const*) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/appsec/_iast/_taint_tracking/utils/string_utils.h:66)
#6 0x00007f20d5851de1 is_text(_object const*) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/appsec/_iast/_taint_tracking/utils/string_utils.h:51)
#7 0x00007f20d5851de1 TaintEngineContext::get_tainted_object_map(_object*) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/appsec/_iast/_taint_tracking/context/taint_engine_context.cpp:141)
#8 0x0000000000000000 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/shared_ptr_base.h:732)
#9 0x0000000000000000 std::__shared_ptr<absl::lts_20250127::node_hash_map<unsigned long, std::pair<long, std::shared_ptr<TaintedObject> >, absl::lts_20250127::hash_internal::Hash<unsigned long>, std::equal_to<unsigned long>, std::allocator<std::pair<unsigned long const, std::pair<long, std::shared_ptr<TaintedObject> > > > >, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/shared_ptr_base.h:1183)
#10 0x0000000000000000 std::shared_ptr<absl::lts_20250127::node_hash_map<unsigned long, std::pair<long, std::shared_ptr<TaintedObject> >, absl::lts_20250127::hash_internal::Hash<unsigned long>, std::equal_to<unsigned long>, std::allocator<std::pair<unsigned long const, std::pair<long, std::shared_ptr<TaintedObject> > > > > >::~shared_ptr() (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/shared_ptr.h:121)
#11 0x0000000000000000 operator() (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/appsec/_iast/_taint_tracking/context/taint_engine_context.cpp:357)
#12 0x0000000000000000 call_impl<bool, pyexport_taint_engine_context(pybind11::module&)::<lambda(pybind11::object)>&, 0, pybind11::detail::void_type> (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/appsec/_iast/_taint_tracking/_vendor/pybind11/include/pybind11/cast.h:2137)
#13 0x0000000000000000 call<bool, pybind11::detail::void_type, pyexport_taint_engine_context(pybind11::module&)::<lambda(pybind11::object)>&> (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/appsec/_iast/_taint_tracking/_vendor/pybind11/include/pybind11/cast.h:2105)
#14 0x0000000000000000 operator() (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/appsec/_iast/_taint_tracking/_vendor/pybind11/include/pybind11/pybind11.h:430)
#15 0x00007f20d5852a70 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/shared_ptr_base.h:732)
#16 0x00007f20d5852a70 std::__shared_ptr<absl::lts_20250127::node_hash_map<unsigned long, std::pair<long, std::shared_ptr<TaintedObject> >, absl::lts_20250127::hash_internal::Hash<unsigned long>, std::equal_to<unsigned long>, std::allocator<std::pair<unsigned long const, std::pair<long, std::shared_ptr<TaintedObject> > > > >, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/shared_ptr_base.h:1183)
#17 0x00007f20d5852a70 std::shared_ptr<absl::lts_20250127::node_hash_map<unsigned long, std::pair<long, std::shared_ptr<TaintedObject> >, absl::lts_20250127::hash_internal::Hash<unsigned long>, std::equal_to<unsigned long>, std::allocator<std::pair<unsigned long const, std::pair<long, std::shared_ptr<TaintedObject> > > > > >::~shared_ptr() (/opt/rh/devtoolset-10/root/usr/include/c++/10/bits/shared_ptr.h:121)
#18 0x00007f20d5852a70 operator() (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/appsec/_iast/_taint_tracking/context/taint_engine_context.cpp:357)
#19 0x00007f20d5852a70 call_impl<bool, pyexport_taint_engine_context(pybind11::module&)::<lambda(pybind11::object)>&, 0, pybind11::detail::void_type> (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/appsec/_iast/_taint_tracking/_vendor/pybind11/include/pybind11/cast.h:2137)
#20 0x00007f20d5852a70 call<bool, pybind11::detail::void_type, pyexport_taint_engine_context(pybind11::module&)::<lambda(pybind11::object)>&> (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/appsec/_iast/_taint_tracking/_vendor/pybind11/include/pybind11/cast.h:2105)
#21 0x00007f20d5852a70 operator() (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/appsec/_iast/_taint_tracking/_vendor/pybind11/include/pybind11/pybind11.h:430)
#22 0x00007f20d5852a70 pybind11::cpp_function::initialize<pyexport_taint_engine_context(pybind11::module_&)::{lambda(pybind11::object)#5}, bool, pybind11::object, pybind11::name, pybind11::scope, pybind11::sibling>(pyexport_taint_engine_context(pybind11::module_&)::{lambda(pybind11::object&&)#5}, bool (*)(pybind11::object), pybind11::name const, pybind11::scope&, pybind11::sibling)::{lambda(pybind11::detail::function_call&)#3}::_FUN(pybind11::detail::function_call&) [clone .cold] (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/appsec/_iast/_taint_tracking/_vendor/pybind11/include/pybind11/pybind11.h:400)
#23 0x00007f20d5809d00 pybind11::cpp_function::dispatcher(_object*, _object*, _object*) (/go/src/github.com/DataDog/apm-reliability/dd-trace-py/ddtrace/appsec/_iast/_taint_tracking/_vendor/pybind11/include/pybind11/pybind11.h:1063)
#24 0x00006553759af7cf cfunction_call (/usr/src/python/Objects/methodobject.c:551)
#25 0x00006553759a05de _PyObject_MakeTpCall (/usr/src/python/Objects/call.c:245)
#26 0x00006553759c7357 _PyEval_EvalFrameDefault (/usr/src/python/Python/bytecodes.c:2719)
#27 0x0000655375a8fa91 _PyObject_VectorcallTstate (/usr/src/python/./Include/internal/pycore_call.h:93)
#28 0x0000655375984894 partial_vectorcall (/usr/src/python/./Modules/_functoolsmodule.c:269)
#29 0x00006553759a0ad4 object_vacall (/usr/src/python/Objects/call.c:850)
#30 0x00006553759fdd3e PyObject_CallFunctionObjArgs (/usr/src/python/Objects/call.c:961)
#31 0x00007f20ded79d0a WraptBoundFunctionWrapper_call (/project/src/wrapt/_wrappers.c:3024)
#32 0x00006553759a05de _PyObject_MakeTpCall (/usr/src/python/Objects/call.c:245)
#33 0x00006553759c7357 _PyEval_EvalFrameDefault (/usr/src/python/Python/bytecodes.c:2719)
#34 0x00006553759a3a86 gen_send_ex2 (/usr/src/python/Objects/genobject.c:238)
#35 0x00007f20e038fdc7 task_step_impl (/usr/src/python/Modules/_asynciomodule.c:2869)
#36 0x00007f20e03905a2 task_step (/usr/src/python/Modules/_asynciomodule.c:3188)
#37 0x0000000000000000 __Pyx_PyObject_Call (/project/uvloop/loop.c:191431)
#38 0x00007f20c37988eb __Pyx_PyObject_Call (/project/uvloop/loop.c:191431)
#39 0x00007f20c37988eb __Pyx_PyObject_FastCallDict (/project/uvloop/loop.c:191552)
#40 0x00007f20c3874a69 __pyx_f_6uvloop_4loop_6Handle__run (/project/uvloop/loop.c:66873)
#41 0x00007f20c387896b __pyx_f_6uvloop_4loop_4Loop__on_idle (/project/uvloop/loop.c:17975)
#42 0x00007f20c3872e52 __pyx_f_6uvloop_4loop_6Handle__run (/project/uvloop/loop.c:66927)
#43 0x00007f20c3874c88 __pyx_f_6uvloop_4loop_cb_idle_callback (/project/uvloop/loop.c:87335)
#44 0x0000000000000000 uv__queue_empty (/project/build/libuv-x86_64/src/queue.h:33)
#45 0x00007f20c3890311 uv__queue_empty (/project/build/libuv-x86_64/src/queue.h:33)
#46 0x00007f20c3890311 uv__run_idle (/project/build/libuv-x86_64/src/unix/loop-watcher.c:68)
#47 0x00007f20c388d647 uv_run (/project/build/libuv-x86_64/src/unix/core.c:440)
#48 0x00007f20c37aedb5 __pyx_f_6uvloop_4loop_4Loop__Loop__run (/project/uvloop/loop.c:18471)
#49 0x00007f20c3816e50 __pyx_f_6uvloop_4loop_4Loop__run (/project/uvloop/loop.c:18876)
#50 0x0000000000000000 __pyx_pf_6uvloop_4loop_4Loop_24run_forever (/project/uvloop/loop.c:31528)
#51 0x00007f20c3827cf0 __pyx_pf_6uvloop_4loop_4Loop_24run_forever (/project/uvloop/loop.c:31528)
#52 0x00007f20c3827cf0 __pyx_pw_6uvloop_4loop_4Loop_25run_forever (/project/uvloop/loop.c:31331)
#53 0x00006553759a147c PyObject_VectorcallMethod (/usr/src/python/Objects/call.c:887)
#54 0x0000000000000000 Py_DECREF (/opt/_internal/cpython-3.12.11/include/python3.12/object.h:700)
#55 0x0000000000000000 Py_XDECREF (/opt/_internal/cpython-3.12.11/include/python3.12/object.h:798)
#56 0x00007f20c382bd60 Py_DECREF (/opt/_internal/cpython-3.12.11/include/python3.12/object.h:700)
#57 0x00007f20c382bd60 Py_XDECREF (/opt/_internal/cpython-3.12.11/include/python3.12/object.h:798)
#58 0x00007f20c382bd60 __pyx_pf_6uvloop_4loop_4Loop_44run_until_complete (/project/uvloop/loop.c:33769)
#59 0x0000000000000000 Py_XDECREF (/opt/_internal/cpython-3.12.11/include/python3.12/object.h:797)
#60 0x00007f20c382d591 Py_XDECREF (/opt/_internal/cpython-3.12.11/include/python3.12/object.h:797)
#61 0x00007f20c382d591 __pyx_pw_6uvloop_4loop_4Loop_45run_until_complete (/project/uvloop/loop.c:33322)
#62 0x00006553759a08f8 PyObject_Vectorcall (/usr/src/python/Objects/call.c:325)
#63 0x00006553759c7357 _PyEval_EvalFrameDefault (/usr/src/python/Python/bytecodes.c:2719)
#64 0x00006553759a3a86 gen_send_ex2 (/usr/src/python/Objects/genobject.c:238)
#65 0x00007f20e038fdc7 task_step_impl (/usr/src/python/Modules/_asynciomodule.c:2869)
#66 0x00007f20e03905a2 task_step (/usr/src/python/Modules/_asynciomodule.c:3188)
#67 0x00006553759a05de _PyObject_MakeTpCall (/usr/src/python/Objects/call.c:245)
#68 0x000065537592377a context_run (/usr/src/python/Python/context.c:668)
#69 0x0000655375a12beb cfunction_vectorcall_FASTCALL_KEYWORDS (/usr/src/python/Objects/methodobject.c:439)
#70 0x0000655375919275 _PyEval_EvalFrameDefault (/usr/src/python/Python/bytecodes.c:3227)
#71 0x0000655375a4a679 PyEval_EvalCode (/usr/src/python/Python/ceval.c:579)
#72 0x0000655375a682bc run_eval_code_obj (/usr/src/python/Python/pythonrun.c:1723)
#73 0x0000655375a68234 run_mod (/usr/src/python/Python/pythonrun.c:1744)
#74 0x0000655375a67df1 pyrun_file (/usr/src/python/Python/pythonrun.c:1643)
#75 0x0000655375a67c37 _PyRun_SimpleFileObject (/usr/src/python/Python/pythonrun.c:433)
#76 0x0000655375a67a57 _PyRun_AnyFileObject (/usr/src/python/Python/pythonrun.c:78)
#77 0x0000655375a71fb0 Py_RunMain (/usr/src/python/Modules/main.c:713)
#78 0x0000655375a71b3d Py_BytesMain (/usr/src/python/Modules/main.c:768)
#79 0x00007f20e1519d90 __libc_start_call_main (sysdeps/nptl/libc_start_call_main.h:58)
#80 0x0000000000000000 call_init (csu/libc-start.c:128)
#81 0x00007f20e1519e40 call_init (csu/libc-start.c:128)
#82 0x00007f20e1519e40 __libc_start_main_alias_2 (csu/libc-start.c:379)
#83 0x00006553759ea075 _start
```
Root cause (as identified by LLM):
- `is_pyobject_tainted` is forwarded to the native `is_in_taint_map(obj,
context_id)`
- When no IAST request context is active, `context_id` is `None`, so the
native binding takes the multi-slot fallback
`get_tainted_object_map(obj)`, whose very first step is `is_text(obj)`
- `is_text` validates the object pointer (non-null, aligned, non-null
`ob_type`) but cannot detect a freed type
- `PyUnicode_Check` → `PyObject_TypeCheck` → `PyType_IsSubtype` then
walks the dangling type and faults.
This happens when the query is invoked from an object finalizer /
async-generator teardown run by the GC (`uvloop`'s `run_until_complete`
is in the reported stack), i.e. mid-lifecycle rather than at interpreter
exit, so the existing `shutting_down` guard (added in #18534, only set
from the `atexit` handler) does not cover it.
No other code seems to be affected by this specific issue.
**Note** the bug affects at least versions 3.19 and 4.8, so I will
backport all the way to 4.8 (and we can do 3.19 if AppSec think it's
worth doing).
### Changes
- `is_pyobject_tainted` now early-returns `False` when
`is_iast_request_enabled()` is false, matching `get_tainted_ranges`.
- The `get_ranges` public wrapper (`_taint_tracking/__init__.py`):
short-circuit to `[]` when the resolved `context_id` is `None`, so
no-request callers can't reach the same `is_text`-on-a-freed-type native
path.
- Added regression tests: no-context query does not consult native; a
`__del__`/GC finalizer querying taint with no active context is safe and
returns `False`; raw `get_ranges` returns empty without a context.
<!--
- Reworked `test_in_taint_map_scans_container_across_active_maps` →
`test_is_pyobject_tainted_is_scoped_to_active_slot`: the old test relied
on the now-removed no-context multi-slot scan (the crash vector). It now
asserts per-slot scoped visibility plus no-context → `False`. The
genuine container-scan capability remains covered by the native
`GetCurrentContextMap_{List,Tuple,Dict}Scan` C++ tests, which run with
an active context.
-->
Co-authored-by: Alberto Vara <[email protected]>1 parent dd06019 commit 3e16e51
5 files changed
Lines changed: 107 additions & 15 deletions
File tree
- ddtrace/appsec/_iast/_taint_tracking
- releasenotes/notes
- scripts
- tests/appsec/iast/taint_tracking
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
59 | 59 | | |
60 | 60 | | |
61 | 61 | | |
| 62 | + | |
| 63 | + | |
62 | 64 | | |
63 | 65 | | |
64 | 66 | | |
| |||
Lines changed: 4 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
| |||
77 | 78 | | |
78 | 79 | | |
79 | 80 | | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
80 | 84 | | |
81 | 85 | | |
82 | 86 | | |
| |||
Lines changed: 5 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
31 | | - | |
| 31 | + | |
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
1 | 3 | | |
2 | 4 | | |
3 | 5 | | |
4 | 6 | | |
| 7 | + | |
5 | 8 | | |
| 9 | + | |
6 | 10 | | |
7 | 11 | | |
8 | 12 | | |
| |||
81 | 85 | | |
82 | 86 | | |
83 | 87 | | |
84 | | - | |
85 | 88 | | |
86 | 89 | | |
87 | 90 | | |
88 | | - | |
| 91 | + | |
89 | 92 | | |
90 | 93 | | |
91 | 94 | | |
| |||
108 | 111 | | |
109 | 112 | | |
110 | 113 | | |
111 | | - | |
| 114 | + | |
| 115 | + | |
112 | 116 | | |
113 | 117 | | |
114 | 118 | | |
115 | | - | |
116 | 119 | | |
117 | 120 | | |
118 | 121 | | |
119 | 122 | | |
120 | | - | |
121 | | - | |
122 | | - | |
123 | | - | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
124 | 134 | | |
125 | | - | |
126 | | - | |
127 | | - | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
128 | 143 | | |
129 | 144 | | |
130 | 145 | | |
| |||
162 | 177 | | |
163 | 178 | | |
164 | 179 | | |
165 | | - | |
166 | | - | |
167 | 180 | | |
168 | 181 | | |
169 | 182 | | |
| |||
203 | 216 | | |
204 | 217 | | |
205 | 218 | | |
206 | | - | |
207 | 219 | | |
208 | 220 | | |
209 | 221 | | |
| |||
229 | 241 | | |
230 | 242 | | |
231 | 243 | | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
0 commit comments