From 791bb4126b26780565ad0f2bd8516e8e754b8a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Fri, 29 Aug 2025 15:10:55 -0300 Subject: [PATCH 1/7] fix: memory leak in JSON datetime serialization --- pandas/_libs/src/vendored/ujson/python/objToJSON.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pandas/_libs/src/vendored/ujson/python/objToJSON.c b/pandas/_libs/src/vendored/ujson/python/objToJSON.c index 8342dbcd1763d..f34e4a68852b3 100644 --- a/pandas/_libs/src/vendored/ujson/python/objToJSON.c +++ b/pandas/_libs/src/vendored/ujson/python/objToJSON.c @@ -107,6 +107,7 @@ typedef struct __TypeContext { JSINT64 longValue; const char *cStr; + int freeCStr; NpyArrContext *npyarr; PdBlockContext *pdblock; int transpose; @@ -162,6 +163,7 @@ static TypeContext *createTypeContext(void) { pc->longValue = 0; pc->doubleValue = 0.0; pc->cStr = NULL; + pc->freeCStr = 0; pc->npyarr = NULL; pc->pdblock = NULL; pc->rowLabels = NULL; @@ -327,6 +329,7 @@ static const char *NpyDateTimeToIsoCallback(JSOBJ Py_UNUSED(unused), NPY_DATETIMEUNIT base = ((PyObjectEncoder *)tc->encoder)->datetimeUnit; NPY_DATETIMEUNIT valueUnit = ((PyObjectEncoder *)tc->encoder)->valueUnit; GET_TC(tc)->cStr = int64ToIso(GET_TC(tc)->longValue, valueUnit, base, len); + GET_TC(tc)->freeCStr = 1; return GET_TC(tc)->cStr; } @@ -334,6 +337,7 @@ static const char *NpyDateTimeToIsoCallback(JSOBJ Py_UNUSED(unused), static const char *NpyTimeDeltaToIsoCallback(JSOBJ Py_UNUSED(unused), JSONTypeContext *tc, size_t *len) { GET_TC(tc)->cStr = int64ToIsoDuration(GET_TC(tc)->longValue, len); + GET_TC(tc)->freeCStr = 1; return GET_TC(tc)->cStr; } @@ -347,7 +351,9 @@ 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); + GET_TC(tc)->freeCStr = 1; + return GET_TC(tc)->cStr; } static const char *PyTimeToJSON(JSOBJ _obj, JSONTypeContext *tc, @@ -1880,6 +1886,9 @@ 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; + if (GET_TC(tc)->freeCStr) { + PyObject_Free((void *)GET_TC(tc)->cStr); + } GET_TC(tc)->cStr = NULL; PyObject_Free(tc->prv); tc->prv = NULL; From f7b2f5b2e8b61994405dc34919fe642bbe5e732f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Fri, 29 Aug 2025 18:43:43 -0300 Subject: [PATCH 2/7] fix: flag to free `cStr` in `Object_getBigNumStringValue` --- pandas/_libs/src/vendored/ujson/python/objToJSON.c | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/_libs/src/vendored/ujson/python/objToJSON.c b/pandas/_libs/src/vendored/ujson/python/objToJSON.c index f34e4a68852b3..7f34976605fe3 100644 --- a/pandas/_libs/src/vendored/ujson/python/objToJSON.c +++ b/pandas/_libs/src/vendored/ujson/python/objToJSON.c @@ -1915,6 +1915,7 @@ static const char *Object_getBigNumStringValue(JSOBJ obj, JSONTypeContext *tc, char *bytes = PyObject_Malloc(*_outLen + 1); memcpy(bytes, str, *_outLen + 1); GET_TC(tc)->cStr = bytes; + GET_TC(tc)->freeCStr = 1; Py_DECREF(repr); From f324bd85aab46b63dc2bb09b846c64f59b5f4c78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Tue, 2 Sep 2025 11:15:11 -0300 Subject: [PATCH 3/7] fix: use dynamic allocated memory to store strings Revert "fix: flag to free `cStr` in `Object_getBigNumStringValue`" Revert "fix: memory leak in JSON datetime serialization" fix: fix memory leak in `PyDateTimeToIsoCallback` fix: ensure successful malloc. --- .../src/vendored/ujson/python/objToJSON.c | 50 ++++++++++++------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/pandas/_libs/src/vendored/ujson/python/objToJSON.c b/pandas/_libs/src/vendored/ujson/python/objToJSON.c index 7f34976605fe3..9ef2d54d88a37 100644 --- a/pandas/_libs/src/vendored/ujson/python/objToJSON.c +++ b/pandas/_libs/src/vendored/ujson/python/objToJSON.c @@ -107,7 +107,6 @@ typedef struct __TypeContext { JSINT64 longValue; const char *cStr; - int freeCStr; NpyArrContext *npyarr; PdBlockContext *pdblock; int transpose; @@ -163,7 +162,6 @@ static TypeContext *createTypeContext(void) { pc->longValue = 0; pc->doubleValue = 0.0; pc->cStr = NULL; - pc->freeCStr = 0; pc->npyarr = NULL; pc->pdblock = NULL; pc->rowLabels = NULL; @@ -329,7 +327,6 @@ static const char *NpyDateTimeToIsoCallback(JSOBJ Py_UNUSED(unused), NPY_DATETIMEUNIT base = ((PyObjectEncoder *)tc->encoder)->datetimeUnit; NPY_DATETIMEUNIT valueUnit = ((PyObjectEncoder *)tc->encoder)->valueUnit; GET_TC(tc)->cStr = int64ToIso(GET_TC(tc)->longValue, valueUnit, base, len); - GET_TC(tc)->freeCStr = 1; return GET_TC(tc)->cStr; } @@ -337,7 +334,6 @@ static const char *NpyDateTimeToIsoCallback(JSOBJ Py_UNUSED(unused), static const char *NpyTimeDeltaToIsoCallback(JSOBJ Py_UNUSED(unused), JSONTypeContext *tc, size_t *len) { GET_TC(tc)->cStr = int64ToIsoDuration(GET_TC(tc)->longValue, len); - GET_TC(tc)->freeCStr = 1; return GET_TC(tc)->cStr; } @@ -352,7 +348,6 @@ static const char *PyDateTimeToIsoCallback(JSOBJ obj, JSONTypeContext *tc, NPY_DATETIMEUNIT base = ((PyObjectEncoder *)tc->encoder)->datetimeUnit; GET_TC(tc)->cStr = PyDateTimeToIso(obj, base, len); - GET_TC(tc)->freeCStr = 1; return GET_TC(tc)->cStr; } @@ -1013,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"); 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; @@ -1055,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; @@ -1102,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 { @@ -1886,9 +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; - if (GET_TC(tc)->freeCStr) { - PyObject_Free((void *)GET_TC(tc)->cStr); - } + PyObject_Free((void *)GET_TC(tc)->cStr); GET_TC(tc)->cStr = NULL; PyObject_Free(tc->prv); tc->prv = NULL; @@ -1915,7 +1932,6 @@ static const char *Object_getBigNumStringValue(JSOBJ obj, JSONTypeContext *tc, char *bytes = PyObject_Malloc(*_outLen + 1); memcpy(bytes, str, *_outLen + 1); GET_TC(tc)->cStr = bytes; - GET_TC(tc)->freeCStr = 1; Py_DECREF(repr); From 15c19c055ea97be70157ea54b35199edc46c2228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Tue, 2 Sep 2025 13:36:12 -0300 Subject: [PATCH 4/7] fix: replace PyObject_Malloc with PyMem_Malloc --- pandas/_libs/src/datetime/date_conversions.c | 4 ++-- pandas/_libs/src/datetime/pd_datetime.c | 2 +- pandas/_libs/src/vendored/ujson/python/objToJSON.c | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pandas/_libs/src/datetime/date_conversions.c b/pandas/_libs/src/datetime/date_conversions.c index 99081746b2c97..36bc9df281b8c 100644 --- a/pandas/_libs/src/datetime/date_conversions.c +++ b/pandas/_libs/src/datetime/date_conversions.c @@ -49,7 +49,7 @@ char *int64ToIso(int64_t value, NPY_DATETIMEUNIT valueUnit, pandas_datetime_to_datetimestruct(value, valueUnit, &dts); *len = (size_t)get_datetime_iso_8601_strlen(0, base); - char *result = PyObject_Malloc(*len); + char *result = PyMem_Malloc(*len); if (result == NULL) { PyErr_NoMemory(); @@ -78,7 +78,7 @@ char *int64ToIsoDuration(int64_t value, size_t *len) { // Max theoretical length of ISO Duration with 64 bit day // as the largest unit is 70 characters + 1 for a null terminator - char *result = PyObject_Malloc(71); + char *result = PyMem_Malloc(71); if (result == NULL) { PyErr_NoMemory(); return NULL; diff --git a/pandas/_libs/src/datetime/pd_datetime.c b/pandas/_libs/src/datetime/pd_datetime.c index addf9c2939133..44c4132cdb507 100644 --- a/pandas/_libs/src/datetime/pd_datetime.c +++ b/pandas/_libs/src/datetime/pd_datetime.c @@ -170,7 +170,7 @@ static char *PyDateTimeToIso(PyObject *obj, NPY_DATETIMEUNIT base, } *len = (size_t)get_datetime_iso_8601_strlen(0, base); - char *result = PyObject_Malloc(*len); + char *result = PyMem_Malloc(*len); // Check to see if PyDateTime has a timezone. // Don't convert to UTC if it doesn't. int is_tz_aware = 0; diff --git a/pandas/_libs/src/vendored/ujson/python/objToJSON.c b/pandas/_libs/src/vendored/ujson/python/objToJSON.c index 9ef2d54d88a37..0ca3779a04464 100644 --- a/pandas/_libs/src/vendored/ujson/python/objToJSON.c +++ b/pandas/_libs/src/vendored/ujson/python/objToJSON.c @@ -1008,7 +1008,7 @@ 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); + GET_TC(tc)->cStr = PyMem_Malloc(20); if (!GET_TC(tc)->cStr) { PyErr_NoMemory(); } @@ -1058,7 +1058,7 @@ 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); + GET_TC(tc)->cStr = PyMem_Malloc(20); if (!GET_TC(tc)->cStr) { PyErr_NoMemory(); } @@ -1113,7 +1113,7 @@ 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); + GET_TC(tc)->cStr = PyMem_Malloc(20); if (!GET_TC(tc)->cStr) { PyErr_NoMemory(); } @@ -1905,7 +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); + PyMem_Free((void *)GET_TC(tc)->cStr); GET_TC(tc)->cStr = NULL; PyObject_Free(tc->prv); tc->prv = NULL; @@ -1929,7 +1929,7 @@ static const char *Object_getBigNumStringValue(JSOBJ obj, JSONTypeContext *tc, size_t *_outLen) { PyObject *repr = PyObject_Str(obj); const char *str = PyUnicode_AsUTF8AndSize(repr, (Py_ssize_t *)_outLen); - char *bytes = PyObject_Malloc(*_outLen + 1); + char *bytes = PyMem_Malloc(*_outLen + 1); memcpy(bytes, str, *_outLen + 1); GET_TC(tc)->cStr = bytes; From 1ea028300c694cf17f0a2dc627112d26c330026e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Tue, 2 Sep 2025 13:53:33 -0300 Subject: [PATCH 5/7] fix: drop `const` qualifier in `cStr` --- .../src/vendored/ujson/python/objToJSON.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pandas/_libs/src/vendored/ujson/python/objToJSON.c b/pandas/_libs/src/vendored/ujson/python/objToJSON.c index 0ca3779a04464..e734f26bcb3dd 100644 --- a/pandas/_libs/src/vendored/ujson/python/objToJSON.c +++ b/pandas/_libs/src/vendored/ujson/python/objToJSON.c @@ -106,7 +106,7 @@ typedef struct __TypeContext { double doubleValue; JSINT64 longValue; - const char *cStr; + char *cStr; NpyArrContext *npyarr; PdBlockContext *pdblock; int transpose; @@ -1022,10 +1022,10 @@ static int Index_iterNext(JSOBJ obj, JSONTypeContext *tc) { } if (index == 0) { - strcpy((char *)GET_TC(tc)->cStr, "name"); + strcpy(GET_TC(tc)->cStr, "name"); GET_TC(tc)->itemValue = PyObject_GetAttrString(obj, "name"); } else if (index == 1) { - strcpy((char *)GET_TC(tc)->cStr, "data"); + strcpy(GET_TC(tc)->cStr, "data"); GET_TC(tc)->itemValue = get_values(obj); if (!GET_TC(tc)->itemValue) { return 0; @@ -1072,13 +1072,13 @@ static int Series_iterNext(JSOBJ obj, JSONTypeContext *tc) { } if (index == 0) { - strcpy((char *)GET_TC(tc)->cStr, "name"); + strcpy(GET_TC(tc)->cStr, "name"); GET_TC(tc)->itemValue = PyObject_GetAttrString(obj, "name"); } else if (index == 1) { - strcpy((char *)GET_TC(tc)->cStr, "index"); + strcpy(GET_TC(tc)->cStr, "index"); GET_TC(tc)->itemValue = PyObject_GetAttrString(obj, "index"); } else if (index == 2) { - strcpy((char *)GET_TC(tc)->cStr, "data"); + strcpy(GET_TC(tc)->cStr, "data"); GET_TC(tc)->itemValue = get_values(obj); if (!GET_TC(tc)->itemValue) { return 0; @@ -1127,13 +1127,13 @@ static int DataFrame_iterNext(JSOBJ obj, JSONTypeContext *tc) { } if (index == 0) { - strcpy((char *)GET_TC(tc)->cStr, "columns"); + strcpy(GET_TC(tc)->cStr, "columns"); GET_TC(tc)->itemValue = PyObject_GetAttrString(obj, "columns"); } else if (index == 1) { - strcpy((char *)GET_TC(tc)->cStr, "index"); + strcpy(GET_TC(tc)->cStr, "index"); GET_TC(tc)->itemValue = PyObject_GetAttrString(obj, "index"); } else if (index == 2) { - strcpy((char *)GET_TC(tc)->cStr, "data"); + strcpy(GET_TC(tc)->cStr, "data"); Py_INCREF(obj); GET_TC(tc)->itemValue = obj; } else { From 0925e6bf32a6f334f67c5d4b1ba52fc587477e3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Tue, 2 Sep 2025 13:57:55 -0300 Subject: [PATCH 6/7] fix: remove void pointer case in PyMem_Free --- pandas/_libs/src/vendored/ujson/python/objToJSON.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/_libs/src/vendored/ujson/python/objToJSON.c b/pandas/_libs/src/vendored/ujson/python/objToJSON.c index e734f26bcb3dd..6e9e07952517c 100644 --- a/pandas/_libs/src/vendored/ujson/python/objToJSON.c +++ b/pandas/_libs/src/vendored/ujson/python/objToJSON.c @@ -1905,7 +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; - PyMem_Free((void *)GET_TC(tc)->cStr); + PyMem_Free(GET_TC(tc)->cStr); GET_TC(tc)->cStr = NULL; PyObject_Free(tc->prv); tc->prv = NULL; From 62b0e26bfe0740117b98af925e656b45d9df0a85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Tue, 2 Sep 2025 14:10:58 -0300 Subject: [PATCH 7/7] fix: create const for default `cStr` size --- pandas/_libs/src/vendored/ujson/python/objToJSON.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pandas/_libs/src/vendored/ujson/python/objToJSON.c b/pandas/_libs/src/vendored/ujson/python/objToJSON.c index 6e9e07952517c..fcb85edfb34da 100644 --- a/pandas/_libs/src/vendored/ujson/python/objToJSON.c +++ b/pandas/_libs/src/vendored/ujson/python/objToJSON.c @@ -51,6 +51,8 @@ Numeric decoder derived from TCL library #include #include +static const int CSTR_SIZE = 20; + npy_int64 get_nat(void) { return NPY_MIN_INT64; } typedef const char *(*PFN_PyTypeToUTF8)(JSOBJ obj, JSONTypeContext *ti, @@ -1008,7 +1010,7 @@ 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 = PyMem_Malloc(20); + GET_TC(tc)->cStr = PyMem_Malloc(CSTR_SIZE); if (!GET_TC(tc)->cStr) { PyErr_NoMemory(); } @@ -1058,7 +1060,7 @@ 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 = PyMem_Malloc(20); + GET_TC(tc)->cStr = PyMem_Malloc(CSTR_SIZE); if (!GET_TC(tc)->cStr) { PyErr_NoMemory(); } @@ -1113,7 +1115,7 @@ 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 = PyMem_Malloc(20); + GET_TC(tc)->cStr = PyMem_Malloc(CSTR_SIZE); if (!GET_TC(tc)->cStr) { PyErr_NoMemory(); }