127 lines
3.6 KiB
Python
127 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Guard _PyMem_DebugRawRealloc's C89 locals and complete operation order."""
|
|
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
source = Path(sys.argv[1]).read_text()
|
|
match = re.search(
|
|
r"\n_PyMem_DebugRawRealloc\(void \*ctx, void \*p, size_t nbytes\)\s*\{(.*?)\n\}",
|
|
source, re.S,
|
|
)
|
|
assert match, "_PyMem_DebugRawRealloc definition not found"
|
|
body = re.sub(r"/\*.*?\*/", "", match.group(1), flags=re.S)
|
|
declarations = """
|
|
debug_alloc_api_t *api;
|
|
uint8_t *head;
|
|
uint8_t *data;
|
|
uint8_t *r;
|
|
uint8_t *tail;
|
|
size_t total;
|
|
size_t original_nbytes;
|
|
#define ERASED_SIZE 64
|
|
uint8_t save[2*ERASED_SIZE];
|
|
#ifdef PYMEM_DEBUG_SERIALNO
|
|
size_t block_serialno;
|
|
#endif
|
|
"""
|
|
assert body.split()[:len(declarations.split())] == declarations.split(), (
|
|
"_PyMem_DebugRawRealloc must declare locals without initializers at function entry, "
|
|
"including block_serialno under PYMEM_DEBUG_SERIALNO"
|
|
)
|
|
|
|
# Keep the ctx cast after the NULL return and the serial-number read after
|
|
# the overflow guard and tail calculation. Freeze both preprocessor paths,
|
|
# allocation failure recovery, saved-byte restoration, and growth filling.
|
|
expected = declarations + """
|
|
|
|
if (p == NULL) {
|
|
return _PyMem_DebugRawAlloc(0, ctx, nbytes);
|
|
}
|
|
|
|
api = (debug_alloc_api_t *)ctx;
|
|
_PyMem_DebugCheckAddress(__func__, api->api_id, p);
|
|
|
|
data = (uint8_t *)p;
|
|
head = data - 2*SST;
|
|
original_nbytes = read_size_t(head);
|
|
if (nbytes > (size_t)PY_SSIZE_T_MAX - PYMEM_DEBUG_EXTRA_BYTES) {
|
|
|
|
return NULL;
|
|
}
|
|
total = nbytes + PYMEM_DEBUG_EXTRA_BYTES;
|
|
|
|
tail = data + original_nbytes;
|
|
#ifdef PYMEM_DEBUG_SERIALNO
|
|
block_serialno = read_size_t(tail + SST);
|
|
#endif
|
|
|
|
if (original_nbytes <= sizeof(save)) {
|
|
memcpy(save, data, original_nbytes);
|
|
memset(data - 2 * SST, PYMEM_DEADBYTE,
|
|
original_nbytes + PYMEM_DEBUG_EXTRA_BYTES);
|
|
}
|
|
else {
|
|
memcpy(save, data, ERASED_SIZE);
|
|
memset(head, PYMEM_DEADBYTE, ERASED_SIZE + 2 * SST);
|
|
memcpy(&save[ERASED_SIZE], tail - ERASED_SIZE, ERASED_SIZE);
|
|
memset(tail - ERASED_SIZE, PYMEM_DEADBYTE,
|
|
ERASED_SIZE + PYMEM_DEBUG_EXTRA_BYTES - 2 * SST);
|
|
}
|
|
|
|
|
|
r = (uint8_t *)api->alloc.realloc(api->alloc.ctx, head, total);
|
|
if (r == NULL) {
|
|
|
|
nbytes = original_nbytes;
|
|
}
|
|
else {
|
|
head = r;
|
|
#ifdef PYMEM_DEBUG_SERIALNO
|
|
bumpserialno();
|
|
block_serialno = serialno;
|
|
#endif
|
|
}
|
|
data = head + 2*SST;
|
|
|
|
write_size_t(head, nbytes);
|
|
head[SST] = (uint8_t)api->api_id;
|
|
memset(head + SST + 1, PYMEM_FORBIDDENBYTE, SST-1);
|
|
|
|
tail = data + nbytes;
|
|
memset(tail, PYMEM_FORBIDDENBYTE, SST);
|
|
#ifdef PYMEM_DEBUG_SERIALNO
|
|
write_size_t(tail + SST, block_serialno);
|
|
#endif
|
|
|
|
|
|
if (original_nbytes <= sizeof(save)) {
|
|
memcpy(data, save, Py_MIN(nbytes, original_nbytes));
|
|
}
|
|
else {
|
|
size_t i = original_nbytes - ERASED_SIZE;
|
|
memcpy(data, save, Py_MIN(nbytes, ERASED_SIZE));
|
|
if (nbytes > i) {
|
|
memcpy(data + i, &save[ERASED_SIZE],
|
|
Py_MIN(nbytes - i, ERASED_SIZE));
|
|
}
|
|
}
|
|
|
|
if (r == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (nbytes > original_nbytes) {
|
|
|
|
memset(data + original_nbytes, PYMEM_CLEANBYTE,
|
|
nbytes - original_nbytes);
|
|
}
|
|
|
|
return data;"""
|
|
assert body.split() == expected.split(), (
|
|
"preserve _PyMem_DebugRawRealloc initialization evaluation points, guards, "
|
|
"and complete operation/control-flow order"
|
|
)
|
|
print("obmalloc _PyMem_DebugRawRealloc C89 declarations and original operation order: PASS")
|