Repository navigation
_operator: methodcaller_clear has the wrong signature for the tp_clear slot (returns void, not int) #156762
Description
Bug report
Bug description:
methodcaller_clear in Modules/_operator.c is declared as returning void, but it is
installed in the Py_tp_clear slot, which requires inquiry
(typedef int (*inquiry)(PyObject *);, Include/object.h:285).
/* Modules/_operator.c:1743 */
static void
methodcaller_clear(PyObject *op)
{
methodcallerobject *mc = methodcallerobject_CAST(op);
Py_CLEAR(mc->name);
Py_CLEAR(mc->args);
Py_CLEAR(mc->kwds);
Py_CLEAR(mc->vectorcall_args);
Py_CLEAR(mc->vectorcall_kwnames);
}
/* Modules/_operator.c:1928 */
static PyType_Slot methodcaller_type_slots[] = {
...
{Py_tp_clear, methodcaller_clear},
...
};The mismatch is not diagnosed at compile time because PyType_Slot.pfunc is void *
(Include/object.h:327), so the function pointer converts implicitly and is later cast
back to inquiry.
UBSan with -fsanitize=function reports it whenever a methodcaller instance is cleared
by the cyclic GC:
Python/gc_free_threading.c:1761:24: runtime error: call to function methodcaller_clear through pointer to incorrect function type 'int (*)(struct _object *)'
Modules/_operator.c:1745: note: methodcaller_clear defined here
The trace above is from a free-threaded build. The defect is in the slot registration, not in
either GC, so it is present in both builds and both reach the bad call the same way:
- default build:
Python/gc.c:1105 - free-threaded build:
Python/gc_free_threading.c:1761
Impact
Benign on the mainstream targets I checked. Both GC implementations discard the result
((void) clear(op); at Python/gc.c:1105 and Python/gc_free_threading.c:1761), and
operator.methodcaller does not set Py_TPFLAGS_BASETYPE, so the return baseclear(self);
path in subtype_clear (Objects/typeobject.c:2751) cannot reach it.
It is still an indirect call through an incompatible function type, i.e. undefined behaviour,
and it matters for:
- builds using Clang CFI (
-fsanitize=cfi-icall), where an indirect-call type mismatch aborts; - WebAssembly targets, where indirect call signatures are checked by the runtime;
- UBSan builds in general, where it is persistent noise that hides other findings.
methodcaller_traverse immediately below (Modules/_operator.c:1764) uses the correct
static int form, so this looks like an oversight from the slot-signature cleanup.
I grepped every tp_clear slot registration under Modules/, Objects/ and Python/:
methodcaller_clear is the only one with a void return type.
Reproducer
The wrong slot signature is present in every build; a sanitizer build is only what makes it
visible. Configured with:
CC=clang CXX=clang++ LDFLAGS='-fuse-ld=lld' ./configure --with-address-sanitizer --with-undefined-behavior-sanitizer --with-pydebug --disable-gil
--with-undefined-behavior-sanitizer adds -fsanitize=undefined (configure.ac:3528), and
Clang includes function in that group, which is the check that fires here.
Then:
./python -c "import gc, operator
class C: pass
c = C()
c.m = operator.methodcaller('foo', c)
del c
gc.collect()"
The methodcaller keeps a reference to c through its args tuple, so the cycle
c -> m -> args -> c reaches tp_clear and the diagnostic fires.
Suggested fix
-static void
+static int
methodcaller_clear(PyObject *op)
{
methodcallerobject *mc = methodcallerobject_CAST(op);
Py_CLEAR(mc->name);
Py_CLEAR(mc->args);
Py_CLEAR(mc->kwds);
Py_CLEAR(mc->vectorcall_args);
Py_CLEAR(mc->vectorcall_kwnames);
+ return 0;
}methodcaller_dealloc (Modules/_operator.c:1759) already calls it as
(void)methodcaller_clear(op); and needs no change.
Environment
- CPython
main, commitb93576a2b66 - Linux x86-64
- Clang 22.1.8, lld
- Free-threaded debug build with ASan and UBSan:
CC=clang CXX=clang++ LDFLAGS='-fuse-ld=lld' ./configure --with-address-sanitizer --with-undefined-behavior-sanitizer --with-pydebug --disable-gil
CPython versions tested on:
3.16, CPython main branch, 3.15
Operating systems tested on:
Linux
Activity
pythongh-156762: Fix methodcaller_clear having wrong signature for tp…
vstinner commentedon Sep 11, 2026
This issue reminds me issue gh-111178 which fixed tons of bugs related to
-fsanitize=function.vstinner commentedon Sep 11, 2026
Using the configure command and the command of "Reproducer", I'm able to reproduce the error on Fedora 44 (clang version 22.1.8).
vstinner commentedon Sep 11, 2026
I ran
./python -m test -j0 -r -u all,-guion Python built with./configure --with-pydebug CFLAGS=-fsanitize=function LDFLAGS=-fsanitize=function CC=clang LD=clang --disable-gil: the full Python test suite passed successfully.gh-156762: Fix tp_clear slot signature for operator.methodcaller() (#…
[3.14] gh-156762: Fix tp_clear slot signature for operator.methodcall…
pythongh-156762: Fix tp_clear slot signature for operator.methodcalle…
[3.15] gh-156762: Fix tp_clear slot signature for operator.methodcall…