From c2bb8a63aabc5912cfb69bd687bc91d470f1edfa Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Mon, 13 Aug 2018 16:24:52 +0500 Subject: [PATCH 1/2] bpo-34395: Fix memory leaks caused by incautious usage of PyMem_Resize(). --- Modules/_csv.c | 51 ++++++++++++++--------------------------------- Modules/_pickle.c | 13 ++++++------ 2 files changed, 22 insertions(+), 42 deletions(-) diff --git a/Modules/_csv.c b/Modules/_csv.c index f58538c4272979..a2dea80ec773dc 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -555,25 +555,17 @@ parse_save_field(ReaderObj *self) static int parse_grow_buff(ReaderObj *self) { - if (self->field_size == 0) { - self->field_size = 4096; - if (self->field != NULL) - PyMem_Free(self->field); - self->field = PyMem_New(Py_UCS4, self->field_size); - } - else { - Py_UCS4 *field = self->field; - if (self->field_size > PY_SSIZE_T_MAX / 2) { - PyErr_NoMemory(); - return 0; - } - self->field_size *= 2; - self->field = PyMem_Resize(field, Py_UCS4, self->field_size); - } - if (self->field == NULL) { + assert((size_t)self->field_size <= PY_SSIZE_T_MAX / sizeof(Py_UCS4)); + + Py_ssize_t field_size_new = self->field_size ? 2 * self->field_size : 4096; + Py_UCS4 *field_new = self->field; + PyMem_Resize(field_new, Py_UCS4, field_size_new); + if (field_new == NULL) { PyErr_NoMemory(); return 0; } + self->field = field_new; + self->field_size = field_size_new; return 1; } @@ -1089,31 +1081,18 @@ join_append_data(WriterObj *self, unsigned int field_kind, void *field_data, static int join_check_rec_size(WriterObj *self, Py_ssize_t rec_len) { - - if (rec_len < 0 || rec_len > PY_SSIZE_T_MAX - MEM_INCR) { - PyErr_NoMemory(); - return 0; - } + assert(rec_len >= 0); if (rec_len > self->rec_size) { - if (self->rec_size == 0) { - self->rec_size = (rec_len / MEM_INCR + 1) * MEM_INCR; - if (self->rec != NULL) - PyMem_Free(self->rec); - self->rec = PyMem_New(Py_UCS4, self->rec_size); - } - else { - Py_UCS4* old_rec = self->rec; - - self->rec_size = (rec_len / MEM_INCR + 1) * MEM_INCR; - self->rec = PyMem_Resize(old_rec, Py_UCS4, self->rec_size); - if (self->rec == NULL) - PyMem_Free(old_rec); - } - if (self->rec == NULL) { + size_t rec_size_new = (size_t)rec_len + MEM_INCR; + Py_UCS4 *rec_new = self->rec; + PyMem_Resize(rec_new, Py_UCS4, rec_size_new); + if (rec_new == NULL) { PyErr_NoMemory(); return 0; } + self->rec = rec_new; + self->rec_size = (Py_ssize_t)rec_size_new; } return 1; } diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 294f33419cb5b1..1d2ca44a386a05 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -1382,11 +1382,13 @@ _Unpickler_ResizeMemoList(UnpicklerObject *self, Py_ssize_t new_size) assert(new_size > self->memo_size); - PyMem_RESIZE(self->memo, PyObject *, new_size); - if (self->memo == NULL) { + PyObject **memo_new = self->memo; + PyMem_Resize(memo_new, PyObject *, new_size); + if (memo_new == NULL) { PyErr_NoMemory(); return -1; } + self->memo = memo_new; for (i = self->memo_size; i < new_size; i++) self->memo[i] = NULL; self->memo_size = new_size; @@ -6295,11 +6297,10 @@ load_mark(UnpicklerObject *self) return -1; } - if (self->marks == NULL) - self->marks = PyMem_NEW(Py_ssize_t, alloc); - else - PyMem_RESIZE(self->marks, Py_ssize_t, alloc); + Py_ssize_t *marks_old = self->marks; + PyMem_Resize(self->marks, Py_ssize_t, alloc); if (self->marks == NULL) { + PyMem_Free(marks_old); self->marks_size = 0; PyErr_NoMemory(); return -1; From 60ea00598d976aa95906d2d2c4c93788ad543852 Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Tue, 14 Aug 2018 23:22:05 +0500 Subject: [PATCH 2/2] Restored some old behavior. --- Modules/_csv.c | 2 +- Modules/_pickle.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/_csv.c b/Modules/_csv.c index a2dea80ec773dc..4cc1f7c88d8779 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -1084,7 +1084,7 @@ join_check_rec_size(WriterObj *self, Py_ssize_t rec_len) assert(rec_len >= 0); if (rec_len > self->rec_size) { - size_t rec_size_new = (size_t)rec_len + MEM_INCR; + size_t rec_size_new = (size_t)(rec_len / MEM_INCR + 1) * MEM_INCR; Py_UCS4 *rec_new = self->rec; PyMem_Resize(rec_new, Py_UCS4, rec_size_new); if (rec_new == NULL) { diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 1d2ca44a386a05..ea778c763520fc 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -1383,7 +1383,7 @@ _Unpickler_ResizeMemoList(UnpicklerObject *self, Py_ssize_t new_size) assert(new_size > self->memo_size); PyObject **memo_new = self->memo; - PyMem_Resize(memo_new, PyObject *, new_size); + PyMem_RESIZE(memo_new, PyObject *, new_size); if (memo_new == NULL) { PyErr_NoMemory(); return -1; @@ -6298,9 +6298,9 @@ load_mark(UnpicklerObject *self) } Py_ssize_t *marks_old = self->marks; - PyMem_Resize(self->marks, Py_ssize_t, alloc); + PyMem_RESIZE(self->marks, Py_ssize_t, alloc); if (self->marks == NULL) { - PyMem_Free(marks_old); + PyMem_FREE(marks_old); self->marks_size = 0; PyErr_NoMemory(); return -1;