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

Skip to content

Commit da5eb5a

Browse files
committed
don't expand the operand to Py_XINCREF/XDECREF/CLEAR/DECREF multiple times (closes #17206)
A patch from Illia Polosukhin.
1 parent e7b47dd commit da5eb5a

4 files changed

Lines changed: 71 additions & 14 deletions

File tree

Include/object.h

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -680,12 +680,6 @@ is not considered to be a reference to the type object, to save
680680
complications in the deallocation function. (This is actually a
681681
decision that's up to the implementer of each new type so if you want,
682682
you can count such references to the type object.)
683-
684-
*** WARNING*** The Py_DECREF macro must have a side-effect-free argument
685-
since it may evaluate its argument multiple times. (The alternative
686-
would be to mace it a proper function or assign it to a global temporary
687-
variable first, both of which are slower; and in a multi-threaded
688-
environment the global variable trick is not safe.)
689683
*/
690684

691685
/* First define a pile of simple helper macros, one set per special
@@ -764,15 +758,16 @@ PyAPI_FUNC(void) _Py_Dealloc(PyObject *);
764758

765759
#define Py_INCREF(op) ( \
766760
_Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA \
767-
((PyObject*)(op))->ob_refcnt++)
761+
((PyObject *)(op))->ob_refcnt++)
768762

769763
#define Py_DECREF(op) \
770764
do { \
765+
PyObject *_py_decref_tmp = (PyObject *)(op); \
771766
if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA \
772-
--((PyObject*)(op))->ob_refcnt != 0) \
773-
_Py_CHECK_REFCNT(op) \
767+
--(_py_decref_tmp)->ob_refcnt != 0) \
768+
_Py_CHECK_REFCNT(_py_decref_tmp) \
774769
else \
775-
_Py_Dealloc((PyObject *)(op)); \
770+
_Py_Dealloc(_py_decref_tmp); \
776771
} while (0)
777772

778773
/* Safely decref `op` and set `op` to NULL, especially useful in tp_clear
@@ -811,16 +806,27 @@ PyAPI_FUNC(void) _Py_Dealloc(PyObject *);
811806
*/
812807
#define Py_CLEAR(op) \
813808
do { \
814-
if (op) { \
815-
PyObject *_py_tmp = (PyObject *)(op); \
809+
PyObject *_py_tmp = (PyObject *)(op); \
810+
if (_py_tmp != NULL) { \
816811
(op) = NULL; \
817812
Py_DECREF(_py_tmp); \
818813
} \
819814
} while (0)
820815

821816
/* Macros to use in case the object pointer may be NULL: */
822-
#define Py_XINCREF(op) do { if ((op) == NULL) ; else Py_INCREF(op); } while (0)
823-
#define Py_XDECREF(op) do { if ((op) == NULL) ; else Py_DECREF(op); } while (0)
817+
#define Py_XINCREF(op) \
818+
do { \
819+
PyObject *_py_xincref_tmp = (PyObject *)(op); \
820+
if (_py_xincref_tmp != NULL) \
821+
Py_INCREF(_py_xincref_tmp); \
822+
} while (0)
823+
824+
#define Py_XDECREF(op) \
825+
do { \
826+
PyObject *_py_xdecref_tmp = (PyObject *)(op); \
827+
if (_py_xdecref_tmp != NULL) \
828+
Py_DECREF(_py_xdecref_tmp); \
829+
} while (0)
824830

825831
/*
826832
These are provided as conveniences to Python runtime embedders, so that

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,7 @@ Oleg Plakhotnyuk
983983
Remi Pointel
984984
Ariel Poliak
985985
Guilherme Polo
986+
Illia Polosukhin
986987
Michael Pomraning
987988
Iustin Pop
988989
Claudiu Popa

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ What's New in Python 3.4.0 Alpha 1?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #17206: Py_CLEAR(), Py_DECREF(), Py_XINCREF() and Py_XDECREF() now
14+
expands their arguments once instead of multiple times.
15+
Patch written by Illia Polosukhin.
16+
1317
- Issue #17937: Try harder to collect cyclic garbage at shutdown.
1418

1519
- Issue #12370: Prevent class bodies from interfering with the __class__

Modules/_testcapimodule.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2468,6 +2468,48 @@ test_pytime_object_to_timespec(PyObject *self, PyObject *args)
24682468
return Py_BuildValue("Nl", _PyLong_FromTime_t(sec), nsec);
24692469
}
24702470

2471+
static PyObject *
2472+
_test_incref(PyObject *ob)
2473+
{
2474+
Py_INCREF(ob);
2475+
return ob;
2476+
}
2477+
2478+
static PyObject *
2479+
test_xincref_doesnt_leak(PyObject *ob)
2480+
{
2481+
PyObject *obj = PyLong_FromLong(0);
2482+
Py_XINCREF(_test_incref(obj));
2483+
Py_DECREF(obj);
2484+
Py_DECREF(obj);
2485+
Py_DECREF(obj);
2486+
Py_RETURN_NONE;
2487+
}
2488+
2489+
static PyObject *
2490+
test_incref_doesnt_leak(PyObject *ob)
2491+
{
2492+
PyObject *obj = PyLong_FromLong(0);
2493+
Py_INCREF(_test_incref(obj));
2494+
Py_DECREF(obj);
2495+
Py_DECREF(obj);
2496+
Py_DECREF(obj);
2497+
Py_RETURN_NONE;
2498+
}
2499+
2500+
static PyObject *
2501+
test_xdecref_doesnt_leak(PyObject *ob)
2502+
{
2503+
Py_XDECREF(PyLong_FromLong(0));
2504+
Py_RETURN_NONE;
2505+
}
2506+
2507+
static PyObject *
2508+
test_decref_doesnt_leak(PyObject *ob)
2509+
{
2510+
Py_DECREF(PyLong_FromLong(0));
2511+
Py_RETURN_NONE;
2512+
}
24712513

24722514
static PyMethodDef TestMethods[] = {
24732515
{"raise_exception", raise_exception, METH_VARARGS},
@@ -2478,6 +2520,10 @@ static PyMethodDef TestMethods[] = {
24782520
{"test_dict_iteration", (PyCFunction)test_dict_iteration,METH_NOARGS},
24792521
{"test_lazy_hash_inheritance", (PyCFunction)test_lazy_hash_inheritance,METH_NOARGS},
24802522
{"test_long_api", (PyCFunction)test_long_api, METH_NOARGS},
2523+
{"test_xincref_doesnt_leak",(PyCFunction)test_xincref_doesnt_leak, METH_NOARGS},
2524+
{"test_incref_doesnt_leak", (PyCFunction)test_incref_doesnt_leak, METH_NOARGS},
2525+
{"test_xdecref_doesnt_leak",(PyCFunction)test_xdecref_doesnt_leak, METH_NOARGS},
2526+
{"test_decref_doesnt_leak", (PyCFunction)test_decref_doesnt_leak, METH_NOARGS},
24812527
{"test_long_and_overflow", (PyCFunction)test_long_and_overflow,
24822528
METH_NOARGS},
24832529
{"test_long_as_double", (PyCFunction)test_long_as_double,METH_NOARGS},

0 commit comments

Comments
 (0)