rustpanicillegal-instruction

Why does a panic while panicking result in an illegal instruction?


Consider the following code that purposely causes a double panic:

use scopeguard::defer; // 1.1.0

fn main() {
    defer!{ panic!() };
    defer!{ panic!() };
}

I know this typically happens when a Drop implementation panics while unwinding from a previous panic, but why does it cause the program to issue an illegal instruction? That sounds like the code is corrupted or jumped somewhere unintended. I figure this might be system or code generation dependent but I tested on various platforms and they all issue similar errors with the same reason:

What's going on? What causes this?


Solution

  • This behavior is intended.

    From a comment by Jonas Schievink in Why does panicking in a Drop impl cause SIGILL?:

    It calls intrinsics::abort(), which LLVM turns into a ub2 instruction, which is illegal, thus SIGILL

    I couldn't find any documentation for how double panics are handled, but a paragraph for std::intrinsics::abort() lines up with this behavior:

    The current implementation of intrinsics::abort is to invoke an invalid instruction, on most platforms. On Unix, the process will probably terminate with a signal like SIGABRT, SIGILL, SIGTRAP, SIGSEGV or SIGBUS. The precise behaviour is not guaranteed and not stable.

    Curiously, this behavior is different from calling std::process::abort(), which always terminates with SIGABRT.

    The illegal instruction of choice on x86 is UD2 (I think a typo in the comment above) a.k.a. an undefined instruction which is paradoxically reserved and documented to not be an instruction. So there is no corruption or invalid jump, just a quick and loud way to tell the OS that something has gone very wrong.