Issue #25135: Avoid possible reentrancy issues in deque_clear.

This commit is contained in:
Raymond Hettinger 2015-09-26 00:14:59 -07:00
parent 03c59b9bef
commit bf49fee125
2 changed files with 62 additions and 3 deletions

View file

@ -27,6 +27,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.

View file

@ -1038,16 +1038,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