https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106437
Bug ID: 106437 Summary: Glibc marks functions that resume a returns_twice call as leaf Product: gcc Version: 13.0 Status: UNCONFIRMED Keywords: wrong-code Severity: normal Priority: P3 Component: ipa Assignee: unassigned at gcc dot gnu.org Reporter: amonakov at gcc dot gnu.org CC: amonakov at gcc dot gnu.org, asolokha at gmx dot com, dcb314 at hotmail dot com, hubicka at gcc dot gnu.org, marxin at gcc dot gnu.org, rguenth at gcc dot gnu.org, unassigned at gcc dot gnu.org Target Milestone: --- In tree-cfg.cc:call_can_make_abnormal_goto GCC implements an assumption that any function with the 'leaf' attribute will not transfer control to a returns_twice function. This behavior is from day 1 since attribute-leaf introduction, but the documentation says: > leaf functions are not allowed to call callback function passed to it from > current compilation unit or directly call functions exported by the unit or > longjmp into the unit So the manual was talking about longjmp exclusively, even though probably it meant resumption of returns_twice calls in general. Today Glibc headers are marking function that can resume vfork as leaf, execve being the biggest problem since it resumes vfork without being technically UB; functions such as 'raise' and 'kill' can also resume vfork by terminating the current process (but pedantically it is UB to invoke them in vfork context). (there's also the point that 'raise' can invoke signal handlers synchronously, and I agree with Richard that it makes it non-leaf; it's been discussed as Glibc issue previously, the most recent instance seems to be here: https://sourceware.org/bugzilla/show_bug.cgi?id=26802 ; ISTR there was a discussion on GCC side also, earlier) Presence of attribute-leaf makes GCC omit modeling of control flow transfer via ABNORMAL_DISPATCHER, potentially causing miscompilation. Testcase with execve, notice absence of abnormal edges on GIMPLE: #include <unistd.h> #include <signal.h> int main() { if (!vfork()) for (;;) execve("/bin/false", 0, 0); }