Skip to content

Memory leak in _gdbm.gdbm.clear() method: missing free() for gdbm_firstkey() result #140272

@ashm-dev

Description

@ashm-dev

Bug report

Bug description:

Bug Description

The _gdbm.gdbm.clear() method has a memory leak detected by LeakSanitizer.
The function calls gdbm_firstkey() in a loop but never frees the memory
allocated by the gdbm library for the returned key.

Location

  • File: Modules/_gdbmmodule.c
  • Function: _gdbm_gdbm_clear_impl() (around line 670)

Root Cause

According to gdbm documentation:

"The storage space for the dptr element is allocated using malloc.
Gdbm does not automatically free this data. It is the programmer's
responsibility to free this storage when it is no longer needed."

The current implementation does not free key.dptr allocated by gdbm_firstkey().

How to Reproduce

Build CPython with AddressSanitizer:

CC=clang CXX=clang++ ./configure --with-address-sanitizer --with-pydebug --with-lto=full --disable-optimizations
make
./python -X dev -m test test_shelve

Expected Behavior

Memory allocated by gdbm_firstkey() should be freed after use.

Actual Behavior

LeakSanitizer detects memory leak in _gdbm_gdbm_clear_impl().

LeakSanitizer output
Using random seed: 3707445279
0:00:00 load avg: 1.46 Run 1 test sequentially in a single process
0:00:00 load avg: 1.46 [1/1] test_shelve
0:00:02 load avg: 1.46 [1/1] test_shelve passed

== Tests result: SUCCESS ==

1 test OK.

Total duration: 2.5 sec
Total tests: run=440 skipped=2
Total test files: run=1/1
Result: SUCCESS

=================================================================
==374446==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 24 byte(s) in 6 object(s) allocated from:
    #0 0x56253b876b18 in malloc (/home/shamil/oss/cpython/main/python+0x33cb18) (BuildId: 426ed01b936e239b573cac4851990fbf7325232b)
    #1 0x6c38f2ab25ea  (/lib/x86_64-linux-gnu/libgdbm.so.6+0xb5ea) (BuildId: d1120256572ee2578404aba32fd025dba1160ee5)
    #2 0x6c38f2ab26af in gdbm_firstkey (/lib/x86_64-linux-gnu/libgdbm.so.6+0xb6af) (BuildId: d1120256572ee2578404aba32fd025dba1160ee5)
    #3 0x6c38f2acebc5 in _gdbm_gdbm_clear_impl /home/shamil/oss/cpython/main/./Modules/_gdbmmodule.c:670:15
    #4 0x6c38f2acebc5 in _gdbm_gdbm_clear /home/shamil/oss/cpython/main/./Modules/clinic/_gdbmmodule.c.h:310:20
    #5 0x56253ba7df68 in _PyObject_VectorcallTstate /home/shamil/oss/cpython/main/./Include/internal/pycore_call.h:169:11
    #6 0x56253be7238d in PyObject_Vectorcall /home/shamil/oss/cpython/main/Objects/call.c:327:12
    #7 0x56253be7238d in _PyEval_EvalFrameDefault /home/shamil/oss/cpython/main/Python/generated_cases.c.h:1620:35
    #8 0x56253be9a841 in _PyEval_EvalFrame /home/shamil/oss/cpython/main/./Include/internal/pycore_ceval.h:121:16
    #9 0x56253be9a841 in _PyEval_Vector /home/shamil/oss/cpython/main/Python/ceval.c:2001:12
    #10 0x56253ba8abb8 in _PyObject_VectorcallTstate.7815 /home/shamil/oss/cpython/main/./Include/internal/pycore_call.h:169:11
    #11 0x56253ba8a70c in method_vectorcall /home/shamil/oss/cpython/main/Objects/classobject.c:95:18
    #12 0x56253be58618 in PyObject_Call /home/shamil/oss/cpython/main/Objects/call.c:373:12
    #13 0x56253be58618 in _PyEval_EvalFrameDefault /home/shamil/oss/cpython/main/Python/generated_cases.c.h:2616:32
    #14 0x56253be9a841 in _PyEval_EvalFrame /home/shamil/oss/cpython/main/./Include/internal/pycore_ceval.h:121:16
    #15 0x56253be9a841 in _PyEval_Vector /home/shamil/oss/cpython/main/Python/ceval.c:2001:12
    #16 0x56253ba7f2d1 in _PyObject_VectorcallDictTstate /home/shamil/oss/cpython/main/Objects/call.c:135:15
    #17 0x56253ba819a5 in _PyObject_Call_Prepend /home/shamil/oss/cpython/main/Objects/call.c:504:24
    #18 0x56253bc58367 in call_method /home/shamil/oss/cpython/main/Objects/typeobject.c:3076:19
    #19 0x56253ba7e346 in _PyObject_MakeTpCall /home/shamil/oss/cpython/main/Objects/call.c:242:18
    #20 0x56253be5ca16 in PyObject_Vectorcall /home/shamil/oss/cpython/main/Objects/call.c:327:12
    #21 0x56253be5ca16 in _PyEval_EvalFrameDefault /home/shamil/oss/cpython/main/Python/generated_cases.c.h:4021:35
    #22 0x56253be9a841 in _PyEval_EvalFrame /home/shamil/oss/cpython/main/./Include/internal/pycore_ceval.h:121:16
    #23 0x56253be9a841 in _PyEval_Vector /home/shamil/oss/cpython/main/Python/ceval.c:2001:12
    #24 0x56253ba8abb8 in _PyObject_VectorcallTstate.7815 /home/shamil/oss/cpython/main/./Include/internal/pycore_call.h:169:11
    #25 0x56253ba8a70c in method_vectorcall /home/shamil/oss/cpython/main/Objects/classobject.c:95:18
    #26 0x56253be58618 in PyObject_Call /home/shamil/oss/cpython/main/Objects/call.c:373:12
    #27 0x56253be58618 in _PyEval_EvalFrameDefault /home/shamil/oss/cpython/main/Python/generated_cases.c.h:2616:32
    #28 0x56253be9a841 in _PyEval_EvalFrame /home/shamil/oss/cpython/main/./Include/internal/pycore_ceval.h:121:16
    #29 0x56253be9a841 in _PyEval_Vector /home/shamil/oss/cpython/main/Python/ceval.c:2001:12
    #30 0x56253ba7f2d1 in _PyObject_VectorcallDictTstate /home/shamil/oss/cpython/main/Objects/call.c:135:15
    #31 0x56253ba819a5 in _PyObject_Call_Prepend /home/shamil/oss/cpython/main/Objects/call.c:504:24
    #32 0x56253bc58367 in call_method /home/shamil/oss/cpython/main/Objects/typeobject.c:3076:19
    #33 0x56253ba7e346 in _PyObject_MakeTpCall /home/shamil/oss/cpython/main/Objects/call.c:242:18
    #34 0x56253be5ca16 in PyObject_Vectorcall /home/shamil/oss/cpython/main/Objects/call.c:327:12
    #35 0x56253be5ca16 in _PyEval_EvalFrameDefault /home/shamil/oss/cpython/main/Python/generated_cases.c.h:4021:35
    #36 0x56253be9a841 in _PyEval_EvalFrame /home/shamil/oss/cpython/main/./Include/internal/pycore_ceval.h:121:16
    #37 0x56253be9a841 in _PyEval_Vector /home/shamil/oss/cpython/main/Python/ceval.c:2001:12
    #38 0x56253ba8abb8 in _PyObject_VectorcallTstate.7815 /home/shamil/oss/cpython/main/./Include/internal/pycore_call.h:169:11
    #39 0x56253ba8a70c in method_vectorcall /home/shamil/oss/cpython/main/Objects/classobject.c:95:18
    #40 0x56253be58618 in PyObject_Call /home/shamil/oss/cpython/main/Objects/call.c:373:12
    #41 0x56253be58618 in _PyEval_EvalFrameDefault /home/shamil/oss/cpython/main/Python/generated_cases.c.h:2616:32

SUMMARY: AddressSanitizer: 24 byte(s) leaked in 6 allocation(s).

Solution

Add free(key.dptr) calls after using the key, both in the success path and error path.
This pattern is already correctly used in _gdbm_gdbm_firstkey_impl() in the same file.

I have a fix ready and will submit a PR.

Related

The clear() method was added in #107122.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    extension-modulesC modules in the Modules dirperformancePerformance or resource usagetype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions