Skip to content

Commit 3cbb7de

Browse files
author
tim_one
committed
_PyObject_DebugRealloc(): rewritten to let the underlying realloc do
most of the work. In particular, if the underlying realloc is able to grow the memory block in place, great (this routine used to do a fresh malloc + memcpy every time a block grew). BTW, I'm not so keen here on avoiding possible quadratic-time realloc patterns as I am on making the debug pymalloc more invisible (the more it uses memory "just like" the underlying allocator, the better the chance that a suspected memory corruption bug won't vanish when the debug malloc is turned on). git-svn-id: http://svn.python.org/projects/python/trunk@26349 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent e1d3ea4 commit 3cbb7de

1 file changed

Lines changed: 30 additions & 27 deletions

File tree

Objects/obmalloc.c

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,46 +1005,49 @@ void *
10051005
_PyObject_DebugRealloc(void *p, size_t nbytes)
10061006
{
10071007
uchar *q = (uchar *)p;
1008+
uchar *tail;
1009+
size_t total; /* nbytes + 16 */
10081010
size_t original_nbytes;
1009-
void *fresh; /* new memory block, if needed */
10101011

10111012
if (p == NULL)
10121013
return _PyObject_DebugMalloc(nbytes);
10131014

10141015
_PyObject_DebugCheckAddress(p);
1016+
bumpserialno();
10151017
original_nbytes = read4(q-8);
1016-
if (nbytes == original_nbytes) {
1017-
/* note that this case is likely to be common due to the
1018-
way Python appends to lists */
1019-
bumpserialno();
1020-
write4(q + nbytes + 4, serialno);
1021-
return p;
1018+
total = nbytes + 16;
1019+
if (total < nbytes || (total >> 31) > 1) {
1020+
/* overflow, or we can't represent it in 4 bytes */
1021+
return NULL;
10221022
}
10231023

10241024
if (nbytes < original_nbytes) {
1025-
/* shrinking -- leave the guts alone, except to
1026-
fill the excess with DEADBYTE */
1027-
const size_t excess = original_nbytes - nbytes;
1028-
bumpserialno();
1029-
write4(q-8, nbytes);
1030-
/* kill the excess bytes plus the trailing 8 pad bytes */
1031-
q += nbytes;
1032-
q[0] = q[1] = q[2] = q[3] = FORBIDDENBYTE;
1033-
write4(q+4, serialno);
1034-
memset(q+8, DEADBYTE, excess);
1035-
return p;
1025+
/* shrinking: mark old extra memory dead */
1026+
memset(q + nbytes, DEADBYTE, original_nbytes - nbytes);
10361027
}
10371028

1038-
assert(nbytes != 0);
1039-
/* More memory is needed: get it, copy over the first original_nbytes
1040-
of the original data, and free the original memory. */
1041-
fresh = _PyObject_DebugMalloc(nbytes);
1042-
if (fresh != NULL) {
1043-
if (original_nbytes > 0)
1044-
memcpy(fresh, p, original_nbytes);
1045-
_PyObject_DebugFree(p);
1029+
/* Resize and add decorations. */
1030+
q = (uchar *)PyObject_Realloc(q-8, total);
1031+
if (q == NULL)
1032+
return NULL;
1033+
1034+
write4(q, nbytes);
1035+
assert(q[4] == FORBIDDENBYTE &&
1036+
q[5] == FORBIDDENBYTE &&
1037+
q[6] == FORBIDDENBYTE &&
1038+
q[7] == FORBIDDENBYTE);
1039+
q += 8;
1040+
tail = q + nbytes;
1041+
tail[0] = tail[1] = tail[2] = tail[3] = FORBIDDENBYTE;
1042+
write4(tail + 4, serialno);
1043+
1044+
if (nbytes > original_nbytes) {
1045+
/* growing: mark new extra memory clean */
1046+
memset(q + original_nbytes, CLEANBYTE,
1047+
nbytes - original_nbytes);
10461048
}
1047-
return fresh;
1049+
1050+
return q;
10481051
}
10491052

10501053
/* Check the forbidden bytes on both ends of the memory allocated for p.

0 commit comments

Comments
 (0)