Uh oh!
There was an error while loading. Please reload this page.
gh-132097: allow AC to disable fastcall convention to avoid UBSan failures - #131605
Conversation
picnixz
commented
Mar 23, 2025
vstinner
left a comment
There was a problem hiding this comment.
This PR changes two things: remove cast and add @disable. It changes 119 files so it's hard to review. You should split your PR int two PRs.
I'm not excited by the removal of the cast since it impacts tons of files (all of them?). I'm +0 on it.
picnixz
commented
Mar 24, 2025
It helped me for grepping contents but I'll cancel that one in this PR and make it in a different one (after everything is done). Indeed, let's focus on actually fixing something. |
vstinner
commented
Mar 24, 2025
The PR title seems to be outdated. |
encukou
commented
Mar 24, 2025
It seems to me that rather than disabling fastcall, what's needed here is to force a particular calling convention. That is, if Clinic changes from fastcall to something else as the new best way of doing things, these functions will still need to stay Disclaimer: I haven't looked into Clinic to check how hard this would be to implement; I'm only commenting at the “API level”. |
picnixz
commented
Mar 24, 2025
That's what I first thought about but as I'm really not familiar with clinic and its assumptions, it was faster to disable fastcall explicitly as there is a |
Uh oh!
There was an error while loading. Please reload this page.
vstinner
commented
Apr 2, 2025
It seems like test_clinic pass on Python built with |
picnixz
commented
Apr 4, 2025
When I tested this, there was a runtime UB that I caught. I've used: Note that I've caugh the UB when running the entire test suite, so I don't know if it was |
picnixz
commented
Apr 4, 2025
I can confirm that I still have the UB on main: ./python -m test test_clinic
Using random seed: 1409063504
0:00:00 load avg: 9.45 Run 1 test sequentially in a single process
0:00:00 load avg: 9.45 [1/1] test_clinic
Objects/methodobject.c:551:18: runtime error: call to function posonly_poskw_varpos_no_fastcall through pointer to incorrect function type 'struct _object *(*)(struct _object *, struct _object *, struct _object *)'
/$HOME/lib/python/cpython/./Modules/clinic/_testclinic.c.h:4174: note: posonly_poskw_varpos_no_fastcall defined here
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ObjeMy clang version: The ./configure --with-undefined-behavior-sanitizer --with-pydebug --prefix="$(pwd)/build" CC=clang LD=clang CFLAGS="-fsanitize=undefined -fno-sanitize-recover" LDFLAGS="-fsanitize=undefined -fno-sanitize-recover" |
Note that I still have a UB on ./python -m test test_xml_etree_c
Using random seed: 644741740
0:00:00 load avg: 3.09 Run 1 test sequentially in a single process
0:00:00 load avg: 3.09 [1/1] test_xml_etree_c
Modules/expat/xmlparse.c:6779:5: runtime error: call to functionexpat_default_handler through pointer to incorrect functiontype'void (*)(void *, const char *, int)'
/$HOME/lib/python/cpython/./Modules/_elementtree.c:3212: note: expat_default_handler defined here
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior Modules/expat/xmlparse.c:6779:I thought you fixed this one? (I can comment on the XML PR if you prefer) EDIT: Fixed in #132265. |
vstinner
commented
Apr 4, 2025
Aha, I only tested |
picnixz
commented
Apr 4, 2025
Yes, I can |
A new extension module, `_hmac`, now exposes the HACL* HMAC (formally verified) implementation. The HACL* implementation is used as a fallback implementation when the OpenSSL implementation of HMAC is not available or disabled. For now, only named hash algorithms are recognized and SIMD support provided by HACL* for the BLAKE2 hash functions is not yet used.
0b34b63 to
24475b1Compare
This comment was marked as resolved.
This comment was marked as resolved.
24475b1 to
65c64bcCompare
We need a way to explicitly disable fastcall convention for non-new methods. Let me explain why. Consider the following case:
What clinic generates is two signatures:
At runtime, the function being called will be
varpos_no_fastcall. In particular, we need to make it aMETH_VARARGS | METH_KEYWORD | METH_CLASS. However, and this is where the runtime UB happens, the fact that we are havingMETH_VARARGSinmethodobject.c::cfunction_call()implies that it will be used as follows:The important bits are:
As you can see,
selfis being passed as aPyObject *to the called method. But, here,methisvarpos_no_fastcall, which expects aPyTypeObject *instead, hence the undefined behaviour. Now, the unfortunate part is that we must use all flagsMETH_VARARGS | METH_KEYWORD | METH_CLASShere.One could wonder: why not using something else as a base classmethod instead of
__new__? well.. the answer is that if a classmethod that is not__new__is generated by AC, then that method will use aMETH_FASTCALLconvention, which entirely defeats the purpose of the test.For efficiency purposes (and that's what we want obviously), if we use
AC will generate:
and we will also have some autogenerated
METHODDEFmacro containingMETH_FASTCALL|METH_CLASSflags. We also cannot just usevarpos_no_fastcallwithout theMETH_FASTCALLflag as its signature wouldn't be compatible forcfunction_callwhich expects either of the two forms:So the solution I came up with is a generic
@disabledirective that can disable the fastcall convention explicitly even for non-__new__class methods:AC will generate:
and a
METHODDEFmacro withMETH_VARARGS|METH_CLASS. Note that nowvarpos_no_fastcallhas the expected signature and can be correctly called at runtime bycfunction_call. I've explained all of this in the comments of the tested functions (_testclinic.c). Since it's part of the tests, I think we can safely keep those comments.-fsanitize=undefined -fno-sanitize-recover#132097