-
-
Notifications
You must be signed in to change notification settings - Fork 18.9k
BUG: fix memory leak in JSON datetime serialization #62217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
791bb41
f7b2f5b
f324bd8
15c19c0
1ea0283
0925e6b
62b0e26
0f20b7d
05f4926
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -347,7 +347,8 @@ static const char *PyDateTimeToIsoCallback(JSOBJ obj, JSONTypeContext *tc, | |
} | ||
|
||
NPY_DATETIMEUNIT base = ((PyObjectEncoder *)tc->encoder)->datetimeUnit; | ||
return PyDateTimeToIso(obj, base, len); | ||
GET_TC(tc)->cStr = PyDateTimeToIso(obj, base, len); | ||
return GET_TC(tc)->cStr; | ||
} | ||
|
||
static const char *PyTimeToJSON(JSOBJ _obj, JSONTypeContext *tc, | ||
|
@@ -1007,16 +1008,24 @@ static const char *List_iterGetName(JSOBJ Py_UNUSED(obj), | |
//============================================================================= | ||
static void Index_iterBegin(JSOBJ Py_UNUSED(obj), JSONTypeContext *tc) { | ||
GET_TC(tc)->index = 0; | ||
GET_TC(tc)->cStr = PyObject_Malloc(20); | ||
if (!GET_TC(tc)->cStr) { | ||
PyErr_NoMemory(); | ||
} | ||
} | ||
|
||
static int Index_iterNext(JSOBJ obj, JSONTypeContext *tc) { | ||
const Py_ssize_t index = GET_TC(tc)->index; | ||
Py_XDECREF(GET_TC(tc)->itemValue); | ||
if (!GET_TC(tc)->cStr) { | ||
return 0; | ||
} | ||
|
||
if (index == 0) { | ||
GET_TC(tc)->cStr = "name"; | ||
strcpy((char *)GET_TC(tc)->cStr, "name"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think if you want to use strcpy here you will need to drop the const qualifier on the struct member; casting away const-ness like this is going to trigger undefined behavior There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed the |
||
GET_TC(tc)->itemValue = PyObject_GetAttrString(obj, "name"); | ||
} else if (index == 1) { | ||
GET_TC(tc)->cStr = "data"; | ||
strcpy((char *)GET_TC(tc)->cStr, "data"); | ||
GET_TC(tc)->itemValue = get_values(obj); | ||
if (!GET_TC(tc)->itemValue) { | ||
return 0; | ||
|
@@ -1049,19 +1058,27 @@ static void Series_iterBegin(JSOBJ Py_UNUSED(obj), JSONTypeContext *tc) { | |
PyObjectEncoder *enc = (PyObjectEncoder *)tc->encoder; | ||
GET_TC(tc)->index = 0; | ||
enc->outputFormat = VALUES; // for contained series | ||
GET_TC(tc)->cStr = PyObject_Malloc(20); | ||
if (!GET_TC(tc)->cStr) { | ||
PyErr_NoMemory(); | ||
} | ||
} | ||
|
||
static int Series_iterNext(JSOBJ obj, JSONTypeContext *tc) { | ||
const Py_ssize_t index = GET_TC(tc)->index; | ||
Py_XDECREF(GET_TC(tc)->itemValue); | ||
if (!GET_TC(tc)->cStr) { | ||
return 0; | ||
} | ||
|
||
if (index == 0) { | ||
GET_TC(tc)->cStr = "name"; | ||
strcpy((char *)GET_TC(tc)->cStr, "name"); | ||
GET_TC(tc)->itemValue = PyObject_GetAttrString(obj, "name"); | ||
} else if (index == 1) { | ||
GET_TC(tc)->cStr = "index"; | ||
strcpy((char *)GET_TC(tc)->cStr, "index"); | ||
GET_TC(tc)->itemValue = PyObject_GetAttrString(obj, "index"); | ||
} else if (index == 2) { | ||
GET_TC(tc)->cStr = "data"; | ||
strcpy((char *)GET_TC(tc)->cStr, "data"); | ||
GET_TC(tc)->itemValue = get_values(obj); | ||
if (!GET_TC(tc)->itemValue) { | ||
return 0; | ||
|
@@ -1096,19 +1113,27 @@ static void DataFrame_iterBegin(JSOBJ Py_UNUSED(obj), JSONTypeContext *tc) { | |
PyObjectEncoder *enc = (PyObjectEncoder *)tc->encoder; | ||
GET_TC(tc)->index = 0; | ||
enc->outputFormat = VALUES; // for contained series & index | ||
GET_TC(tc)->cStr = PyObject_Malloc(20); | ||
if (!GET_TC(tc)->cStr) { | ||
PyErr_NoMemory(); | ||
} | ||
} | ||
|
||
static int DataFrame_iterNext(JSOBJ obj, JSONTypeContext *tc) { | ||
const Py_ssize_t index = GET_TC(tc)->index; | ||
Py_XDECREF(GET_TC(tc)->itemValue); | ||
if (!GET_TC(tc)->cStr) { | ||
return 0; | ||
} | ||
|
||
if (index == 0) { | ||
GET_TC(tc)->cStr = "columns"; | ||
strcpy((char *)GET_TC(tc)->cStr, "columns"); | ||
GET_TC(tc)->itemValue = PyObject_GetAttrString(obj, "columns"); | ||
} else if (index == 1) { | ||
GET_TC(tc)->cStr = "index"; | ||
strcpy((char *)GET_TC(tc)->cStr, "index"); | ||
GET_TC(tc)->itemValue = PyObject_GetAttrString(obj, "index"); | ||
} else if (index == 2) { | ||
GET_TC(tc)->cStr = "data"; | ||
strcpy((char *)GET_TC(tc)->cStr, "data"); | ||
Py_INCREF(obj); | ||
GET_TC(tc)->itemValue = obj; | ||
} else { | ||
|
@@ -1880,6 +1905,7 @@ static void Object_endTypeContext(JSOBJ Py_UNUSED(obj), JSONTypeContext *tc) { | |
GET_TC(tc)->rowLabels = NULL; | ||
NpyArr_freeLabels(GET_TC(tc)->columnLabels, GET_TC(tc)->columnLabelsLen); | ||
GET_TC(tc)->columnLabels = NULL; | ||
PyObject_Free((void *)GET_TC(tc)->cStr); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not too sure about this approach - generally the CPython API only wants you to call this function when you've allocated with the corresponding function(s) https://docs.python.org/3/c-api/memory.html#c.PyObject_Free
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah sorry I misread your PR. I see you allocate 20 bytes to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unless I'm missing something, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why cast to a void pointer here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was because of the const qualifier in |
||
GET_TC(tc)->cStr = NULL; | ||
PyObject_Free(tc->prv); | ||
tc->prv = NULL; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its probably better to create a static const variable for a size within the module, rather than repeating the hard-coded 20 multiple times
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I created the variable
CSTR_SIZE