| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent e8c06d6 commit 8bcbcf8
12 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,94 @@ | |||
| 1 | + #ifndef Py_INTERNAL_GLOBAL_OBJECTS_FINI_H | ||
| 2 | + #define Py_INTERNAL_GLOBAL_OBJECTS_FINI_H | ||
| 3 | + #ifdef __cplusplus | ||
| 4 | + extern "C" { | ||
| 5 | + #endif | ||
| 6 | + | ||
| 7 | + #ifndef Py_BUILD_CORE | ||
| 8 | + # error "this header requires Py_BUILD_CORE define" | ||
| 9 | + #endif | ||
| 10 | + | ||
| 11 | + #ifdef Py_DEBUG | ||
| 12 | + | ||
| 13 | + #include "pycore_bytesobject.h" // _PyBytes_CheckOverflow() | ||
| 14 | + #include "pycore_long.h" // TAG_FROM_SIGN_AND_SIZE() | ||
| 15 | + | ||
| 16 | + static inline void | ||
| 17 | + _PyStaticObject_CheckSingleton(PyObject *obj, PyTypeObject *type) | ||
| 18 | + { | ||
| 19 | + // Check PyObject.ob_refcnt | ||
| 20 | + _PyObject_ASSERT(obj, _Py_IsImmortal(obj)); | ||
| 21 | + | ||
| 22 | + // Check PyObject.ob_type | ||
| 23 | + _PyObject_ASSERT(obj, Py_TYPE(obj) == type); | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + static void | ||
| 28 | + _PyStaticObject_CheckLongSingleton(PyObject *obj, long value, int is_bool) | ||
| 29 | + { | ||
| 30 | + PyTypeObject *type = is_bool ? &PyBool_Type : &PyLong_Type; | ||
| 31 | + _PyStaticObject_CheckSingleton(obj, type); | ||
| 32 | + | ||
| 33 | + // Check _PyLong_CompactValue() | ||
| 34 | + Py_ssize_t compact = _PyLong_CompactValue((const PyLongObject *)obj); | ||
| 35 | + _PyObject_ASSERT(obj, compact == value); | ||
| 36 | + | ||
| 37 | + // Check tv_tag and ob_digit[0] | ||
| 38 | + _PyLongValue *long_value = &((PyLongObject*)obj)->long_value; | ||
| 39 | + int sign = (value == 0) ? 0 : ((value < 0) ? -1 : 1); | ||
| 40 | + uintptr_t lv_tag = TAG_FROM_SIGN_AND_SIZE(sign, (value == 0) ? 0 : 1); | ||
| 41 | + if (!is_bool) { | ||
| 42 | + lv_tag |= IMMORTALITY_BIT_MASK; | ||
| 43 | + } | ||
| 44 | + _PyObject_ASSERT(obj, long_value->lv_tag == lv_tag); | ||
| 45 | + _PyObject_ASSERT(obj, long_value->ob_digit[0] == Py_ABS(value)); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + | ||
| 49 | + static inline void | ||
| 50 | + _PyStaticObject_CheckBytesSingleton(PyObject *obj, | ||
| 51 | + Py_ssize_t size, unsigned char ch) | ||
| 52 | + { | ||
| 53 | + _PyStaticObject_CheckSingleton(obj, &PyBytes_Type); | ||
| 54 | + _PyObject_ASSERT(obj, PyBytes_GET_SIZE(obj) == size); | ||
| 55 | + const unsigned char *str = (const unsigned char *)PyBytes_AS_STRING(obj); | ||
| 56 | + _PyObject_ASSERT(obj, str[0] == ch); | ||
| 57 | + _PyBytes_CheckOverflow(obj, obj, "bytes singleton"); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + static void | ||
| 61 | + _PyStaticObject_CheckUnicode(PyObject *obj, const char *str, Py_ssize_t length) | ||
| 62 | + { | ||
| 63 | + _PyStaticObject_CheckSingleton(obj, &PyUnicode_Type); | ||
| 64 | + _PyObject_ASSERT(obj, _PyUnicode_CheckConsistency(obj, 1)); | ||
| 65 | + _PyObject_ASSERT(obj, PyUnicode_GET_LENGTH(obj) == length); | ||
| 66 | + _PyObject_ASSERT(obj, PyUnicode_KIND(obj) == PyUnicode_1BYTE_KIND); | ||
| 67 | + const Py_UCS1 *data = PyUnicode_1BYTE_DATA(obj); | ||
| 68 | + _PyObject_ASSERT(obj, memcmp(data, str, length) == 0); | ||
| 69 | + _PyObject_ASSERT(obj, data[length] == 0); | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + | ||
| 73 | + static void | ||
| 74 | + _PyStaticObject_CheckUnicodeCharSingleton(PyObject *obj, unsigned char ch) | ||
| 75 | + { | ||
| 76 | + _PyStaticObject_CheckUnicode(obj, (char *)&ch, 1); | ||
| 77 | + _PyObject_ASSERT(obj, PyUnicode_IS_ASCII(obj) == (ch <= 127)); | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + | ||
| 81 | + static void | ||
| 82 | + _PyStaticObject_CheckUnicodeSingleton(PyObject *obj, | ||
| 83 | + const char *str, Py_ssize_t length) | ||
| 84 | + { | ||
| 85 | + _PyStaticObject_CheckUnicode(obj, str, length); | ||
| 86 | + _PyObject_ASSERT(obj, PyUnicode_IS_ASCII(obj)); | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + #endif // Py_DEBUG | ||
| 90 | + | ||
| 91 | + #ifdef __cplusplus | ||
| 92 | + } | ||
| 93 | + #endif | ||
| 94 | + #endif /* !Py_INTERNAL_GLOBAL_OBJECTS_FINI_H */ | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3094,5 +3094,51 @@ def test_ceval_decref(self): | |||
| 3094 | 3094 | self.assertEqual(lines.count("DESTROY list"), 2) | |
| 3095 | 3095 | ||
| 3096 | 3096 | ||
| 3097 | + @unittest.skipUnless(support.Py_DEBUG, 'need Py_DEBUG') | ||
| 3098 | + class TestCheckSingleton(unittest.TestCase): | ||
| 3099 | + # Test that _PyStaticObjects_CheckAll() detects memory corruptions in | ||
| 3100 | + # singleton objects at Python exit. | ||
| 3101 | + | ||
| 3102 | + def check(self, code): | ||
| 3103 | + code = f"""if 1: | ||
| 3104 | + import _testcapi | ||
| 3105 | + from test import support | ||
| 3106 | + support.SuppressCrashReport().__enter__() | ||
| 3107 | + {code} | ||
| 3108 | + """ | ||
| 3109 | + proc = assert_python_failure("-c", code) | ||
| 3110 | + return proc.err | ||
| 3111 | + | ||
| 3112 | + def test_corrupt_bytes(self): | ||
| 3113 | + stderr = self.check("_testcapi.corrupt_bytes(b'a', b'#')") | ||
| 3114 | + | ||
| 3115 | + self.assertIn((b'_PyStaticObject_CheckBytesSingleton: ' | ||
| 3116 | + b'Assertion "str[0] == ch" failed'), stderr) | ||
| 3117 | + self.assertIn(b"object repr : b'#'", stderr) | ||
| 3118 | + | ||
| 3119 | + def test_corrupt_unicode(self): | ||
| 3120 | + stderr = self.check("_testcapi.corrupt_unicode('a', '#')") | ||
| 3121 | + | ||
| 3122 | + self.assertIn((b'_PyStaticObject_CheckUnicode: ' | ||
| 3123 | + b'Assertion "memcmp(data, str, length) == 0" failed'), stderr) | ||
| 3124 | + self.assertIn(b"object repr : '#'", stderr) | ||
| 3125 | + | ||
| 3126 | + def test_corrupt_bool(self): | ||
| 3127 | + stderr = self.check("_testcapi.corrupt_long(True, 0)") | ||
| 3128 | + | ||
| 3129 | + self.assertIn((b'_PyStaticObject_CheckLongSingleton: ' | ||
| 3130 | + b'Assertion "compact == value" failed'), | ||
| 3131 | + stderr) | ||
| 3132 | + self.assertIn(b"object repr : True", stderr) | ||
| 3133 | + | ||
| 3134 | + def test_corrupt_long(self): | ||
| 3135 | + stderr = self.check("_testcapi.corrupt_long(5, 42)") | ||
| 3136 | + | ||
| 3137 | + self.assertIn((b'_PyStaticObject_CheckLongSingleton: ' | ||
| 3138 | + b'Assertion "compact == value" failed'), | ||
| 3139 | + stderr) | ||
| 3140 | + self.assertIn(b"object repr : 42", stderr) | ||
| 3141 | + | ||
| 3142 | + | ||
| 3097 | 3143 | if __name__ == "__main__": | |
| 3098 | 3144 | unittest.main() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1345,6 +1345,7 @@ PYTHON_HEADERS= \ | |||
| 1345 | 1345 | $(srcdir)/Include/internal/pycore_getopt.h \ | |
| 1346 | 1346 | $(srcdir)/Include/internal/pycore_gil.h \ | |
| 1347 | 1347 | $(srcdir)/Include/internal/pycore_global_objects.h \ | |
| 1348 | + $(srcdir)/Include/internal/pycore_global_objects_fini.h \ | ||
| 1348 | 1349 | $(srcdir)/Include/internal/pycore_global_objects_fini_generated.h \ | |
| 1349 | 1350 | $(srcdir)/Include/internal/pycore_global_strings.h \ | |
| 1350 | 1351 | $(srcdir)/Include/internal/pycore_hamt.h \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + In debug mode at Python exit, check if immutable singleton objects have been | ||
| 2 | + modified by mistake to detect bugs in C extensions. Patch by Victor Stinner. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -567,6 +567,21 @@ bytearray_overflow(PyObject *Py_UNUSED(module), PyObject *args) | |||
| 567 | 567 | } | |
| 568 | 568 | ||
| 569 | 569 | ||
| 570 | + // Write into an immutable bytes object to test _PyStaticObjects_CheckAll() | ||
| 571 | + static PyObject * | ||
| 572 | + corrupt_bytes(PyObject *Py_UNUSED(module), PyObject *args) | ||
| 573 | + { | ||
| 574 | + char *bytes, *override; | ||
| 575 | + Py_ssize_t size; | ||
| 576 | + if (!PyArg_ParseTuple(args, "yy#", &bytes, &override, &size)) { | ||
| 577 | + return NULL; | ||
| 578 | + } | ||
| 579 | + | ||
| 580 | + memcpy(bytes, override, size); | ||
| 581 | + Py_RETURN_NONE; | ||
| 582 | + } | ||
| 583 | + | ||
| 584 | + | ||
| 570 | 585 | static PyMethodDef test_methods[] = { | |
| 571 | 586 | {"bytes_resize", bytes_resize, METH_VARARGS}, | |
| 572 | 587 | {"bytes_join", bytes_join, METH_VARARGS}, | |
@@ -576,6 +591,7 @@ static PyMethodDef test_methods[] = { | |||
| 576 | 591 | {"test_byteswriter_ptr", test_byteswriter_ptr, METH_NOARGS}, | |
| 577 | 592 | {"bytes_overflow", bytes_overflow, METH_VARARGS}, | |
| 578 | 593 | {"bytearray_overflow", bytearray_overflow, METH_VARARGS}, | |
| 594 | + {"corrupt_bytes", corrupt_bytes, METH_VARARGS}, | ||
| 579 | 595 | {NULL}, | |
| 580 | 596 | }; | |
| 581 | 597 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -281,6 +281,21 @@ get_pylong_layout(PyObject *module, PyObject *Py_UNUSED(args)) | |||
| 281 | 281 | } | |
| 282 | 282 | ||
| 283 | 283 | ||
| 284 | + // Write into an immutable int object to test _PyStaticObjects_CheckAll() | ||
| 285 | + static PyObject * | ||
| 286 | + corrupt_long(PyObject *Py_UNUSED(module), PyObject *args) | ||
| 287 | + { | ||
| 288 | + PyObject *obj; | ||
| 289 | + int value; | ||
| 290 | + if (!PyArg_ParseTuple(args, "Oi", &obj, &value)) { | ||
| 291 | + return NULL; | ||
| 292 | + } | ||
| 293 | + | ||
| 294 | + ((PyLongObject*)obj)->long_value.ob_digit[0] = value; | ||
| 295 | + Py_RETURN_NONE; | ||
| 296 | + } | ||
| 297 | + | ||
| 298 | + | ||
| 284 | 299 | static PyMethodDef test_methods[] = { | |
| 285 | 300 | _TESTCAPI_CALL_LONG_COMPACT_API_METHODDEF | |
| 286 | 301 | {"pylong_fromunicodeobject", pylong_fromunicodeobject, METH_VARARGS}, | |
@@ -295,6 +310,7 @@ static PyMethodDef test_methods[] = { | |||
| 295 | 310 | {"pylong_ispositive", pylong_ispositive, METH_O}, | |
| 296 | 311 | {"pylong_isnegative", pylong_isnegative, METH_O}, | |
| 297 | 312 | {"pylong_iszero", pylong_iszero, METH_O}, | |
| 313 | + {"corrupt_long", corrupt_long, METH_VARARGS}, | ||
| 298 | 314 | {NULL}, | |
| 299 | 315 | }; | |
| 300 | 316 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -276,6 +276,25 @@ _Py_COMP_DIAG_POP | |||
| 276 | 276 | } | |
| 277 | 277 | ||
| 278 | 278 | ||
| 279 | + // Write into an immutable str object to test _PyStaticObjects_CheckAll() | ||
| 280 | + static PyObject * | ||
| 281 | + corrupt_unicode(PyObject *Py_UNUSED(module), PyObject *args) | ||
| 282 | + { | ||
| 283 | + PyObject *obj, *override; | ||
| 284 | + if (!PyArg_ParseTuple(args, "OO", &obj, &override)) { | ||
| 285 | + return NULL; | ||
| 286 | + } | ||
| 287 | + assert(PyUnicode_KIND(obj) == PyUnicode_1BYTE_KIND); | ||
| 288 | + assert(PyUnicode_KIND(override) == PyUnicode_1BYTE_KIND); | ||
| 289 | + | ||
| 290 | + Py_UCS1 *dst = PyUnicode_1BYTE_DATA(obj); | ||
| 291 | + Py_UCS1 *src = PyUnicode_1BYTE_DATA(override); | ||
| 292 | + Py_ssize_t size = PyUnicode_GET_LENGTH(override); | ||
| 293 | + memcpy(dst, src, size); | ||
| 294 | + Py_RETURN_NONE; | ||
| 295 | + } | ||
| 296 | + | ||
| 297 | + | ||
| 279 | 298 | // --- PyUnicodeWriter type ------------------------------------------------- | |
| 280 | 299 | ||
| 281 | 300 | typedef struct { | |
@@ -622,6 +641,7 @@ static PyMethodDef TestMethods[] = { | |||
| 622 | 641 | {"unicode_copycharacters", unicode_copycharacters, METH_VARARGS}, | |
| 623 | 642 | {"unicode_GET_CACHED_HASH", unicode_GET_CACHED_HASH, METH_O}, | |
| 624 | 643 | {"test_py_identifier", test_py_identifier, METH_NOARGS}, | |
| 644 | + {"corrupt_unicode", corrupt_unicode, METH_VARARGS}, | ||
| 625 | 645 | {NULL}, | |
| 626 | 646 | }; | |
| 627 | 647 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -261,6 +261,7 @@ | |||
| 261 | 261 | <ClInclude Include="..\Include\internal\pycore_getopt.h" /> | |
| 262 | 262 | <ClInclude Include="..\Include\internal\pycore_gil.h" /> | |
| 263 | 263 | <ClInclude Include="..\Include\internal\pycore_global_objects.h" /> | |
| 264 | + <ClInclude Include="..\Include\internal\pycore_global_objects_fini.h" /> | ||
| 264 | 265 | <ClInclude Include="..\Include\internal\pycore_global_objects_fini_generated.h" /> | |
| 265 | 266 | <ClInclude Include="..\Include\internal\pycore_hamt.h" /> | |
| 266 | 267 | <ClInclude Include="..\Include\internal\pycore_hashtable.h" /> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -702,6 +702,9 @@ | |||
| 702 | 702 | <ClInclude Include="..\Include\internal\pycore_global_objects.h"> | |
| 703 | 703 | <Filter>Include\internal</Filter> | |
| 704 | 704 | </ClInclude> | |
| 705 | + <ClInclude Include="..\Include\internal\pycore_global_objects_fini.h"> | ||
| 706 | + <Filter>Include\internal</Filter> | ||
| 707 | + </ClInclude> | ||
| 705 | 708 | <ClInclude Include="..\Include\internal\pycore_global_objects_fini_generated.h"> | |
| 706 | 709 | <Filter>Include\internal</Filter> | |
| 707 | 710 | </ClInclude> | |
| Back | FazBrowse Home | New Git URL |
0 commit comments