@@ -1241,6 +1241,10 @@ PyObject_Free(void *p)
12411241#define DEADBYTE 0xDB /* dead (newly freed) memory */
12421242#define FORBIDDENBYTE 0xFB /* untouchable bytes at each end of a block */
12431243
1244+ /* We tag each block with an API ID in order to tag API violations */
1245+ #define _PYMALLOC_MEM_ID 'm' /* the PyMem_Malloc() API */
1246+ #define _PYMALLOC_OBJ_ID 'o' /* The PyObject_Malloc() API */
1247+
12441248static size_t serialno = 0 ; /* incremented on each debug {m,re}alloc */
12451249
12461250/* serialno is always incremented via calling this routine. The point is
@@ -1331,8 +1335,49 @@ p[2*S+n+S: 2*S+n+2*S]
13311335 instant at which this block was passed out.
13321336*/
13331337
1338+ /* debug replacements for the PyMem_* memory API */
1339+ void *
1340+ _PyMem_DebugMalloc (size_t nbytes )
1341+ {
1342+ return _PyObject_DebugMallocApi (_PYMALLOC_MEM_ID , nbytes );
1343+ }
1344+ void *
1345+ _PyMem_DebugRealloc (void * p , size_t nbytes )
1346+ {
1347+ return _PyObject_DebugReallocApi (_PYMALLOC_MEM_ID , p , nbytes );
1348+ }
1349+ void
1350+ _PyMem_DebugFree (void * p )
1351+ {
1352+ _PyObject_DebugFreeApi (_PYMALLOC_MEM_ID , p );
1353+ }
1354+
1355+ /* debug replacements for the PyObject_* memory API */
13341356void *
13351357_PyObject_DebugMalloc (size_t nbytes )
1358+ {
1359+ return _PyObject_DebugMallocApi (_PYMALLOC_OBJ_ID , nbytes );
1360+ }
1361+ void *
1362+ _PyObject_DebugRealloc (void * p , size_t nbytes )
1363+ {
1364+ return _PyObject_DebugReallocApi (_PYMALLOC_OBJ_ID , p , nbytes );
1365+ }
1366+ void
1367+ _PyObject_DebugFree (void * p )
1368+ {
1369+ _PyObject_DebugFreeApi (_PYMALLOC_OBJ_ID , p );
1370+ }
1371+ void
1372+ _PyObject_DebugCheckAddress (void * p )
1373+ {
1374+ _PyObject_DebugCheckAddressApi (_PYMALLOC_OBJ_ID , p );
1375+ }
1376+
1377+
1378+ /* generic debug memory api, with an "id" to identify the API in use */
1379+ void *
1380+ _PyObject_DebugMallocApi (char id , size_t nbytes )
13361381{
13371382 uchar * p ; /* base address of malloc'ed block */
13381383 uchar * tail ; /* p + 2*SST + nbytes == pointer to tail pad bytes */
@@ -1348,12 +1393,15 @@ _PyObject_DebugMalloc(size_t nbytes)
13481393 if (p == NULL )
13491394 return NULL ;
13501395
1396+ /* at p, write size (SST bytes), id (1 byte), pad (SST-1 bytes) */
13511397 write_size_t (p , nbytes );
1352- memset (p + SST , FORBIDDENBYTE , SST );
1398+ p [SST ] = (uchar )id ;
1399+ memset (p + SST + 1 , FORBIDDENBYTE , SST - 1 );
13531400
13541401 if (nbytes > 0 )
13551402 memset (p + 2 * SST , CLEANBYTE , nbytes );
13561403
1404+ /* at tail, write pad (SST bytes) and serialno (SST bytes) */
13571405 tail = p + 2 * SST + nbytes ;
13581406 memset (tail , FORBIDDENBYTE , SST );
13591407 write_size_t (tail + SST , serialno );
@@ -1362,27 +1410,28 @@ _PyObject_DebugMalloc(size_t nbytes)
13621410}
13631411
13641412/* The debug free first checks the 2*SST bytes on each end for sanity (in
1365- particular, that the FORBIDDENBYTEs are still intact).
1413+ particular, that the FORBIDDENBYTEs with the api ID are still intact).
13661414 Then fills the original bytes with DEADBYTE.
13671415 Then calls the underlying free.
13681416*/
13691417void
1370- _PyObject_DebugFree ( void * p )
1418+ _PyObject_DebugFreeApi ( char api , void * p )
13711419{
13721420 uchar * q = (uchar * )p - 2 * SST ; /* address returned from malloc */
13731421 size_t nbytes ;
13741422
13751423 if (p == NULL )
13761424 return ;
1377- _PyObject_DebugCheckAddress ( p );
1425+ _PyObject_DebugCheckAddressApi ( api , p );
13781426 nbytes = read_size_t (q );
1427+ nbytes += 4 * SST ;
13791428 if (nbytes > 0 )
13801429 memset (q , DEADBYTE , nbytes );
13811430 PyObject_Free (q );
13821431}
13831432
13841433void *
1385- _PyObject_DebugRealloc ( void * p , size_t nbytes )
1434+ _PyObject_DebugReallocApi ( char api , void * p , size_t nbytes )
13861435{
13871436 uchar * q = (uchar * )p ;
13881437 uchar * tail ;
@@ -1391,9 +1440,9 @@ _PyObject_DebugRealloc(void *p, size_t nbytes)
13911440 int i ;
13921441
13931442 if (p == NULL )
1394- return _PyObject_DebugMalloc ( nbytes );
1443+ return _PyObject_DebugMallocApi ( api , nbytes );
13951444
1396- _PyObject_DebugCheckAddress ( p );
1445+ _PyObject_DebugCheckAddressApi ( api , p );
13971446 bumpserialno ();
13981447 original_nbytes = read_size_t (q - 2 * SST );
13991448 total = nbytes + 4 * SST ;
@@ -1403,16 +1452,20 @@ _PyObject_DebugRealloc(void *p, size_t nbytes)
14031452
14041453 if (nbytes < original_nbytes ) {
14051454 /* shrinking: mark old extra memory dead */
1406- memset (q + nbytes , DEADBYTE , original_nbytes - nbytes );
1455+ memset (q + nbytes , DEADBYTE , original_nbytes - nbytes + 2 * SST );
14071456 }
14081457
1409- /* Resize and add decorations. */
1458+ /* Resize and add decorations. We may get a new pointer here, in which
1459+ * case we didn't get the chance to mark the old memory with DEADBYTE,
1460+ * but we live with that.
1461+ */
14101462 q = (uchar * )PyObject_Realloc (q - 2 * SST , total );
14111463 if (q == NULL )
14121464 return NULL ;
14131465
14141466 write_size_t (q , nbytes );
1415- for (i = 0 ; i < SST ; ++ i )
1467+ assert (q [SST ] == (uchar )api );
1468+ for (i = 1 ; i < SST ; ++ i )
14161469 assert (q [SST + i ] == FORBIDDENBYTE );
14171470 q += 2 * SST ;
14181471 tail = q + nbytes ;
@@ -1431,26 +1484,38 @@ _PyObject_DebugRealloc(void *p, size_t nbytes)
14311484/* Check the forbidden bytes on both ends of the memory allocated for p.
14321485 * If anything is wrong, print info to stderr via _PyObject_DebugDumpAddress,
14331486 * and call Py_FatalError to kill the program.
1487+ * The API id, is also checked.
14341488 */
14351489 void
1436- _PyObject_DebugCheckAddress ( const void * p )
1490+ _PyObject_DebugCheckAddressApi ( char api , const void * p )
14371491{
14381492 const uchar * q = (const uchar * )p ;
1493+ char msgbuf [64 ];
14391494 char * msg ;
14401495 size_t nbytes ;
14411496 const uchar * tail ;
14421497 int i ;
1498+ char id ;
14431499
14441500 if (p == NULL ) {
14451501 msg = "didn't expect a NULL pointer" ;
14461502 goto error ;
14471503 }
14481504
1505+ /* Check the API id */
1506+ id = (char )q [- SST ];
1507+ if (id != api ) {
1508+ msg = msgbuf ;
1509+ snprintf (msg , sizeof (msgbuf ), "bad ID: Allocated using API '%c', verified using API '%c'" , id , api );
1510+ msgbuf [sizeof (msgbuf )- 1 ] = 0 ;
1511+ goto error ;
1512+ }
1513+
14491514 /* Check the stuff at the start of p first: if there's underwrite
14501515 * corruption, the number-of-bytes field may be nuts, and checking
14511516 * the tail could lead to a segfault then.
14521517 */
1453- for (i = SST ; i >= 1 ; -- i ) {
1518+ for (i = SST - 1 ; i >= 1 ; -- i ) {
14541519 if (* (q - i ) != FORBIDDENBYTE ) {
14551520 msg = "bad leading pad byte" ;
14561521 goto error ;
@@ -1482,19 +1547,24 @@ _PyObject_DebugDumpAddress(const void *p)
14821547 size_t nbytes , serial ;
14831548 int i ;
14841549 int ok ;
1550+ char id ;
14851551
1486- fprintf (stderr , "Debug memory block at address p=%p:\n" , p );
1487- if (p == NULL )
1552+ fprintf (stderr , "Debug memory block at address p=%p:" , p );
1553+ if (p == NULL ) {
1554+ fprintf (stderr , "\n" );
14881555 return ;
1556+ }
1557+ id = (char )q [- SST ];
1558+ fprintf (stderr , " API '%c'\n" , id );
14891559
14901560 nbytes = read_size_t (q - 2 * SST );
14911561 fprintf (stderr , " %" PY_FORMAT_SIZE_T "u bytes originally "
14921562 "requested\n" , nbytes );
14931563
14941564 /* In case this is nuts, check the leading pad bytes first. */
1495- fprintf (stderr , " The %d pad bytes at p-%d are " , SST , SST );
1565+ fprintf (stderr , " The %d pad bytes at p-%d are " , SST - 1 , SST - 1 );
14961566 ok = 1 ;
1497- for (i = 1 ; i <= SST ; ++ i ) {
1567+ for (i = 1 ; i <= SST - 1 ; ++ i ) {
14981568 if (* (q - i ) != FORBIDDENBYTE ) {
14991569 ok = 0 ;
15001570 break ;
@@ -1505,7 +1575,7 @@ _PyObject_DebugDumpAddress(const void *p)
15051575 else {
15061576 fprintf (stderr , "not all FORBIDDENBYTE (0x%02x):\n" ,
15071577 FORBIDDENBYTE );
1508- for (i = SST ; i >= 1 ; -- i ) {
1578+ for (i = SST - 1 ; i >= 1 ; -- i ) {
15091579 const uchar byte = * (q - i );
15101580 fprintf (stderr , " at p-%d: 0x%02x" , i , byte );
15111581 if (byte != FORBIDDENBYTE )
0 commit comments