From e80219afcf6a9aedf8ea5997b2474d14a87d92c8 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 4 Jun 2026 15:31:31 -0700 Subject: [PATCH 1/9] Only show definite leaks to reduce the number of suppressions we need to save. When running Python with valgrind, PYTHONMALLOC=malloc, and the default suppressions file in python/cpython, most of the false positive leaks are possibly lost and a few are reachable. I believe memory leaks are more likely to happen in the Python client with definitely lost leaks; I don't think the client moves the pointer to heap allocated memory that much, which would lead to possibly lost leaks. Also I don't believe reachable leaks would usually lead to an out of memory error. --- .github/workflows/test-artifact.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-artifact.yml b/.github/workflows/test-artifact.yml index c66d58ad64..1c62b05212 100644 --- a/.github/workflows/test-artifact.yml +++ b/.github/workflows/test-artifact.yml @@ -200,7 +200,7 @@ jobs: wget -P test "https://raw.githubusercontent.com/python/cpython/$PYTHON_VERSION/Misc/$VALGRIND_SUPP_FILE" # sed -i "127,185s/###//" "$VALGRIND_SUPP_FILE" - echo VALGRIND_ARGS="$VALGRIND_ARGS --suppressions=./$VALGRIND_SUPP_FILE --leak-check=full --errors-for-leak-kinds=none" >> "$GITHUB_ENV" + echo VALGRIND_ARGS="$VALGRIND_ARGS --suppressions=./$VALGRIND_SUPP_FILE --show-leak-kinds=definite --leak-check=full --errors-for-leak-kinds=none" >> "$GITHUB_ENV" fi fi env: From 9a0ec453d06fc1b8d3143f9038b85a7bf204e9d5 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 5 Jun 2026 07:48:02 -0700 Subject: [PATCH 2/9] Generate suppressions so it's easier to append to the existing valgrind suppressions file in this repo --- .github/workflows/test-artifact.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-artifact.yml b/.github/workflows/test-artifact.yml index 1c62b05212..228d945eaa 100644 --- a/.github/workflows/test-artifact.yml +++ b/.github/workflows/test-artifact.yml @@ -200,7 +200,7 @@ jobs: wget -P test "https://raw.githubusercontent.com/python/cpython/$PYTHON_VERSION/Misc/$VALGRIND_SUPP_FILE" # sed -i "127,185s/###//" "$VALGRIND_SUPP_FILE" - echo VALGRIND_ARGS="$VALGRIND_ARGS --suppressions=./$VALGRIND_SUPP_FILE --show-leak-kinds=definite --leak-check=full --errors-for-leak-kinds=none" >> "$GITHUB_ENV" + echo VALGRIND_ARGS="$VALGRIND_ARGS --gen-suppressions=all --suppressions=./$VALGRIND_SUPP_FILE --show-leak-kinds=definite --leak-check=full --errors-for-leak-kinds=none" >> "$GITHUB_ENV" fi fi env: From 780ed26628edf1e258a2d62fc9ab6bfbfb8839c7 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 22 Jun 2026 15:46:14 -0700 Subject: [PATCH 3/9] Throw an error when a definite leak is found if our suppressions file is supposed to detect all definite leaks. --- .github/workflows/test-artifact.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-artifact.yml b/.github/workflows/test-artifact.yml index 228d945eaa..b3ad8bbabe 100644 --- a/.github/workflows/test-artifact.yml +++ b/.github/workflows/test-artifact.yml @@ -200,7 +200,7 @@ jobs: wget -P test "https://raw.githubusercontent.com/python/cpython/$PYTHON_VERSION/Misc/$VALGRIND_SUPP_FILE" # sed -i "127,185s/###//" "$VALGRIND_SUPP_FILE" - echo VALGRIND_ARGS="$VALGRIND_ARGS --gen-suppressions=all --suppressions=./$VALGRIND_SUPP_FILE --show-leak-kinds=definite --leak-check=full --errors-for-leak-kinds=none" >> "$GITHUB_ENV" + echo VALGRIND_ARGS="$VALGRIND_ARGS --gen-suppressions=all --suppressions=./$VALGRIND_SUPP_FILE --show-leak-kinds=definite --leak-check=full --errors-for-leak-kinds=definite" >> "$GITHUB_ENV" fi fi env: From 1861f1f27d97dc3cb63f9ec6d480a704b11701b9 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 22 Jun 2026 15:49:07 -0700 Subject: [PATCH 4/9] Add default valgrind-python.supp from cpython repo @ branch for 3.10 --- test/valgrind-python.supp | 498 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 498 insertions(+) create mode 100644 test/valgrind-python.supp diff --git a/test/valgrind-python.supp b/test/valgrind-python.supp new file mode 100644 index 0000000000..faac3cbdf1 --- /dev/null +++ b/test/valgrind-python.supp @@ -0,0 +1,498 @@ +# +# This is a valgrind suppression file that should be used when using valgrind. +# +# Here's an example of running valgrind: +# +# cd python/dist/src +# valgrind --tool=memcheck --suppressions=Misc/valgrind-python.supp \ +# ./python -E ./Lib/test/regrtest.py -u gui,network +# +# You must edit Objects/obmalloc.c and uncomment Py_USING_MEMORY_DEBUGGER +# to use the preferred suppressions with address_in_range. +# +# If you do not want to recompile Python, you can uncomment +# suppressions for _PyObject_Free and _PyObject_Realloc. +# +# See Misc/README.valgrind for more information. + +# all tool names: Addrcheck,Memcheck,cachegrind,helgrind,massif +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Addr4 + fun:address_in_range +} + +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Value4 + fun:address_in_range +} + +{ + ADDRESS_IN_RANGE/Invalid read of size 8 (x86_64 aka amd64) + Memcheck:Value8 + fun:address_in_range +} + +{ + ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value + Memcheck:Cond + fun:address_in_range +} + +# +# Leaks (including possible leaks) +# Hmmm, I wonder if this masks some real leaks. I think it does. +# Will need to fix that. +# + +{ + Suppress leaking the GIL. Happens once per process, see comment in ceval.c. + Memcheck:Leak + fun:malloc + fun:PyThread_allocate_lock + fun:PyEval_InitThreads +} + +{ + Suppress leaking the GIL after a fork. + Memcheck:Leak + fun:malloc + fun:PyThread_allocate_lock + fun:PyEval_ReInitThreads +} + +{ + Suppress leaking the autoTLSkey. This looks like it shouldn't leak though. + Memcheck:Leak + fun:malloc + fun:PyThread_create_key + fun:_PyGILState_Init + fun:Py_InitializeEx + fun:Py_Main +} + +{ + Hmmm, is this a real leak or like the GIL? + Memcheck:Leak + fun:malloc + fun:PyThread_ReInitTLS +} + +{ + Handle PyMalloc confusing valgrind (possibly leaked) + Memcheck:Leak + fun:realloc + fun:_PyObject_GC_Resize + fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING +} + +{ + Handle PyMalloc confusing valgrind (possibly leaked) + Memcheck:Leak + fun:malloc + fun:_PyObject_GC_New + fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING +} + +{ + Handle PyMalloc confusing valgrind (possibly leaked) + Memcheck:Leak + fun:malloc + fun:_PyObject_GC_NewVar + fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING +} + +# +# Non-python specific leaks +# + +{ + Handle pthread issue (possibly leaked) + Memcheck:Leak + fun:calloc + fun:allocate_dtv + fun:_dl_allocate_tls_storage + fun:_dl_allocate_tls +} + +{ + Handle pthread issue (possibly leaked) + Memcheck:Leak + fun:memalign + fun:_dl_allocate_tls_storage + fun:_dl_allocate_tls +} + +###{ +### ADDRESS_IN_RANGE/Invalid read of size 4 +### Memcheck:Addr4 +### fun:_PyObject_Free +###} +### +###{ +### ADDRESS_IN_RANGE/Invalid read of size 4 +### Memcheck:Value4 +### fun:_PyObject_Free +###} +### +###{ +### ADDRESS_IN_RANGE/Use of uninitialised value of size 8 +### Memcheck:Addr8 +### fun:_PyObject_Free +###} +### +###{ +### ADDRESS_IN_RANGE/Use of uninitialised value of size 8 +### Memcheck:Value8 +### fun:_PyObject_Free +###} +### +###{ +### ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value +### Memcheck:Cond +### fun:_PyObject_Free +###} + +###{ +### ADDRESS_IN_RANGE/Invalid read of size 4 +### Memcheck:Addr4 +### fun:_PyObject_Realloc +###} +### +###{ +### ADDRESS_IN_RANGE/Invalid read of size 4 +### Memcheck:Value4 +### fun:_PyObject_Realloc +###} +### +###{ +### ADDRESS_IN_RANGE/Use of uninitialised value of size 8 +### Memcheck:Addr8 +### fun:_PyObject_Realloc +###} +### +###{ +### ADDRESS_IN_RANGE/Use of uninitialised value of size 8 +### Memcheck:Value8 +### fun:_PyObject_Realloc +###} +### +###{ +### ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value +### Memcheck:Cond +### fun:_PyObject_Realloc +###} + +### +### All the suppressions below are for errors that occur within libraries +### that Python uses. The problems to not appear to be related to Python's +### use of the libraries. +### + +{ + Generic ubuntu ld problems + Memcheck:Addr8 + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so + obj:/lib/ld-2.4.so +} + +{ + Generic gentoo ld problems + Memcheck:Cond + obj:/lib/ld-2.3.4.so + obj:/lib/ld-2.3.4.so + obj:/lib/ld-2.3.4.so + obj:/lib/ld-2.3.4.so +} + +{ + DBM problems, see test_dbm + Memcheck:Param + write(buf) + fun:write + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + fun:dbm_close +} + +{ + DBM problems, see test_dbm + Memcheck:Value8 + fun:memmove + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + fun:dbm_store + fun:dbm_ass_sub +} + +{ + DBM problems, see test_dbm + Memcheck:Cond + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + fun:dbm_store + fun:dbm_ass_sub +} + +{ + DBM problems, see test_dbm + Memcheck:Cond + fun:memmove + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + obj:/usr/lib/libdb1.so.2 + fun:dbm_store + fun:dbm_ass_sub +} + +{ + GDBM problems, see test_gdbm + Memcheck:Param + write(buf) + fun:write + fun:gdbm_open + +} + +{ + Uninitialised byte(s) false alarm, see bpo-35561 + Memcheck:Param + epoll_ctl(event) + fun:epoll_ctl + fun:pyepoll_internal_ctl +} + +{ + ZLIB problems, see test_gzip + Memcheck:Cond + obj:/lib/libz.so.1.2.3 + obj:/lib/libz.so.1.2.3 + fun:deflate +} + +{ + Avoid problems w/readline doing a putenv and leaking on exit + Memcheck:Leak + fun:malloc + fun:xmalloc + fun:sh_set_lines_and_columns + fun:_rl_get_screen_size + fun:_rl_init_terminal_io + obj:/lib/libreadline.so.4.3 + fun:rl_initialize +} + +# Valgrind emits "Conditional jump or move depends on uninitialised value(s)" +# false alarms on GCC builtin strcmp() function. The GCC code is correct. +# +# Valgrind bug: https://bugs.kde.org/show_bug.cgi?id=264936 +{ + bpo-38118: Valgrind emits false alarm on GCC builtin strcmp() + Memcheck:Cond + fun:PyUnicode_Decode +} + + +### +### These occur from somewhere within the SSL, when running +### test_socket_sll. They are too general to leave on by default. +### +###{ +### somewhere in SSL stuff +### Memcheck:Cond +### fun:memset +###} +###{ +### somewhere in SSL stuff +### Memcheck:Value4 +### fun:memset +###} +### +###{ +### somewhere in SSL stuff +### Memcheck:Cond +### fun:MD5_Update +###} +### +###{ +### somewhere in SSL stuff +### Memcheck:Value4 +### fun:MD5_Update +###} + +# Fedora's package "openssl-1.0.1-0.1.beta2.fc17.x86_64" on x86_64 +# See http://bugs.python.org/issue14171 +{ + openssl 1.0.1 prng 1 + Memcheck:Cond + fun:bcmp + fun:fips_get_entropy + fun:FIPS_drbg_instantiate + fun:RAND_init_fips + fun:OPENSSL_init_library + fun:SSL_library_init + fun:init_hashlib +} + +{ + openssl 1.0.1 prng 2 + Memcheck:Cond + fun:fips_get_entropy + fun:FIPS_drbg_instantiate + fun:RAND_init_fips + fun:OPENSSL_init_library + fun:SSL_library_init + fun:init_hashlib +} + +{ + openssl 1.0.1 prng 3 + Memcheck:Value8 + fun:_x86_64_AES_encrypt_compact + fun:AES_encrypt +} + +# +# All of these problems come from using test_socket_ssl +# +{ + from test_socket_ssl + Memcheck:Cond + fun:BN_bin2bn +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:BN_num_bits_word +} + +{ + from test_socket_ssl + Memcheck:Value4 + fun:BN_num_bits_word +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:BN_mod_exp_mont_word +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:BN_mod_exp_mont +} + +{ + from test_socket_ssl + Memcheck:Param + write(buf) + fun:write + obj:/usr/lib/libcrypto.so.0.9.7 +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:RSA_verify +} + +{ + from test_socket_ssl + Memcheck:Value4 + fun:RSA_verify +} + +{ + from test_socket_ssl + Memcheck:Value4 + fun:DES_set_key_unchecked +} + +{ + from test_socket_ssl + Memcheck:Value4 + fun:DES_encrypt2 +} + +{ + from test_socket_ssl + Memcheck:Cond + obj:/usr/lib/libssl.so.0.9.7 +} + +{ + from test_socket_ssl + Memcheck:Value4 + obj:/usr/lib/libssl.so.0.9.7 +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:BUF_MEM_grow_clean +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:memcpy + fun:ssl3_read_bytes +} + +{ + from test_socket_ssl + Memcheck:Cond + fun:SHA1_Update +} + +{ + from test_socket_ssl + Memcheck:Value4 + fun:SHA1_Update +} + +{ + test_buffer_non_debug + Memcheck:Addr4 + fun:PyUnicodeUCS2_FSConverter +} + +{ + test_buffer_non_debug + Memcheck:Addr4 + fun:PyUnicode_FSConverter +} + +{ + wcscmp_false_positive + Memcheck:Addr8 + fun:wcscmp + fun:_PyOS_GetOpt + fun:Py_Main + fun:main +} + +# Additional suppressions for the unified decimal tests: +{ + test_decimal + Memcheck:Addr4 + fun:PyUnicodeUCS2_FSConverter +} + +{ + test_decimal2 + Memcheck:Addr4 + fun:PyUnicode_FSConverter +} From 9d0654014cc065e9a46e5525d3fca615b4e33a2a Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 22 Jun 2026 15:51:24 -0700 Subject: [PATCH 5/9] Rm step that downloads valgrind suppressions file from cpython repo now that it is vendored in this repo. --- .github/workflows/test-artifact.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/test-artifact.yml b/.github/workflows/test-artifact.yml index b3ad8bbabe..daba370afb 100644 --- a/.github/workflows/test-artifact.yml +++ b/.github/workflows/test-artifact.yml @@ -196,10 +196,6 @@ jobs: echo VALGRIND_ARGS="$VALGRIND_ARGS --tool=massif --massif-out-file=./${MASSIF_REPORT_FILE_NAME}" >> "$GITHUB_ENV" else VALGRIND_SUPP_FILE=valgrind-python.supp - - wget -P test "https://raw.githubusercontent.com/python/cpython/$PYTHON_VERSION/Misc/$VALGRIND_SUPP_FILE" - # sed -i "127,185s/###//" "$VALGRIND_SUPP_FILE" - echo VALGRIND_ARGS="$VALGRIND_ARGS --gen-suppressions=all --suppressions=./$VALGRIND_SUPP_FILE --show-leak-kinds=definite --leak-check=full --errors-for-leak-kinds=definite" >> "$GITHUB_ENV" fi fi From 6fb96eab96bef62743d711f03bf095691ee2ed18 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 23 Jun 2026 08:33:26 -0700 Subject: [PATCH 6/9] Add generated suppressions from HEAD~ to suppressions file. TODO - rerun valgrind multiple times to see if these suppressions are good enough to generate a stable baseline. --- test/valgrind-python.supp | 458 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 458 insertions(+) diff --git a/test/valgrind-python.supp b/test/valgrind-python.supp index faac3cbdf1..393aa7c071 100644 --- a/test/valgrind-python.supp +++ b/test/valgrind-python.supp @@ -496,3 +496,461 @@ Memcheck:Addr4 fun:PyUnicode_FSConverter } + +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:PyFloat_FromDouble + fun:parsenumber_raw + fun:parsenumber + fun:_PyPegen_number_token + fun:atom_rule + fun:primary_raw + fun:primary_rule + fun:await_primary_rule + fun:power_rule + fun:factor_rule + fun:term_raw + fun:term_rule + fun:sum_raw + fun:sum_rule + fun:shift_expr_raw + fun:shift_expr_rule +} + +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:_PyLong_New + fun:PyLong_FromSsize_t + fun:long_mul + fun:long_pow + fun:ternary_op + fun:PyNumber_Power + fun:safe_power + fun:fold_binop + fun:astfold_expr + fun:astfold_expr + fun:astfold_stmt + fun:astfold_body + fun:astfold_stmt + fun:astfold_body + fun:astfold_mod + fun:_PyAST_Optimize + fun:_PyAST_Compile + fun:builtin_compile_impl + fun:builtin_compile.cold +} + +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:PyUnicode_New + fun:unicode_decode_utf8 + fun:_PyPegen_parsestr + fun:_PyPegen_concatenate_strings + fun:strings_rule + fun:atom_rule + fun:primary_raw + fun:primary_rule + fun:await_primary_rule + fun:power_rule + fun:factor_rule + fun:term_raw + fun:term_rule + fun:sum_raw + fun:sum_rule +} + +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:_PyObject_GC_Alloc + fun:_PyObject_GC_Malloc + fun:_PyObject_GC_New + fun:new_dict + fun:PyDict_New + fun:_PyDict_NewPresized + fun:_PyStack_AsDict + fun:_PyObject_MakeTpCall + fun:_PyObject_VectorcallTstate + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:_PyFunction_Vectorcall + fun:_PyObject_FastCallDictTstate + fun:_PyObject_Call_Prepend + fun:slot_tp_init + fun:type_call + fun:_PyObject_MakeTpCall + fun:_PyObject_VectorcallTstate + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector +} + +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:_PyObject_GC_Alloc + fun:_PyObject_GC_Malloc + fun:_PyObject_GC_New + fun:new_dict + fun:new_dict_with_shared_keys + fun:_PyObjectDict_SetItem + fun:_PyObject_GenericSetAttrWithDict + fun:PyObject_GenericSetAttr + fun:PyObject_SetAttr + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:_PyFunction_Vectorcall + fun:_PyObject_FastCallDictTstate + fun:_PyObject_Call_Prepend + fun:slot_tp_init + fun:type_call + fun:_PyObject_MakeTpCall + fun:_PyObject_VectorcallTstate + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault +} + +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:_PyObject_GC_Alloc + fun:_PyObject_GC_Malloc + fun:_PyObject_GC_New + fun:new_dict + fun:PyDict_New + fun:_PyDict_NewPresized + fun:_PyStack_AsDict + fun:_PyObject_MakeTpCall + fun:_PyObject_VectorcallTstate + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:_PyObject_VectorcallTstate + fun:method_vectorcall + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault +} + +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:_PyObject_GC_Alloc + fun:_PyObject_GC_Malloc + fun:_PyObject_GC_New + fun:new_dict + fun:new_dict_with_shared_keys + fun:_PyObjectDict_SetItem + fun:_PyObject_GenericSetAttrWithDict + fun:PyObject_GenericSetAttr + fun:PyObject_SetAttr + fun:ast2obj_arguments + fun:ast2obj_stmt + fun:ast2obj_list + fun:ast2obj_mod + fun:PyAST_mod2obj + fun:Py_CompileStringObject + fun:builtin_compile_impl + fun:builtin_compile + fun:cfunction_vectorcall_FASTCALL_KEYWORDS +} + +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:_PyObject_GC_Alloc + fun:_PyObject_GC_Malloc + fun:_PyObject_GC_New + fun:new_dict + fun:PyDict_New + fun:_PyEval_MakeFrameVector + fun:_PyEval_Vector + fun:_PyObject_VectorcallTstate + fun:method_vectorcall + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:_PyFunction_Vectorcall + fun:_PyObject_FastCallDictTstate + fun:_PyObject_Call_Prepend + fun:slot_tp_init + fun:type_call + fun:PyObject_Call +} + +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:_PyObject_GC_Alloc + fun:_PyObject_GC_Malloc + fun:_PyObject_GC_New + fun:new_dict + fun:PyDict_New + fun:_PyDict_NewPresized + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:_PyObject_VectorcallTstate + fun:method_vectorcall +} + +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:_PyObject_GC_Alloc + fun:_PyObject_GC_Malloc + fun:_PyObject_GC_New + fun:new_dict + fun:PyDict_New + fun:_PyObjectDict_SetItem + fun:_PyObject_GenericSetAttrWithDict + fun:PyObject_GenericSetAttr + fun:PyObject_SetAttr + fun:ast2obj_expr + fun:ast2obj_expr + fun:ast2obj_list + fun:ast2obj_expr + fun:ast2obj_expr.cold + fun:ast2obj_stmt.cold + fun:ast2obj_list + fun:ast2obj_stmt + fun:ast2obj_list + fun:ast2obj_mod + fun:PyAST_mod2obj +} + +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:_PyObject_GC_Alloc + fun:_PyObject_GC_Malloc + fun:_PyObject_GC_New + fun:new_dict + fun:PyDict_New + fun:_PyEval_MakeFrameVector + fun:_PyEval_Vector + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:_PyObject_VectorcallTstate + fun:method_vectorcall + fun:do_call_core + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:do_call_core + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector +} + +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:_PyLong_New + fun:_PyLong_Init + fun:pycore_init_singletons + fun:pycore_interp_init + fun:pyinit_config + fun:pyinit_core.constprop.0 + fun:Py_InitializeFromConfig + fun:pymain_init + fun:pymain_main + fun:Py_BytesMain + fun:(below main) +} + +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:_PyObject_GC_Alloc + fun:_PyObject_GC_Malloc + fun:_PyObject_GC_New + fun:new_dict + fun:PyDict_New + fun:_PyDict_NewPresized + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault +} + +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:_PyObject_GC_Alloc + fun:_PyObject_GC_Malloc + fun:_PyObject_GC_NewVar + fun:tuple_alloc + fun:PyTuple_New + fun:error_to_pyobject + fun:raise_exception_base + fun:AerospikeClient_Prepend + fun:method_vectorcall_VARARGS_KEYWORDS + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:_PyObject_VectorcallTstate + fun:method_vectorcall + fun:do_call_core + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector +} + +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:PyUnicode_New + fun:unicode_repeat + fun:unicode_repeat + fun:safe_multiply + fun:fold_binop + fun:astfold_expr + fun:astfold_expr + fun:astfold_keyword + fun:astfold_expr + fun:astfold_stmt + fun:astfold_body + fun:astfold_stmt + fun:astfold_body + fun:astfold_stmt + fun:astfold_body + fun:astfold_mod + fun:_PyAST_Optimize +} + +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:PyUnicode_New + fun:_PyUnicode_JoinArray + fun:PyUnicode_Join + fun:join_list_unicode + fun:flush_accumulator + fun:_PyAccu_FinishAsList + fun:encoder_call + fun:_PyObject_MakeTpCall + fun:_PyObject_VectorcallTstate + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault + fun:_PyEval_EvalFrame + fun:_PyEval_Vector + fun:_PyObject_VectorcallTstate + fun:method_vectorcall + fun:_PyObject_VectorcallTstate + fun:PyObject_Vectorcall + fun:call_function + fun:_PyEval_EvalFrameDefault +} From a14713016064c62eb3b4874a6577cc5d52e5f561 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 23 Jun 2026 11:24:02 -0700 Subject: [PATCH 7/9] Add PSF copyright notice and summary of changes to the original valgrind suppressions file --- test/valgrind-python.supp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/valgrind-python.supp b/test/valgrind-python.supp index 393aa7c071..6df1943c6a 100644 --- a/test/valgrind-python.supp +++ b/test/valgrind-python.supp @@ -1,3 +1,6 @@ +# Copyright (c) 2001 Python Software Foundation; All Rights Reserved +# +# Summary of changes: Added custom suppressions blocks for running the Python client integration tests # # This is a valgrind suppression file that should be used when using valgrind. # From 2d3532803d253f02f579c736956e9e46afb7f5de Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 23 Jun 2026 11:26:58 -0700 Subject: [PATCH 8/9] Add comment showing where the custom suppressions begin. --- test/valgrind-python.supp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/valgrind-python.supp b/test/valgrind-python.supp index 6df1943c6a..466adf4ae6 100644 --- a/test/valgrind-python.supp +++ b/test/valgrind-python.supp @@ -500,6 +500,8 @@ fun:PyUnicode_FSConverter } +# Below are the custom suppressions for the Python client + { Memcheck:Leak From 85686229b314ea03d4afe9a6981a4366afc77cb5 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 23 Jun 2026 11:34:48 -0700 Subject: [PATCH 9/9] Try increasing num-callers to 500 for the definite leaks to see if they originate from the Python client. --- .github/workflows/test-artifact.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-artifact.yml b/.github/workflows/test-artifact.yml index daba370afb..dbd446c1e5 100644 --- a/.github/workflows/test-artifact.yml +++ b/.github/workflows/test-artifact.yml @@ -196,7 +196,7 @@ jobs: echo VALGRIND_ARGS="$VALGRIND_ARGS --tool=massif --massif-out-file=./${MASSIF_REPORT_FILE_NAME}" >> "$GITHUB_ENV" else VALGRIND_SUPP_FILE=valgrind-python.supp - echo VALGRIND_ARGS="$VALGRIND_ARGS --gen-suppressions=all --suppressions=./$VALGRIND_SUPP_FILE --show-leak-kinds=definite --leak-check=full --errors-for-leak-kinds=definite" >> "$GITHUB_ENV" + echo VALGRIND_ARGS="$VALGRIND_ARGS --gen-suppressions=all --num-callers=500 --show-leak-kinds=definite --leak-check=full --errors-for-leak-kinds=definite" >> "$GITHUB_ENV" fi fi env: