Skip to content

test_*_code functions in _testcapi/getargs.c have memory leaks#110572

@sobolevn

Description

@sobolevn

Bug report

I don't think it is very important, since this is just a test, but why have it when it is spotted?

  1. test_k_code:
    /* This function not only tests the 'k' getargs code, but also the
    PyLong_AsUnsignedLongMask() function. */
    staticPyObject*
    test_k_code(PyObject*self, PyObject*Py_UNUSED(ignored))
    {
    PyObject*tuple, *num;
    unsigned longvalue;
    tuple=PyTuple_New(1);
    if (tuple==NULL){
    returnNULL;
    }
    /* a number larger than ULONG_MAX even on 64-bit platforms */
    num=PyLong_FromString("FFFFFFFFFFFFFFFFFFFFFFFF", NULL, 16);
    if (num==NULL){
    returnNULL;
    }
    value=PyLong_AsUnsignedLongMask(num);
    if (value!=ULONG_MAX){
    PyErr_SetString(PyExc_AssertionError,
    "test_k_code: "
    "PyLong_AsUnsignedLongMask() returned wrong value for long 0xFFF...FFF");
    returnNULL;
    }
    PyTuple_SET_ITEM(tuple, 0, num);
    value=0;
    if (!PyArg_ParseTuple(tuple, "k:test_k_code", &value)){
    returnNULL;
    }
    if (value!=ULONG_MAX){
    PyErr_SetString(PyExc_AssertionError,
    "test_k_code: k code returned wrong value for long 0xFFF...FFF");
    returnNULL;
    }
    Py_DECREF(num);
    num=PyLong_FromString("-FFFFFFFF000000000000000042", NULL, 16);
    if (num==NULL){
    returnNULL;
    }
    value=PyLong_AsUnsignedLongMask(num);
    if (value!= (unsigned long)-0x42){
    PyErr_SetString(PyExc_AssertionError,
    "test_k_code: "
    "PyLong_AsUnsignedLongMask() returned wrong value for long -0xFFF..000042");
    returnNULL;
    }
    PyTuple_SET_ITEM(tuple, 0, num);
    value=0;
    if (!PyArg_ParseTuple(tuple, "k:test_k_code", &value)){
    returnNULL;
    }
    if (value!= (unsigned long)-0x42){
    PyErr_SetString(PyExc_AssertionError,
    "test_k_code: k code returned wrong value for long -0xFFF..000042");
    returnNULL;
    }
    Py_DECREF(tuple);
    Py_RETURN_NONE;
    }

On errors tuple is not decrefed.
Also, note these lines:

PyTuple_SET_ITEM(tuple, 0, num);
value=0;
if (!PyArg_ParseTuple(tuple, "k:test_k_code", &value)){
returnNULL;
}
if (value!=ULONG_MAX){
PyErr_SetString(PyExc_AssertionError,
"test_k_code: k code returned wrong value for long 0xFFF...FFF");
returnNULL;
}
Py_DECREF(num);
num=PyLong_FromString("-FFFFFFFF000000000000000042", NULL, 16);
if (num==NULL){
returnNULL;
}

Here' we leave a tuple is a semi-broken state. Its 0'th item has a reference count of 0.
We should also recreate a tuple here with the new items. test_L_code also has this problem.

  1. staticPyObject*
    test_L_code(PyObject*self, PyObject*Py_UNUSED(ignored))
    {
    PyObject*tuple, *num;
    long longvalue;
    tuple=PyTuple_New(1);
    if (tuple==NULL){
    returnNULL;
    }
    num=PyLong_FromLong(42);
    if (num==NULL){
    returnNULL;
    }
    PyTuple_SET_ITEM(tuple, 0, num);
    value=-1;
    if (!PyArg_ParseTuple(tuple, "L:test_L_code", &value)){
    returnNULL;
    }
    if (value!=42){
    PyErr_SetString(PyExc_AssertionError,
    "test_L_code: L code returned wrong value for long 42");
    returnNULL;
    }
    Py_DECREF(num);
    num=PyLong_FromLong(42);
    if (num==NULL){
    returnNULL;
    }
    PyTuple_SET_ITEM(tuple, 0, num);
    value=-1;
    if (!PyArg_ParseTuple(tuple, "L:test_L_code", &value)){
    returnNULL;
    }
    if (value!=42){
    PyErr_SetString(PyExc_AssertionError,
    "test_L_code: L code returned wrong value for int 42");
    returnNULL;
    }
    Py_DECREF(tuple);
    Py_RETURN_NONE;
    }

On errors tuple is not decrefed. And num is re-assigned without tuple cleanup.

  1. /* Test the s and z codes for PyArg_ParseTuple.
    */
    staticPyObject*
    test_s_code(PyObject*self, PyObject*Py_UNUSED(ignored))
    {
    /* Unicode strings should be accepted */
    PyObject*tuple=PyTuple_New(1);
    if (tuple==NULL){
    returnNULL;
    }
    PyObject*obj=PyUnicode_Decode("t\xeate", strlen("t\xeate"),
    "latin-1", NULL);
    if (obj==NULL){
    returnNULL;
    }
    PyTuple_SET_ITEM(tuple, 0, obj);
    /* These two blocks used to raise a TypeError:
    * "argument must be string without null bytes, not str"
    */
    char*value;
    if (!PyArg_ParseTuple(tuple, "s:test_s_code1", &value)){
    returnNULL;
    }
    if (!PyArg_ParseTuple(tuple, "z:test_s_code2", &value)){
    returnNULL;
    }
    Py_DECREF(tuple);
    Py_RETURN_NONE;
    }

As well, tuple is leaked on errors.

I have a PR ready.

Linked PRs

Metadata

Metadata

Assignees

Labels

testsTests in the Lib/test dirtype-bugAn unexpected behavior, bug, or error

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions