Skip to content

Commit

Permalink
gh-111178: fix USAN failures for partialobject (#124733)
Browse files Browse the repository at this point in the history
  • Loading branch information
picnixz authored Oct 14, 2024
1 parent 6a08a75 commit c77121e
Showing 1 changed file with 34 additions and 25 deletions.
59 changes: 34 additions & 25 deletions Modules/_functoolsmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,13 @@ typedef struct {
vectorcallfunc vectorcall;
} partialobject;

// cast a PyObject pointer PTR to a partialobject pointer (no type checks)
#define _PyPartialObject_CAST(PTR) ((partialobject *)(PTR))

static void partial_setvectorcall(partialobject *pto);
static struct PyModuleDef _functools_module;
static PyObject *
partial_call(partialobject *pto, PyObject *args, PyObject *kwargs);
partial_call(PyObject *pto, PyObject *args, PyObject *kwargs);

static inline _functools_state *
get_functools_state_by_type(PyTypeObject *type)
Expand Down Expand Up @@ -307,8 +310,9 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
}

static int
partial_clear(partialobject *pto)
partial_clear(PyObject *self)
{
partialobject *pto = _PyPartialObject_CAST(self);
Py_CLEAR(pto->fn);
Py_CLEAR(pto->args);
Py_CLEAR(pto->kw);
Expand All @@ -317,8 +321,9 @@ partial_clear(partialobject *pto)
}

static int
partial_traverse(partialobject *pto, visitproc visit, void *arg)
partial_traverse(PyObject *self, visitproc visit, void *arg)
{
partialobject *pto = _PyPartialObject_CAST(self);
Py_VISIT(Py_TYPE(pto));
Py_VISIT(pto->fn);
Py_VISIT(pto->args);
Expand All @@ -328,16 +333,16 @@ partial_traverse(partialobject *pto, visitproc visit, void *arg)
}

static void
partial_dealloc(partialobject *pto)
partial_dealloc(PyObject *self)
{
PyTypeObject *tp = Py_TYPE(pto);
PyTypeObject *tp = Py_TYPE(self);
/* bpo-31095: UnTrack is needed before calling any callbacks */
PyObject_GC_UnTrack(pto);
if (pto->weakreflist != NULL) {
PyObject_ClearWeakRefs((PyObject *) pto);
PyObject_GC_UnTrack(self);
if (_PyPartialObject_CAST(self)->weakreflist != NULL) {
PyObject_ClearWeakRefs(self);
}
(void)partial_clear(pto);
tp->tp_free(pto);
(void)partial_clear(self);
tp->tp_free(self);
Py_DECREF(tp);
}

Expand All @@ -360,14 +365,14 @@ partial_vectorcall_fallback(PyThreadState *tstate, partialobject *pto,
{
pto->vectorcall = NULL;
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
return _PyObject_MakeTpCall(tstate, (PyObject *)pto,
args, nargs, kwnames);
return _PyObject_MakeTpCall(tstate, (PyObject *)pto, args, nargs, kwnames);
}

static PyObject *
partial_vectorcall(partialobject *pto, PyObject *const *args,
partial_vectorcall(PyObject *self, PyObject *const *args,
size_t nargsf, PyObject *kwnames)
{
partialobject *pto = _PyPartialObject_CAST(self);;
PyThreadState *tstate = _PyThreadState_GET();
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);

Expand Down Expand Up @@ -468,15 +473,16 @@ partial_setvectorcall(partialobject *pto)
* but that is unlikely (why use partial without arguments?),
* so we don't optimize that */
else {
pto->vectorcall = (vectorcallfunc)partial_vectorcall;
pto->vectorcall = partial_vectorcall;
}
}


// Not converted to argument clinic, because of `*args, **kwargs` arguments.
static PyObject *
partial_call(partialobject *pto, PyObject *args, PyObject *kwargs)
partial_call(PyObject *self, PyObject *args, PyObject *kwargs)
{
partialobject *pto = _PyPartialObject_CAST(self);
assert(PyCallable_Check(pto->fn));
assert(PyTuple_Check(pto->args));
assert(PyDict_Check(pto->kw));
Expand Down Expand Up @@ -587,8 +593,9 @@ static PyGetSetDef partial_getsetlist[] = {
};

static PyObject *
partial_repr(partialobject *pto)
partial_repr(PyObject *self)
{
partialobject *pto = _PyPartialObject_CAST(self);
PyObject *result = NULL;
PyObject *arglist;
PyObject *mod;
Expand All @@ -597,7 +604,7 @@ partial_repr(partialobject *pto)
PyObject *key, *value;
int status;

status = Py_ReprEnter((PyObject *)pto);
status = Py_ReprEnter(self);
if (status != 0) {
if (status < 0)
return NULL;
Expand All @@ -608,7 +615,7 @@ partial_repr(partialobject *pto)
if (arglist == NULL)
goto done;
/* Pack positional arguments */
assert (PyTuple_Check(pto->args));
assert(PyTuple_Check(pto->args));
n = PyTuple_GET_SIZE(pto->args);
for (i = 0; i < n; i++) {
Py_SETREF(arglist, PyUnicode_FromFormat("%U, %R", arglist,
Expand Down Expand Up @@ -643,11 +650,11 @@ partial_repr(partialobject *pto)
Py_DECREF(arglist);

done:
Py_ReprLeave((PyObject *)pto);
Py_ReprLeave(self);
return result;
error:
Py_DECREF(arglist);
Py_ReprLeave((PyObject *)pto);
Py_ReprLeave(self);
return NULL;
}

Expand All @@ -659,16 +666,18 @@ partial_repr(partialobject *pto)
*/

static PyObject *
partial_reduce(partialobject *pto, PyObject *unused)
partial_reduce(PyObject *self, PyObject *Py_UNUSED(args))
{
partialobject *pto = _PyPartialObject_CAST(self);
return Py_BuildValue("O(O)(OOOO)", Py_TYPE(pto), pto->fn, pto->fn,
pto->args, pto->kw,
pto->dict ? pto->dict : Py_None);
}

static PyObject *
partial_setstate(partialobject *pto, PyObject *state)
partial_setstate(PyObject *self, PyObject *state)
{
partialobject *pto = _PyPartialObject_CAST(self);
PyObject *fn, *fnargs, *kw, *dict;

if (!PyTuple_Check(state)) {
Expand Down Expand Up @@ -730,8 +739,8 @@ partial_setstate(partialobject *pto, PyObject *state)
}

static PyMethodDef partial_methods[] = {
{"__reduce__", (PyCFunction)partial_reduce, METH_NOARGS},
{"__setstate__", (PyCFunction)partial_setstate, METH_O},
{"__reduce__", partial_reduce, METH_NOARGS},
{"__setstate__", partial_setstate, METH_O},
{"__class_getitem__", Py_GenericAlias,
METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
{NULL, NULL} /* sentinel */
Expand All @@ -749,7 +758,7 @@ static PyType_Slot partial_type_slots[] = {
{Py_tp_methods, partial_methods},
{Py_tp_members, partial_memberlist},
{Py_tp_getset, partial_getsetlist},
{Py_tp_descr_get, (descrgetfunc)partial_descr_get},
{Py_tp_descr_get, partial_descr_get},
{Py_tp_new, partial_new},
{Py_tp_free, PyObject_GC_Del},
{0, 0}
Expand Down

0 comments on commit c77121e

Please sign in to comment.