Skip to content

Commit 5d48b8e

Browse files
dd-octo-sts[bot]github-actions[bot]christophe-papazian
authored
fix(aap): fix possible memory corruption in WAF context [backport 4.5] (#16755)
Backport #16740 to 4.5 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Christophe Papazian <114495376+christophe-papazian@users.noreply.github.com>
1 parent bdedb37 commit 5d48b8e

File tree

3 files changed

+17
-1
lines changed

3 files changed

+17
-1
lines changed

ddtrace/appsec/_ddwaf/waf.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,8 @@ def run(
183183
observator = _observator()
184184
wrapper = ddwaf_object(data, observator=observator)
185185
wrapper_ephemeral = ddwaf_object(ephemeral_data, observator=observator) if ephemeral_data else None
186-
error = ddwaf_run(ctx.ctx, wrapper, wrapper_ephemeral, result_obj, int(timeout_ms * 1000))
186+
with ctx._lock:
187+
error = ddwaf_run(ctx.ctx, wrapper, wrapper_ephemeral, result_obj, int(timeout_ms * 1000))
187188
if error < 0:
188189
LOGGER.debug("run DDWAF error: %d\ninput %s\nerror %s", error, wrapper.struct, self.info.errors)
189190
result = result_obj.struct

ddtrace/appsec/_ddwaf/waf_stubs.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from ddtrace.appsec._constants import DEFAULT
1212
from ddtrace.appsec._utils import DDWaf_info
1313
from ddtrace.appsec._utils import DDWaf_result
14+
from ddtrace.internal._unpatched import threading_Lock
1415
from ddtrace.internal.logger import get_logger
1516
from ddtrace.internal.remoteconfig import PayloadType
1617

@@ -46,6 +47,12 @@ def __init__(self, ctx: type[T], free_fn: Callable[[type[T]], None]) -> None:
4647
self.ctx = ctx
4748
self.free_fn = free_fn
4849
self.rc_products: str = ""
50+
# AIDEV-NOTE: This lock serializes concurrent ddwaf_run calls on the same context.
51+
# ctypes foreign function calls release the Python GIL, allowing thread pool workers
52+
# (run_in_executor) to call the WAF simultaneously with the event loop thread when both
53+
# inherit the same ddwaf_context via contextvars propagation. libddwaf's ddwaf_context
54+
# is not thread-safe for concurrent runs, so we must serialize them here.
55+
self._lock: threading_Lock = threading_Lock()
4956

5057
def __del__(self):
5158
if self.ctx:
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
fixes:
3+
- |
4+
AAP: Fixes a memory corruption issue where concurrent calls to the WAF
5+
on the same request context from multiple threads (e.g. an asyncio event loop
6+
and a thread pool executor inheriting the same context via ``contextvars``)
7+
could cause use-after-free or double-free crashes (SIGSEGV) inside
8+
``libddwaf``. A per-context lock now serializes WAF calls on the same context.

0 commit comments

Comments
 (0)