diff options
author | Victor Stinner <vstinner@python.org> | 2019-10-07 16:42:01 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-10-07 16:42:01 (GMT) |
commit | 6876257eaabdb30f27ebcbd7d2557278ce2e5705 (patch) | |
tree | 05597a0310d1e330c0c156c97f0fbc8a6386675e /Objects | |
parent | 321def805abc5b7c92c7e90ca90cb2434fdab855 (diff) | |
download | cpython-6876257eaabdb30f27ebcbd7d2557278ce2e5705.zip cpython-6876257eaabdb30f27ebcbd7d2557278ce2e5705.tar.gz cpython-6876257eaabdb30f27ebcbd7d2557278ce2e5705.tar.bz2 |
bpo-36389: _PyObject_CheckConsistency() available in release mode (GH-16612)
bpo-36389, bpo-38376: The _PyObject_CheckConsistency() function is
now also available in release mode. For example, it can be used to
debug a crash in the visit_decref() function of the GC.
Modify the following functions to also work in release mode:
* _PyDict_CheckConsistency()
* _PyObject_CheckConsistency()
* _PyType_CheckConsistency()
* _PyUnicode_CheckConsistency()
Other changes:
* _PyMem_IsPtrFreed(ptr) now also returns 1 if ptr is NULL
(equals to 0).
* _PyBytesWriter_CheckConsistency() now returns 1 and is only used
with assert().
* Reorder _PyObject_Dump() to write safe fields first, and only
attempt to render repr() at the end.
Diffstat (limited to 'Objects')
-rw-r--r-- | Objects/bytesobject.c | 15 | ||||
-rw-r--r-- | Objects/dictobject.c | 36 | ||||
-rw-r--r-- | Objects/object.c | 71 | ||||
-rw-r--r-- | Objects/typeobject.c | 18 | ||||
-rw-r--r-- | Objects/unicodeobject.c | 84 |
5 files changed, 113 insertions, 111 deletions
diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 7df9344..4223dc9 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -3206,10 +3206,10 @@ _PyBytesWriter_GetSize(_PyBytesWriter *writer, char *str) return str - start; } -Py_LOCAL_INLINE(void) +#ifndef NDEBUG +Py_LOCAL_INLINE(int) _PyBytesWriter_CheckConsistency(_PyBytesWriter *writer, char *str) { -#ifdef Py_DEBUG char *start, *end; if (writer->use_small_buffer) { @@ -3239,15 +3239,16 @@ _PyBytesWriter_CheckConsistency(_PyBytesWriter *writer, char *str) end = start + writer->allocated; assert(str != NULL); assert(start <= str && str <= end); -#endif + return 1; } +#endif void* _PyBytesWriter_Resize(_PyBytesWriter *writer, void *str, Py_ssize_t size) { Py_ssize_t allocated, pos; - _PyBytesWriter_CheckConsistency(writer, str); + assert(_PyBytesWriter_CheckConsistency(writer, str)); assert(writer->allocated < size); allocated = size; @@ -3303,7 +3304,7 @@ _PyBytesWriter_Resize(_PyBytesWriter *writer, void *str, Py_ssize_t size) writer->allocated = allocated; str = _PyBytesWriter_AsString(writer) + pos; - _PyBytesWriter_CheckConsistency(writer, str); + assert(_PyBytesWriter_CheckConsistency(writer, str)); return str; error: @@ -3316,7 +3317,7 @@ _PyBytesWriter_Prepare(_PyBytesWriter *writer, void *str, Py_ssize_t size) { Py_ssize_t new_min_size; - _PyBytesWriter_CheckConsistency(writer, str); + assert(_PyBytesWriter_CheckConsistency(writer, str)); assert(size >= 0); if (size == 0) { @@ -3377,7 +3378,7 @@ _PyBytesWriter_Finish(_PyBytesWriter *writer, void *str) Py_ssize_t size; PyObject *result; - _PyBytesWriter_CheckConsistency(writer, str); + assert(_PyBytesWriter_CheckConsistency(writer, str)); size = _PyBytesWriter_GetSize(writer, str); if (size == 0 && !writer->use_bytearray) { diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 164fe2a..99908a8 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -459,23 +459,26 @@ static PyObject *empty_values[1] = { NULL }; int _PyDict_CheckConsistency(PyObject *op, int check_content) { -#ifndef NDEBUG - _PyObject_ASSERT(op, PyDict_Check(op)); +#define CHECK(expr) \ + do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0) + + assert(op != NULL); + CHECK(PyDict_Check(op)); PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; int splitted = _PyDict_HasSplitTable(mp); Py_ssize_t usable = USABLE_FRACTION(keys->dk_size); - _PyObject_ASSERT(op, 0 <= mp->ma_used && mp->ma_used <= usable); - _PyObject_ASSERT(op, IS_POWER_OF_2(keys->dk_size)); - _PyObject_ASSERT(op, 0 <= keys->dk_usable && keys->dk_usable <= usable); - _PyObject_ASSERT(op, 0 <= keys->dk_nentries && keys->dk_nentries <= usable); - _PyObject_ASSERT(op, keys->dk_usable + keys->dk_nentries <= usable); + CHECK(0 <= mp->ma_used && mp->ma_used <= usable); + CHECK(IS_POWER_OF_2(keys->dk_size)); + CHECK(0 <= keys->dk_usable && keys->dk_usable <= usable); + CHECK(0 <= keys->dk_nentries && keys->dk_nentries <= usable); + CHECK(keys->dk_usable + keys->dk_nentries <= usable); if (!splitted) { /* combined table */ - _PyObject_ASSERT(op, keys->dk_refcnt == 1); + CHECK(keys->dk_refcnt == 1); } if (check_content) { @@ -484,7 +487,7 @@ _PyDict_CheckConsistency(PyObject *op, int check_content) for (i=0; i < keys->dk_size; i++) { Py_ssize_t ix = dictkeys_get_index(keys, i); - _PyObject_ASSERT(op, DKIX_DUMMY <= ix && ix <= usable); + CHECK(DKIX_DUMMY <= ix && ix <= usable); } for (i=0; i < usable; i++) { @@ -494,32 +497,33 @@ _PyDict_CheckConsistency(PyObject *op, int check_content) if (key != NULL) { if (PyUnicode_CheckExact(key)) { Py_hash_t hash = ((PyASCIIObject *)key)->hash; - _PyObject_ASSERT(op, hash != -1); - _PyObject_ASSERT(op, entry->me_hash == hash); + CHECK(hash != -1); + CHECK(entry->me_hash == hash); } else { /* test_dict fails if PyObject_Hash() is called again */ - _PyObject_ASSERT(op, entry->me_hash != -1); + CHECK(entry->me_hash != -1); } if (!splitted) { - _PyObject_ASSERT(op, entry->me_value != NULL); + CHECK(entry->me_value != NULL); } } if (splitted) { - _PyObject_ASSERT(op, entry->me_value == NULL); + CHECK(entry->me_value == NULL); } } if (splitted) { /* splitted table */ for (i=0; i < mp->ma_used; i++) { - _PyObject_ASSERT(op, mp->ma_values[i] != NULL); + CHECK(mp->ma_values[i] != NULL); } } } -#endif return 1; + +#undef CHECK } diff --git a/Objects/object.c b/Objects/object.c index 43ed4c5..e94480f 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -25,13 +25,14 @@ _Py_IDENTIFIER(__isabstractmethod__); int _PyObject_CheckConsistency(PyObject *op, int check_content) { - _PyObject_ASSERT(op, op != NULL); - _PyObject_ASSERT(op, !_PyObject_IsFreed(op)); - _PyObject_ASSERT(op, Py_REFCNT(op) >= 1); +#define CHECK(expr) \ + do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0) - PyTypeObject *type = op->ob_type; - _PyObject_ASSERT(op, type != NULL); - _PyType_CheckConsistency(type); + CHECK(!_PyObject_IsFreed(op)); + CHECK(Py_REFCNT(op) >= 1); + + CHECK(op->ob_type != NULL); + _PyType_CheckConsistency(op->ob_type); if (PyUnicode_Check(op)) { _PyUnicode_CheckConsistency(op, check_content); @@ -40,6 +41,8 @@ _PyObject_CheckConsistency(PyObject *op, int check_content) _PyDict_CheckConsistency(op, check_content); } return 1; + +#undef CHECK } @@ -463,41 +466,41 @@ _PyObject_IsFreed(PyObject *op) void _PyObject_Dump(PyObject* op) { - if (op == NULL) { - fprintf(stderr, "<object at NULL>\n"); - fflush(stderr); - return; - } - if (_PyObject_IsFreed(op)) { /* It seems like the object memory has been freed: don't access it to prevent a segmentation fault. */ fprintf(stderr, "<object at %p is freed>\n", op); + fflush(stderr); return; } - PyGILState_STATE gil; - PyObject *error_type, *error_value, *error_traceback; + /* first, write fields which are the least likely to crash */ + fprintf(stderr, "object address : %p\n", (void *)op); + /* XXX(twouters) cast refcount to long until %zd is + universally available */ + fprintf(stderr, "object refcount : %ld\n", (long)op->ob_refcnt); + fflush(stderr); + + PyTypeObject *type = Py_TYPE(op); + fprintf(stderr, "object type : %p\n", type); + fprintf(stderr, "object type name: %s\n", + type==NULL ? "NULL" : type->tp_name); - fprintf(stderr, "object : "); + /* the most dangerous part */ + fprintf(stderr, "object repr : "); fflush(stderr); - gil = PyGILState_Ensure(); + PyGILState_STATE gil = PyGILState_Ensure(); + PyObject *error_type, *error_value, *error_traceback; PyErr_Fetch(&error_type, &error_value, &error_traceback); + (void)PyObject_Print(op, stderr, 0); fflush(stderr); - PyErr_Restore(error_type, error_value, error_traceback); + PyErr_Restore(error_type, error_value, error_traceback); PyGILState_Release(gil); - /* XXX(twouters) cast refcount to long until %zd is - universally available */ - fprintf(stderr, "\n" - "type : %s\n" - "refcount: %ld\n" - "address : %p\n", - Py_TYPE(op)==NULL ? "NULL" : Py_TYPE(op)->tp_name, - (long)op->ob_refcnt, - (void *)op); + + fprintf(stderr, "\n"); fflush(stderr); } @@ -2146,6 +2149,7 @@ _PyObject_AssertFailed(PyObject *obj, const char *expr, const char *msg, fprintf(stderr, "%s: ", function); } fflush(stderr); + if (expr) { fprintf(stderr, "Assertion \"%s\" failed", expr); } @@ -2153,26 +2157,18 @@ _PyObject_AssertFailed(PyObject *obj, const char *expr, const char *msg, fprintf(stderr, "Assertion failed"); } fflush(stderr); + if (msg) { fprintf(stderr, ": %s", msg); } fprintf(stderr, "\n"); fflush(stderr); - if (obj == NULL) { - fprintf(stderr, "<object at NULL>\n"); - } - else if (_PyObject_IsFreed(obj)) { + if (_PyObject_IsFreed(obj)) { /* It seems like the object memory has been freed: don't access it to prevent a segmentation fault. */ fprintf(stderr, "<object at %p is freed>\n", obj); - } - else if (Py_TYPE(obj) == NULL) { - fprintf(stderr, "<object at %p: ob_type=NULL>\n", obj); - } - else if (_PyObject_IsFreed((PyObject *)Py_TYPE(obj))) { - fprintf(stderr, "<object at %p: type at %p is freed>\n", - obj, (void *)Py_TYPE(obj)); + fflush(stderr); } else { /* Display the traceback where the object has been allocated. @@ -2192,7 +2188,6 @@ _PyObject_AssertFailed(PyObject *obj, const char *expr, const char *msg, try to provide any extra info we can: */ _PyObject_Dump(obj); } - fflush(stderr); Py_FatalError("_PyObject_AssertFailed"); } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 94a4da2..890246e 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -137,22 +137,24 @@ skip_signature(const char *doc) int _PyType_CheckConsistency(PyTypeObject *type) { -#define ASSERT(expr) _PyObject_ASSERT((PyObject *)type, (expr)) +#define CHECK(expr) \ + do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG((PyObject *)type, Py_STRINGIFY(expr)); } } while (0) + + CHECK(!_PyObject_IsFreed((PyObject *)type)); if (!(type->tp_flags & Py_TPFLAGS_READY)) { - /* don't check types before PyType_Ready() */ + /* don't check static types before PyType_Ready() */ return 1; } - ASSERT(!_PyObject_IsFreed((PyObject *)type)); - ASSERT(Py_REFCNT(type) >= 1); - ASSERT(PyType_Check(type)); + CHECK(Py_REFCNT(type) >= 1); + CHECK(PyType_Check(type)); - ASSERT(!(type->tp_flags & Py_TPFLAGS_READYING)); - ASSERT(type->tp_dict != NULL); + CHECK(!(type->tp_flags & Py_TPFLAGS_READYING)); + CHECK(type->tp_dict != NULL); return 1; -#undef ASSERT +#undef CHECK } static const char * diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 0526225..8d442fb 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -487,65 +487,63 @@ PyUnicode_GetMax(void) int _PyUnicode_CheckConsistency(PyObject *op, int check_content) { +#define CHECK(expr) \ + do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0) + PyASCIIObject *ascii; unsigned int kind; - _PyObject_ASSERT(op, PyUnicode_Check(op)); + assert(op != NULL); + CHECK(PyUnicode_Check(op)); ascii = (PyASCIIObject *)op; kind = ascii->state.kind; if (ascii->state.ascii == 1 && ascii->state.compact == 1) { - _PyObject_ASSERT(op, kind == PyUnicode_1BYTE_KIND); - _PyObject_ASSERT(op, ascii->state.ready == 1); + CHECK(kind == PyUnicode_1BYTE_KIND); + CHECK(ascii->state.ready == 1); } else { PyCompactUnicodeObject *compact = (PyCompactUnicodeObject *)op; -#ifndef NDEBUG void *data; -#endif if (ascii->state.compact == 1) { -#ifndef NDEBUG data = compact + 1; -#endif - _PyObject_ASSERT(op, kind == PyUnicode_1BYTE_KIND + CHECK(kind == PyUnicode_1BYTE_KIND || kind == PyUnicode_2BYTE_KIND || kind == PyUnicode_4BYTE_KIND); - _PyObject_ASSERT(op, ascii->state.ascii == 0); - _PyObject_ASSERT(op, ascii->state.ready == 1); - _PyObject_ASSERT(op, compact->utf8 != data); + CHECK(ascii->state.ascii == 0); + CHECK(ascii->state.ready == 1); + CHECK(compact->utf8 != data); } else { -#ifndef NDEBUG PyUnicodeObject *unicode = (PyUnicodeObject *)op; data = unicode->data.any; -#endif if (kind == PyUnicode_WCHAR_KIND) { - _PyObject_ASSERT(op, ascii->length == 0); - _PyObject_ASSERT(op, ascii->hash == -1); - _PyObject_ASSERT(op, ascii->state.compact == 0); - _PyObject_ASSERT(op, ascii->state.ascii == 0); - _PyObject_ASSERT(op, ascii->state.ready == 0); - _PyObject_ASSERT(op, ascii->state.interned == SSTATE_NOT_INTERNED); - _PyObject_ASSERT(op, ascii->wstr != NULL); - _PyObject_ASSERT(op, data == NULL); - _PyObject_ASSERT(op, compact->utf8 == NULL); + CHECK(ascii->length == 0); + CHECK(ascii->hash == -1); + CHECK(ascii->state.compact == 0); + CHECK(ascii->state.ascii == 0); + CHECK(ascii->state.ready == 0); + CHECK(ascii->state.interned == SSTATE_NOT_INTERNED); + CHECK(ascii->wstr != NULL); + CHECK(data == NULL); + CHECK(compact->utf8 == NULL); } else { - _PyObject_ASSERT(op, kind == PyUnicode_1BYTE_KIND + CHECK(kind == PyUnicode_1BYTE_KIND || kind == PyUnicode_2BYTE_KIND || kind == PyUnicode_4BYTE_KIND); - _PyObject_ASSERT(op, ascii->state.compact == 0); - _PyObject_ASSERT(op, ascii->state.ready == 1); - _PyObject_ASSERT(op, data != NULL); + CHECK(ascii->state.compact == 0); + CHECK(ascii->state.ready == 1); + CHECK(data != NULL); if (ascii->state.ascii) { - _PyObject_ASSERT(op, compact->utf8 == data); - _PyObject_ASSERT(op, compact->utf8_length == ascii->length); + CHECK(compact->utf8 == data); + CHECK(compact->utf8_length == ascii->length); } else - _PyObject_ASSERT(op, compact->utf8 != data); + CHECK(compact->utf8 != data); } } if (kind != PyUnicode_WCHAR_KIND) { @@ -557,16 +555,16 @@ _PyUnicode_CheckConsistency(PyObject *op, int check_content) #endif ) { - _PyObject_ASSERT(op, ascii->wstr == data); - _PyObject_ASSERT(op, compact->wstr_length == ascii->length); + CHECK(ascii->wstr == data); + CHECK(compact->wstr_length == ascii->length); } else - _PyObject_ASSERT(op, ascii->wstr != data); + CHECK(ascii->wstr != data); } if (compact->utf8 == NULL) - _PyObject_ASSERT(op, compact->utf8_length == 0); + CHECK(compact->utf8_length == 0); if (ascii->wstr == NULL) - _PyObject_ASSERT(op, compact->wstr_length == 0); + CHECK(compact->wstr_length == 0); } /* check that the best kind is used: O(n) operation */ @@ -585,23 +583,25 @@ _PyUnicode_CheckConsistency(PyObject *op, int check_content) } if (kind == PyUnicode_1BYTE_KIND) { if (ascii->state.ascii == 0) { - _PyObject_ASSERT(op, maxchar >= 128); - _PyObject_ASSERT(op, maxchar <= 255); + CHECK(maxchar >= 128); + CHECK(maxchar <= 255); } else - _PyObject_ASSERT(op, maxchar < 128); + CHECK(maxchar < 128); } else if (kind == PyUnicode_2BYTE_KIND) { - _PyObject_ASSERT(op, maxchar >= 0x100); - _PyObject_ASSERT(op, maxchar <= 0xFFFF); + CHECK(maxchar >= 0x100); + CHECK(maxchar <= 0xFFFF); } else { - _PyObject_ASSERT(op, maxchar >= 0x10000); - _PyObject_ASSERT(op, maxchar <= MAX_UNICODE); + CHECK(maxchar >= 0x10000); + CHECK(maxchar <= MAX_UNICODE); } - _PyObject_ASSERT(op, PyUnicode_READ(kind, data, ascii->length) == 0); + CHECK(PyUnicode_READ(kind, data, ascii->length) == 0); } return 1; + +#undef CHECK } |