diff options
author | Raymond Hettinger <python@rcn.com> | 2015-09-26 07:15:46 (GMT) |
---|---|---|
committer | Raymond Hettinger <python@rcn.com> | 2015-09-26 07:15:46 (GMT) |
commit | e055b889373893d6f34e5c0eca98b6231cd2a913 (patch) | |
tree | 42be392f3cd5a82edd27d276932ff51099420090 | |
parent | 5b8854eee0d81fdc56e38a1356fd4a0972d231bc (diff) | |
parent | bf49fee125047e34927d7ecea1cc9cac53dc3f6f (diff) | |
download | cpython-e055b889373893d6f34e5c0eca98b6231cd2a913.zip cpython-e055b889373893d6f34e5c0eca98b6231cd2a913.tar.gz cpython-e055b889373893d6f34e5c0eca98b6231cd2a913.tar.bz2 |
merge
-rw-r--r-- | Misc/NEWS | 3 | ||||
-rw-r--r-- | Modules/_collectionsmodule.c | 62 |
2 files changed, 62 insertions, 3 deletions
@@ -147,6 +147,9 @@ Library respects the letter case given by the user. This restores the ability to write encoding names in uppercase like "UTF-8", which worked in Python 2. +- Issue #25135: Make deque_clear() safer by emptying the deque before clearing. + This helps avoid possible reentrancy issues. + - Issue #19143: platform module now reads Windows version from kernel32.dll to avoid compatibility shims. diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 8d753c3..d50bc42 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -1045,16 +1045,72 @@ PyDoc_STRVAR(remove_doc, static void deque_clear(dequeobject *deque) { + block *b; + block *prevblock; + block *leftblock; + Py_ssize_t leftindex; + Py_ssize_t n; PyObject *item; + /* During the process of clearing a deque, decrefs can cause the + deque to mutate. To avoid fatal confusion, we have to make the + deque empty before clearing the blocks and never refer to + anything via deque->ref while clearing. (This is the same + technique used for clearing lists, sets, and dicts.) + + Making the deque empty requires allocating a new empty block. In + the unlikely event that memory is full, we fall back to an + alternate method that doesn't require a new block. Repeating + pops in a while-loop is slower, possibly re-entrant (and a clever + adversary could cause it to never terminate). + */ + + b = newblock(0); + if (b == NULL) { + PyErr_Clear(); + goto alternate_method; + } + + /* Remember the old size, leftblock, and leftindex */ + leftblock = deque->leftblock; + leftindex = deque->leftindex; + n = Py_SIZE(deque); + + /* Set the deque to be empty using the newly allocated block */ + MARK_END(b->leftlink); + MARK_END(b->rightlink); + Py_SIZE(deque) = 0; + deque->leftblock = b; + deque->rightblock = b; + deque->leftindex = CENTER + 1; + deque->rightindex = CENTER; + deque->state++; + + /* Now the old size, leftblock, and leftindex are disconnected from + the empty deque and we can use them to decref the pointers. + */ + while (n--) { + item = leftblock->data[leftindex]; + Py_DECREF(item); + leftindex++; + if (leftindex == BLOCKLEN && n) { + CHECK_NOT_END(leftblock->rightlink); + prevblock = leftblock; + leftblock = leftblock->rightlink; + leftindex = 0; + freeblock(prevblock); + } + } + CHECK_END(leftblock->rightlink); + freeblock(leftblock); + return; + + alternate_method: while (Py_SIZE(deque)) { item = deque_pop(deque, NULL); assert (item != NULL); Py_DECREF(item); } - assert(deque->leftblock == deque->rightblock); - assert(deque->leftindex - 1 == deque->rightindex); - assert(Py_SIZE(deque) == 0); } static int |