From f5d8f27929c82de97fcef202f9c4b353c474fc54 Mon Sep 17 00:00:00 2001 From: Stella Huang Date: Thu, 28 May 2026 14:11:11 -0700 Subject: [PATCH 1/2] Fix TSA #2816217: suppress Flawfinder false positive on Cython JoinPyUnicode memcpy Flawfinder's buffer/memcpy rule (CWE-120) fires on any memcpy() call by default. The flagged call sits inside the Cython 3.x string-join helper __Pyx_PyUnicode_Join: memcpy((char *)result_udata + (char_pos << kind_shift), udata, (size_t) (ulength << kind_shift)); It is provably safe: * result_uval was just allocated via PyUnicode_New(result_ulength, max_char) and result_udata = PyUnicode_DATA(result_uval) points into that buffer. * The immediately preceding check (PY_SSIZE_T_MAX >> kind_shift) - ulength < char_pos guards against char_pos+ulength overflow before the memcpy executes. * result_ulength is computed by the caller as the sum of input lengths, so char_pos + ulength <= result_ulength after each iteration. The byte count `ulength << kind_shift` is bounded by the allocated buffer. Add an inline /* Flawfinder: ignore */ annotation on the flagged line in the Cython-generated _pydevd_sys_monitoring_cython.c and extend the existing post-processing block in setup_pydevd_cython.py so the annotation is re-applied automatically whenever Cython regenerates the .c files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../_pydevd_sys_monitoring_cython.c | 2 +- src/debugpy/_vendored/pydevd/setup_pydevd_cython.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/debugpy/_vendored/pydevd/_pydevd_sys_monitoring/_pydevd_sys_monitoring_cython.c b/src/debugpy/_vendored/pydevd/_pydevd_sys_monitoring/_pydevd_sys_monitoring_cython.c index 30b927110..840b668d1 100644 --- a/src/debugpy/_vendored/pydevd/_pydevd_sys_monitoring/_pydevd_sys_monitoring_cython.c +++ b/src/debugpy/_vendored/pydevd/_pydevd_sys_monitoring/_pydevd_sys_monitoring_cython.c @@ -38185,7 +38185,7 @@ static PyObject* __Pyx_PyUnicode_Join(PyObject** values, Py_ssize_t value_count, ukind = __Pyx_PyUnicode_KIND(uval); udata = __Pyx_PyUnicode_DATA(uval); if (ukind == result_ukind) { - memcpy((char *)result_udata + (char_pos << kind_shift), udata, (size_t) (ulength << kind_shift)); + memcpy((char *)result_udata + (char_pos << kind_shift), udata, (size_t) (ulength << kind_shift)); /* Flawfinder: ignore */ } else { #if PY_VERSION_HEX >= 0x030d0000 if (unlikely(PyUnicode_CopyCharacters(result_uval, char_pos, uval, 0, ulength) < 0)) goto bad; diff --git a/src/debugpy/_vendored/pydevd/setup_pydevd_cython.py b/src/debugpy/_vendored/pydevd/setup_pydevd_cython.py index 14f73f4f8..a478e8bb3 100644 --- a/src/debugpy/_vendored/pydevd/setup_pydevd_cython.py +++ b/src/debugpy/_vendored/pydevd/setup_pydevd_cython.py @@ -177,6 +177,17 @@ def build_extension(dir_name, extension_name, target_pydevd_name, force_cython, c_file_contents = c_file_contents.replace(r"_pydevd_bundle\\pydevd_cython.pxd", "_pydevd_bundle/pydevd_cython.pxd") c_file_contents = c_file_contents.replace(r"_pydevd_bundle\\pydevd_cython.pyx", "_pydevd_bundle/pydevd_cython.pyx") + # Suppress Flawfinder false positive (CWE-120) in the Cython 3.x + # `__Pyx_PyUnicode_Join` boilerplate: the destination `result_uval` was just + # allocated via `PyUnicode_New(result_ulength, max_char)`, and the immediately + # preceding `(PY_SSIZE_T_MAX >> kind_shift) - ulength < char_pos` check guards + # against char_pos+ulength overflow before the memcpy. The size argument is + # `ulength << kind_shift` which is bounded by the pre-allocated buffer length. + c_file_contents = c_file_contents.replace( + " memcpy((char *)result_udata + (char_pos << kind_shift), udata, (size_t) (ulength << kind_shift));\n", + " memcpy((char *)result_udata + (char_pos << kind_shift), udata, (size_t) (ulength << kind_shift)); /* Flawfinder: ignore */\n", + ) + with open(c_file, "w") as stream: stream.write(c_file_contents) From 6bebc9d1185635d359f441eacd0a3ca99c395716 Mon Sep 17 00:00:00 2001 From: Stella Huang Date: Thu, 28 May 2026 16:49:50 -0700 Subject: [PATCH 2/2] Fix SyntaxError: add missing closing paren on JoinPyUnicode .replace() call The merge from main inadvertently dropped the closing ')' of the new JoinPyUnicode '.replace(...)' call, so the subsequent 'read