From 5db3e157b6d9f63acf1b98cad674f860f0323ec9 Mon Sep 17 00:00:00 2001 From: Duane Griffin Date: Mon, 12 Sep 2016 14:13:57 +1200 Subject: [PATCH] Issue #27945: fix PyDict_MergeFromSeq2 use-after-free Increment the reference count on key/value before using the key to lookup the dictionary. This may call into python code (implementing __hash__) which can in turn modify the collection, invalidating the key/value if we don't increment the ref count. --- Lib/test/test_dict.py | 9 +++++++++ Objects/dictobject.c | 9 ++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index 5ab0a57..22cad46 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -1043,6 +1043,15 @@ class DictTest(unittest.TestCase): self.assertRaises(RuntimeError, iter_and_mutate) + def test_init_use_after_free(self): + class X: + def __hash__(self): + pair[:] = [] + return 13 + + pair = [X(), 123] + dict([pair]) + from test import mapping_tests class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol): diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 809ec8b..8039f9e 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2278,11 +2278,18 @@ PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override) /* Update/merge with this (key, value) pair. */ key = PySequence_Fast_GET_ITEM(fast, 0); value = PySequence_Fast_GET_ITEM(fast, 1); + Py_INCREF(key); + Py_INCREF(value); if (override || PyDict_GetItem(d, key) == NULL) { int status = PyDict_SetItem(d, key, value); - if (status < 0) + if (status < 0) { + Py_DECREF(key); + Py_DECREF(value); goto Fail; + } } + Py_DECREF(key); + Py_DECREF(value); Py_DECREF(fast); Py_DECREF(item); } -- 2.10.0