But there are multiple ways to have execution leave a function.
The function could return.
The function could be aborted by SIGKILL.
The function could be aborted by a non-SIGKILL signal that preserves subsequent execution invariants.
The function could be aborted by a non-SIGKILL signal that doesn't preserve subsequent execution invariants (SIGSEGV in most--but not all--cases).
The function could abort(2) (not always the same as SIGABRT, but usually).
The function could overflow its stack (not always the same as abort(2)).
The computer could lose power.
...and that's without violating the spirit of the law with weird instruction-level stuff (e.g. is pop/jmp the same as ret? If I move the stack pointer and re-set all the argument registers, did I return?)
The function could return.
The function could be aborted by SIGKILL.
The function could be aborted by a non-SIGKILL signal that preserves subsequent execution invariants.
The function could be aborted by a non-SIGKILL signal that doesn't preserve subsequent execution invariants (SIGSEGV in most--but not all--cases).
The function could abort(2) (not always the same as SIGABRT, but usually).
The function could overflow its stack (not always the same as abort(2)).
The computer could lose power.
...and that's without violating the spirit of the law with weird instruction-level stuff (e.g. is pop/jmp the same as ret? If I move the stack pointer and re-set all the argument registers, did I return?)