#!/usr/bin/env python3 """Guard _PyMem_DebugCheckAddress's C89 locals and validation order.""" import re import sys from pathlib import Path source = Path(sys.argv[1]).read_text() match = re.search( r"\n_PyMem_DebugCheckAddress\(const char \*func, char api, const void \*p\)\s*\{(.*?)\n\}", source, re.S, ) assert match, "_PyMem_DebugCheckAddress definition not found" body = re.sub(r"/\*.*?\*/", "", match.group(1), flags=re.S) declarations = """ const uint8_t *q; size_t nbytes; const uint8_t *tail; int i; char id; """ assert body.split()[:len(declarations.split())] == declarations.split(), ( "_PyMem_DebugCheckAddress must declare all locals without initializers at function entry" ) # Keep the pointer cast after the assertion, and validate the leading pad # before reading the size or checking the tail of a possibly corrupt block. expected = declarations + """ assert(p != NULL); q = (const uint8_t *)p; id = (char)q[-SST]; if (id != api) { _PyObject_DebugDumpAddress(p); _Py_FatalErrorFormat(func, "bad ID: Allocated using API '%c', " "verified using API '%c'", id, api); } for (i = SST-1; i >= 1; --i) { if (*(q-i) != PYMEM_FORBIDDENBYTE) { _PyObject_DebugDumpAddress(p); _Py_FatalErrorFunc(func, "bad leading pad byte"); } } nbytes = read_size_t(q - 2*SST); tail = q + nbytes; for (i = 0; i < SST; ++i) { if (tail[i] != PYMEM_FORBIDDENBYTE) { _PyObject_DebugDumpAddress(p); _Py_FatalErrorFunc(func, "bad trailing pad byte"); } } """ assert body.split() == expected.split(), ( "preserve _PyMem_DebugCheckAddress assertion, cast evaluation point, " "and complete validation/diagnostic order" ) print("obmalloc _PyMem_DebugCheckAddress C89 declarations and original validation order: PASS")