From 5adfac2c1b7fb4f0782d097e7e0e6c5614965634 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 2 Dec 2016 08:42:43 +0200 Subject: [PATCH] =?utf8?q?Issue=20#5322:=20Fixed=20setting=20=5F=5Fnew=5F?= =?utf8?q?=5F=20to=20a=20PyCFunction=20inside=20Python=20code.=20Original?= =?utf8?q?=20patch=20by=20Andreas=20St=C3=BChrk.?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- Lib/test/test_descr.py | 88 ++++++++++++++++++++++++++++++++++++++++++ Misc/NEWS | 3 ++ Objects/typeobject.c | 29 +++++++++++++- 3 files changed, 119 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 418f8d2323..15d291451b 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -7,6 +7,7 @@ import pickle import sys import types import unittest +import warnings import weakref from copy import deepcopy @@ -1661,6 +1662,75 @@ order (MRO) for bases """ self.assertEqual(b.foo, 3) self.assertEqual(b.__class__, D) + def test_bad_new(self): + self.assertRaises(TypeError, object.__new__) + self.assertRaises(TypeError, object.__new__, '') + self.assertRaises(TypeError, list.__new__, object) + self.assertRaises(TypeError, object.__new__, list) + class C(object): + __new__ = list.__new__ + self.assertRaises(TypeError, C) + class C(list): + __new__ = object.__new__ + self.assertRaises(TypeError, C) + + def test_object_new(self): + class A(object): + pass + object.__new__(A) + self.assertRaises(TypeError, object.__new__, A, 5) + object.__init__(A()) + self.assertRaises(TypeError, object.__init__, A(), 5) + + class A(object): + def __init__(self, foo): + self.foo = foo + object.__new__(A) + object.__new__(A, 5) + object.__init__(A(3)) + self.assertRaises(TypeError, object.__init__, A(3), 5) + + class A(object): + def __new__(cls, foo): + return object.__new__(cls) + object.__new__(A) + self.assertRaises(TypeError, object.__new__, A, 5) + object.__init__(A(3)) + object.__init__(A(3), 5) + + class A(object): + def __new__(cls, foo): + return object.__new__(cls) + def __init__(self, foo): + self.foo = foo + object.__new__(A) + self.assertRaises(TypeError, object.__new__, A, 5) + object.__init__(A(3)) + self.assertRaises(TypeError, object.__init__, A(3), 5) + + def test_restored_object_new(self): + class A(object): + def __new__(cls, *args, **kwargs): + raise AssertionError + self.assertRaises(AssertionError, A) + class B(A): + __new__ = object.__new__ + def __init__(self, foo): + self.foo = foo + with warnings.catch_warnings(): + warnings.simplefilter('error', DeprecationWarning) + b = B(3) + self.assertEqual(b.foo, 3) + self.assertEqual(b.__class__, B) + del B.__new__ + self.assertRaises(AssertionError, B) + del A.__new__ + with warnings.catch_warnings(): + warnings.simplefilter('error', DeprecationWarning) + b = B(3) + self.assertEqual(b.foo, 3) + self.assertEqual(b.__class__, B) + def test_altmro(self): # Testing mro() and overriding it... class A(object): @@ -3522,6 +3592,24 @@ order (MRO) for bases """ self.assertIsInstance(d, D) self.assertEqual(d.foo, 1) + class C(object): + @staticmethod + def __new__(*args): + return args + self.assertEqual(C(1, 2), (C, 1, 2)) + class D(C): + pass + self.assertEqual(D(1, 2), (D, 1, 2)) + + class C(object): + @classmethod + def __new__(*args): + return args + self.assertEqual(C(1, 2), (C, C, 1, 2)) + class D(C): + pass + self.assertEqual(D(1, 2), (D, D, 1, 2)) + def test_imul_bug(self): # Testing for __imul__ problems... # SF bug 544647 diff --git a/Misc/NEWS b/Misc/NEWS index 07897d7bb3..a285005007 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Release date: TBA Core and Builtins ----------------- +- Issue #5322: Fixed setting __new__ to a PyCFunction inside Python code. + Original patch by Andreas Stührk. + - Issue #28648: Fixed crash in Py_DecodeLocale() in debug build on Mac OS X when decode astral characters. Patch by Xiang Zhang. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 7b76e5cd4d..eac89eca45 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -6798,7 +6798,34 @@ update_one_slot(PyTypeObject *type, slotdef *p) sanity checks and constructing a new argument list. Cut all that nonsense short -- this speeds up instance creation tremendously. */ - specific = (void *)type->tp_new; + PyObject *self = PyCFunction_GET_SELF(descr); + if (!self || !PyType_Check(self)) { + /* This should never happen because + tp_new_wrapper expects a type for self. + Use slot_tp_new which will call + tp_new_wrapper which will raise an + exception. */ + specific = (void *)slot_tp_new; + } + else { + PyTypeObject *staticbase; + specific = ((PyTypeObject *)self)->tp_new; + /* Check that the user does not do anything + silly and unsafe like object.__new__(dict). + To do this, we check that the most derived + base that's not a heap type is this type. */ + staticbase = type->tp_base; + while (staticbase && + (staticbase->tp_flags & Py_TPFLAGS_HEAPTYPE)) + staticbase = staticbase->tp_base; + if (staticbase && + staticbase->tp_new != specific) + /* Seems to be unsafe, better use + slot_tp_new which will call + tp_new_wrapper which will raise an + exception if it is unsafe. */ + specific = (void *)slot_tp_new; + } /* XXX I'm not 100% sure that there isn't a hole in this reasoning that requires additional sanity checks. I'll buy the first person to -- 2.40.0