Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit faf7b7f

Browse files
committed
Issue 8420: Fix obscure set crashers.
1 parent 8844441 commit faf7b7f

2 files changed

Lines changed: 54 additions & 10 deletions

File tree

Lib/test/test_set.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,6 +1660,39 @@ def test_inplace_methods(self):
16601660
self.assertRaises(TypeError, getattr(set('january'), methname), N(data))
16611661
self.assertRaises(ZeroDivisionError, getattr(set('january'), methname), E(data))
16621662

1663+
class bad_eq:
1664+
def __eq__(self, other):
1665+
if be_bad:
1666+
set2.clear()
1667+
raise ZeroDivisionError
1668+
return self is other
1669+
def __hash__(self):
1670+
return 0
1671+
1672+
class bad_dict_clear:
1673+
def __eq__(self, other):
1674+
if be_bad:
1675+
dict2.clear()
1676+
return self is other
1677+
def __hash__(self):
1678+
return 0
1679+
1680+
class Test_Weird_Bugs(unittest.TestCase):
1681+
def test_8420_set_merge(self):
1682+
# This used to segfault
1683+
global be_bad, set2, dict2
1684+
be_bad = False
1685+
set1 = {bad_eq()}
1686+
set2 = {bad_eq() for i in range(75)}
1687+
be_bad = True
1688+
self.assertRaises(ZeroDivisionError, set1.update, set2)
1689+
1690+
be_bad = False
1691+
set1 = {bad_dict_clear()}
1692+
dict2 = {bad_dict_clear(): None}
1693+
be_bad = True
1694+
set1.symmetric_difference_update(dict2)
1695+
16631696
# Application tests (based on David Eppstein's graph recipes ====================================
16641697

16651698
def powerset(U):
@@ -1804,6 +1837,7 @@ def test_main(verbose=None):
18041837
TestIdentities,
18051838
TestVariousIteratorArgs,
18061839
TestGraphs,
1840+
Test_Weird_Bugs,
18071841
)
18081842

18091843
support.run_unittest(*test_classes)

Objects/setobject.c

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -364,12 +364,13 @@ static int
364364
set_add_entry(register PySetObject *so, setentry *entry)
365365
{
366366
register Py_ssize_t n_used;
367+
PyObject *key = entry->key;
367368

368369
assert(so->fill <= so->mask); /* at least one empty slot */
369370
n_used = so->used;
370-
Py_INCREF(entry->key);
371-
if (set_insert_key(so, entry->key, (long) entry->hash) == -1) {
372-
Py_DECREF(entry->key);
371+
Py_INCREF(key);
372+
if (set_insert_key(so, key, (long) entry->hash) == -1) {
373+
Py_DECREF(key);
373374
return -1;
374375
}
375376
if (!(so->used > n_used && so->fill*3 >= (so->mask+1)*2))
@@ -637,6 +638,7 @@ static int
637638
set_merge(PySetObject *so, PyObject *otherset)
638639
{
639640
PySetObject *other;
641+
PyObject *key;
640642
register Py_ssize_t i;
641643
register setentry *entry;
642644

@@ -657,11 +659,12 @@ set_merge(PySetObject *so, PyObject *otherset)
657659
}
658660
for (i = 0; i <= other->mask; i++) {
659661
entry = &other->table[i];
660-
if (entry->key != NULL &&
661-
entry->key != dummy) {
662-
Py_INCREF(entry->key);
663-
if (set_insert_key(so, entry->key, (long) entry->hash) == -1) {
664-
Py_DECREF(entry->key);
662+
key = entry->key;
663+
if (key != NULL &&
664+
key != dummy) {
665+
Py_INCREF(key);
666+
if (set_insert_key(so, key, (long) entry->hash) == -1) {
667+
Py_DECREF(key);
665668
return -1;
666669
}
667670
}
@@ -1642,15 +1645,22 @@ set_symmetric_difference_update(PySetObject *so, PyObject *other)
16421645
while (_PyDict_Next(other, &pos, &key, &value, &hash)) {
16431646
setentry an_entry;
16441647

1648+
Py_INCREF(key);
16451649
an_entry.hash = hash;
16461650
an_entry.key = key;
1651+
16471652
rv = set_discard_entry(so, &an_entry);
1648-
if (rv == -1)
1653+
if (rv == -1) {
1654+
Py_DECREF(key);
16491655
return NULL;
1656+
}
16501657
if (rv == DISCARD_NOTFOUND) {
1651-
if (set_add_entry(so, &an_entry) == -1)
1658+
if (set_add_entry(so, &an_entry) == -1) {
1659+
Py_DECREF(key);
16521660
return NULL;
1661+
}
16531662
}
1663+
Py_DECREF(key);
16541664
}
16551665
Py_RETURN_NONE;
16561666
}

0 commit comments

Comments
 (0)