Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-45476: Disallow using PyFloat_AS_DOUBLE() as l-value #28976

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions Doc/whatsnew/3.11.rst
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,39 @@ Porting to Python 3.11
been included directly, consider including ``Python.h`` instead.
(Contributed by Victor Stinner in :issue:`35134`.)

* The following "GET" and "AS" functions can no longer be used as l-value (to
modify a Python object):

* :c:func:`PyByteArray_GET_SIZE`
* :c:func:`PyBytes_GET_SIZE`
* :c:func:`PyCFunction_GET_CLASS`
* :c:func:`PyCFunction_GET_FLAGS`
* :c:func:`PyCFunction_GET_FUNCTION`
* :c:func:`PyCFunction_GET_SELF`
* :c:func:`PyDict_GET_SIZE`
* :c:func:`PyFloat_AS_DOUBLE`
* :c:func:`PyFunction_GET_ANNOTATIONS`
* :c:func:`PyFunction_GET_CLOSURE`
* :c:func:`PyFunction_GET_CODE`
* :c:func:`PyFunction_GET_DEFAULTS`
* :c:func:`PyFunction_GET_GLOBALS`
* :c:func:`PyFunction_GET_KW_DEFAULTS`
* :c:func:`PyFunction_GET_MODULE`
* :c:func:`PyHeapType_GET_MEMBERS`
* :c:func:`PyInstanceMethod_GET_FUNCTION`
* :c:func:`PyList_GET_SIZE`
* :c:func:`PyMemoryView_GET_BASE`
* :c:func:`PyMemoryView_GET_BUFFER`
* :c:func:`PyMethod_GET_FUNCTION`
* :c:func:`PyMethod_GET_SELF`
* :c:func:`PySet_GET_SIZE`
* :c:func:`PyTuple_GET_SIZE`
* :c:func:`PyWeakref_GET_OBJECT`

For example, ``PyList_GET_SIZE(list) = 5;`` now fails with a compiler error.
The :c:func:`Py_SET_SIZE` function must be used instead.
(Contributed by Victor Stinner in :issue:`45476`.)

Deprecated
----------

Expand Down
9 changes: 6 additions & 3 deletions Include/cpython/bytearrayobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ typedef struct {
Py_ssize_t ob_exports; /* How many buffer exports */
} PyByteArrayObject;

PyAPI_DATA(char) _PyByteArray_empty_string[];

/* Macros, trading safety for speed */
#define _PyByteArray_CAST(op) \
(assert(PyByteArray_Check(op)), (PyByteArrayObject *)(op))
#define PyByteArray_AS_STRING(self) \
(assert(PyByteArray_Check(self)), \
Py_SIZE(self) ? ((PyByteArrayObject *)(self))->ob_start : _PyByteArray_empty_string)
#define PyByteArray_GET_SIZE(self) (assert(PyByteArray_Check(self)), Py_SIZE(self))

PyAPI_DATA(char) _PyByteArray_empty_string[];
#define PyByteArray_GET_SIZE(self) \
_Py_RVALUE(Py_SIZE(_PyByteArray_CAST(self)))
6 changes: 3 additions & 3 deletions Include/cpython/bytesobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ PyAPI_FUNC(PyObject *) _PyBytes_DecodeEscape(const char *, Py_ssize_t,
const char *, const char **);

/* Macro, trading safety for speed */
#define PyBytes_AS_STRING(op) (assert(PyBytes_Check(op)), \
(((PyBytesObject *)(op))->ob_sval))
#define PyBytes_GET_SIZE(op) (assert(PyBytes_Check(op)),Py_SIZE(op))
#define _PyBytes_CAST(op) (assert(PyBytes_Check(op)), (PyBytesObject *)(op))
#define PyBytes_AS_STRING(op) (_PyBytes_CAST(op)->ob_sval)
#define PyBytes_GET_SIZE(op) _Py_RVALUE(Py_SIZE(_PyBytes_CAST(op)))

/* _PyBytes_Join(sep, x) is like sep.join(x). sep must be PyBytesObject*,
x must be an iterable object. */
Expand Down
6 changes: 3 additions & 3 deletions Include/cpython/classobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ PyAPI_FUNC(PyObject *) PyMethod_Self(PyObject *);
/* Macros for direct access to these values. Type checks are *not*
done, so use with care. */
#define PyMethod_GET_FUNCTION(meth) \
(((PyMethodObject *)meth) -> im_func)
_Py_RVALUE(((PyMethodObject *)meth) -> im_func)
#define PyMethod_GET_SELF(meth) \
(((PyMethodObject *)meth) -> im_self)
_Py_RVALUE(((PyMethodObject *)meth) -> im_self)

typedef struct {
PyObject_HEAD
Expand All @@ -48,7 +48,7 @@ PyAPI_FUNC(PyObject *) PyInstanceMethod_Function(PyObject *);
/* Macros for direct access to these values. Type checks are *not*
done, so use with care. */
#define PyInstanceMethod_GET_FUNCTION(meth) \
(((PyInstanceMethodObject *)meth) -> func)
_Py_RVALUE(((PyInstanceMethodObject *)meth) -> func)

#ifdef __cplusplus
}
Expand Down
3 changes: 2 additions & 1 deletion Include/cpython/dictobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ PyAPI_FUNC(int) _PyDict_Next(
PyObject *mp, Py_ssize_t *pos, PyObject **key, PyObject **value, Py_hash_t *hash);

/* Get the number of items of a dictionary. */
#define PyDict_GET_SIZE(mp) (assert(PyDict_Check(mp)),((PyDictObject *)mp)->ma_used)
#define PyDict_GET_SIZE(mp) \
_Py_RVALUE((assert(PyDict_Check(mp)), ((PyDictObject *)mp)->ma_used))
PyAPI_FUNC(int) _PyDict_Contains_KnownHash(PyObject *, PyObject *, Py_hash_t);
PyAPI_FUNC(int) _PyDict_ContainsId(PyObject *, struct _Py_Identifier *);
PyAPI_FUNC(PyObject *) _PyDict_NewPresized(Py_ssize_t minused);
Expand Down
5 changes: 3 additions & 2 deletions Include/cpython/floatobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ typedef struct {
} PyFloatObject;

// Macro version of PyFloat_AsDouble() trading safety for speed.
// It doesn't check if op is a double object.
#define PyFloat_AS_DOUBLE(op) (((PyFloatObject *)(op))->ob_fval)
// It only checks if op is a double object in debug mode.
#define _PyFloat_CAST(op) (assert(PyFloat_Check(op)), (PyFloatObject *)(op))
#define PyFloat_AS_DOUBLE(op) _Py_RVALUE(_PyFloat_CAST(op)->ob_fval)
15 changes: 8 additions & 7 deletions Include/cpython/funcobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,20 +86,21 @@ uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func);

/* Macros for direct access to these values. Type checks are *not*
done, so use with care. */
#define _PyFunction_CAST(func) ((PyFunctionObject *)func)
#define PyFunction_GET_CODE(func) \
(((PyFunctionObject *)func) -> func_code)
_Py_RVALUE(_PyFunction_CAST(func)->func_code)
#define PyFunction_GET_GLOBALS(func) \
(((PyFunctionObject *)func) -> func_globals)
_Py_RVALUE(_PyFunction_CAST(func)->func_globals)
#define PyFunction_GET_MODULE(func) \
(((PyFunctionObject *)func) -> func_module)
_Py_RVALUE(_PyFunction_CAST(func)->func_module)
#define PyFunction_GET_DEFAULTS(func) \
(((PyFunctionObject *)func) -> func_defaults)
_Py_RVALUE(_PyFunction_CAST(func)->func_defaults)
#define PyFunction_GET_KW_DEFAULTS(func) \
(((PyFunctionObject *)func) -> func_kwdefaults)
_Py_RVALUE(_PyFunction_CAST(func)->func_kwdefaults)
#define PyFunction_GET_CLOSURE(func) \
(((PyFunctionObject *)func) -> func_closure)
_Py_RVALUE(_PyFunction_CAST(func)->func_closure)
#define PyFunction_GET_ANNOTATIONS(func) \
(((PyFunctionObject *)func) -> func_annotations)
_Py_RVALUE(_PyFunction_CAST(func)->func_annotations)

/* The classmethod and staticmethod types lives here, too */
PyAPI_DATA(PyTypeObject) PyClassMethod_Type;
Expand Down
2 changes: 1 addition & 1 deletion Include/cpython/listobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ PyAPI_FUNC(void) _PyList_DebugMallocStats(FILE *out);

#define PyList_GET_ITEM(op, i) (_PyList_CAST(op)->ob_item[i])
#define PyList_SET_ITEM(op, i, v) _Py_RVALUE(_PyList_CAST(op)->ob_item[i] = (v))
#define PyList_GET_SIZE(op) Py_SIZE(_PyList_CAST(op))
#define PyList_GET_SIZE(op) _Py_RVALUE(Py_SIZE(_PyList_CAST(op)))
14 changes: 8 additions & 6 deletions Include/cpython/methodobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ PyAPI_DATA(PyTypeObject) PyCMethod_Type;

/* Macros for direct access to these values. Type checks are *not*
done, so use with care. */
#define _PyCFunction_CAST(func) ((PyCFunctionObject *)func)
#define _PyCFunction_ML(func) (_PyCFunction_CAST(func)->m_ml)
#define PyCFunction_GET_FUNCTION(func) \
(((PyCFunctionObject *)func) -> m_ml -> ml_meth)
_Py_RVALUE(_PyCFunction_ML(func)->ml_meth)
#define PyCFunction_GET_SELF(func) \
(((PyCFunctionObject *)func) -> m_ml -> ml_flags & METH_STATIC ? \
NULL : ((PyCFunctionObject *)func) -> m_self)
_Py_RVALUE(_PyCFunction_ML(func)->ml_flags & METH_STATIC ? \
NULL : _PyCFunction_CAST(func)->m_self)
#define PyCFunction_GET_FLAGS(func) \
(((PyCFunctionObject *)func) -> m_ml -> ml_flags)
_Py_RVALUE(_PyCFunction_ML(func)-> ml_flags)
#define PyCFunction_GET_CLASS(func) \
(((PyCFunctionObject *)func) -> m_ml -> ml_flags & METH_METHOD ? \
((PyCMethodObject *)func) -> mm_class : NULL)
_Py_RVALUE(_PyCFunction_ML(func)->ml_flags & METH_METHOD ? \
((PyCMethodObject *)func)->mm_class : NULL)

typedef struct {
PyObject_HEAD
Expand Down
2 changes: 1 addition & 1 deletion Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ typedef struct _heaptypeobject {

/* access macro to the members which are floating "behind" the object */
#define PyHeapType_GET_MEMBERS(etype) \
((PyMemberDef *)(((char *)etype) + Py_TYPE(etype)->tp_basicsize))
_Py_RVALUE((PyMemberDef *)(((char *)etype) + Py_TYPE(etype)->tp_basicsize))

PyAPI_FUNC(const char *) _PyType_Name(PyTypeObject *);
PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *);
Expand Down
5 changes: 4 additions & 1 deletion Include/cpython/tupleobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ PyAPI_FUNC(void) _PyTuple_MaybeUntrack(PyObject *);
/* Cast argument to PyTupleObject* type. */
#define _PyTuple_CAST(op) (assert(PyTuple_Check(op)), (PyTupleObject *)(op))

#define PyTuple_GET_SIZE(op) Py_SIZE(_PyTuple_CAST(op))
#define PyTuple_GET_SIZE(op) _Py_RVALUE(Py_SIZE(_PyTuple_CAST(op)))

// Don't use _Py_RVALUE() for now since many C extensions abuse
// PyTuple_GET_ITEM() to get the PyTuple_GET_ITEM.ob_item array
// using: "&PyTuple_GET_ITEM(tuple, 0)".
#define PyTuple_GET_ITEM(op, i) (_PyTuple_CAST(op)->ob_item[i])

/* Macro, *only* to be used to fill in brand new tuples */
Expand Down
8 changes: 4 additions & 4 deletions Include/cpython/weakrefobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ PyAPI_FUNC(void) _PyWeakref_ClearRef(PyWeakReference *self);
has dropped to zero. In the meantime, code accessing the weakref will
be able to "see" the target object even though it is supposed to be
unreachable. See issue #16602. */
#define PyWeakref_GET_OBJECT(ref) \
(Py_REFCNT(((PyWeakReference *)(ref))->wr_object) > 0 \
? ((PyWeakReference *)(ref))->wr_object \
: Py_None)
#define PyWeakref_GET_OBJECT(ref) \
_Py_RVALUE(Py_REFCNT(((PyWeakReference *)(ref))->wr_object) > 0 \
? ((PyWeakReference *)(ref))->wr_object \
: Py_None)
6 changes: 4 additions & 2 deletions Include/memoryobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ PyAPI_DATA(PyTypeObject) PyMemoryView_Type;

#ifndef Py_LIMITED_API
/* Get a pointer to the memoryview's private copy of the exporter's buffer. */
#define PyMemoryView_GET_BUFFER(op) (&((PyMemoryViewObject *)(op))->view)
#define PyMemoryView_GET_BUFFER(op) \
_Py_RVALUE(&((PyMemoryViewObject *)(op))->view)
/* Get a pointer to the exporting object (this may be NULL!). */
#define PyMemoryView_GET_BASE(op) (((PyMemoryViewObject *)(op))->view.obj)
#define PyMemoryView_GET_BASE(op) \
_Py_RVALUE(((PyMemoryViewObject *)(op))->view.obj)
#endif

PyAPI_FUNC(PyObject *) PyMemoryView_FromObject(PyObject *base);
Expand Down
3 changes: 2 additions & 1 deletion Include/setobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ typedef struct {
PyObject *weakreflist; /* List of weak references */
} PySetObject;

#define PySet_GET_SIZE(so) (assert(PyAnySet_Check(so)),(((PySetObject *)(so))->used))
#define PySet_GET_SIZE(so) \
_Py_RVALUE((assert(PyAnySet_Check(so)), ((PySetObject *)(so))->used))

PyAPI_DATA(PyObject *) _PySet_Dummy;

Expand Down
33 changes: 33 additions & 0 deletions Misc/NEWS.d/next/C API/2021-11-30-11-12-53.bpo-45476.IFVjjP.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
The following "GET" and "AS" functions can no longer be used as l-value (to
modify a Python object):

* :c:func:`PyByteArray_GET_SIZE`
* :c:func:`PyBytes_GET_SIZE`
* :c:func:`PyCFunction_GET_CLASS`
* :c:func:`PyCFunction_GET_FLAGS`
* :c:func:`PyCFunction_GET_FUNCTION`
* :c:func:`PyCFunction_GET_SELF`
* :c:func:`PyDict_GET_SIZE`
* :c:func:`PyFloat_AS_DOUBLE`
* :c:func:`PyFunction_GET_ANNOTATIONS`
* :c:func:`PyFunction_GET_CLOSURE`
* :c:func:`PyFunction_GET_CODE`
* :c:func:`PyFunction_GET_DEFAULTS`
* :c:func:`PyFunction_GET_GLOBALS`
* :c:func:`PyFunction_GET_KW_DEFAULTS`
* :c:func:`PyFunction_GET_MODULE`
* :c:func:`PyHeapType_GET_MEMBERS`
* :c:func:`PyInstanceMethod_GET_FUNCTION`
* :c:func:`PyList_GET_SIZE`
* :c:func:`PyMemoryView_GET_BASE`
* :c:func:`PyMemoryView_GET_BUFFER`
* :c:func:`PyMethod_GET_FUNCTION`
* :c:func:`PyMethod_GET_SELF`
* :c:func:`PySet_GET_SIZE`
* :c:func:`PyTuple_GET_SIZE`
* :c:func:`PyWeakref_GET_OBJECT`

For example, ``PyList_GET_SIZE(list) = 5;`` now fails with a compiler error.
The :c:func:`Py_SET_SIZE` function must be used instead.

Patch by Victor Stinner.