Skip to content

When mixing C++, ObjC++, and ObjC, something goes wrong in stack unwinding and a crash (abort) ensues #320

@lcampbel

Description

@lcampbel

I boiled this down from a large codebase into the tiniest example I could manage, but it's still six files (including the GNUmakefile) so I'm attaching a tarball. The code executes normally on macOS:

2024-12-07 11:04:20.489861-0500 xtest[83830:4242596] finally
2024-12-07 11:04:20.490356-0500 xtest[83830:4242596] Got expected exception: yup
2024-12-07 11:04:20.490434-0500 xtest[83830:4242596] passed

but on gnustep/linux (Ubuntu 20.04, gnustep-base 1.27.0, libobjc2 2.0.1) it crashes with an abort in libobjc2/eh_personality.c:

2024-12-07 16:03:45.990 xtest[3003478:3003478] finally
2024-12-07 16:03:45.991 xtest[3003478:3003478] Got expected exception: yup
Aborted (core dumped)

with this stack:

#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x00007facc5d1c859 in __GI_abort () at abort.c:79
#2  0x00007facc607b35a in objc_begin_catch () from /home/lcampbel/dev/AkamaiKit-3.7-alsi11-lib64/common/lib/libobjc.so.4.6
#3  0x00000000004023a0 in -[UnitTest xidTest] (self=0x1335488, _cmd=0x408c00 <objc_selector_list+144>) at xtest.m:42
#4  0x000000000040250a in main (argc=<optimized out>, argv=<optimized out>) at xtest.m:75

Weirdly, any of the following tweaks to the code makes the crash go away:

Reversing the order of the tests, changing this:

        [u fooTest];
        [u xidTest];

to this:

        [u xidTest];
        [u fooTest];

Inlining the C++ ctor and dtor, by omitting utest.cc and changing the declarations in utest.h from this:

  xid();
  ~xid();

to this:

  xid() {}
  ~xid() {}

or eliminating the allocation of an xid object in -[Xid init] by removing this line:

    xid u;

There's only one call to abort() in objc_begin_catch:

        // If we have a foreign exception while we have stacked exceptions, we have                                                                                                                                                                                       
        // a problem.  We can't chain them, so we follow the example of C++ and                                                                                                                                                                                           
        // just abort.                                                                                                                                                                                                                                                    
        if (td->caughtExceptions != 0)
        {
                // FIXME: Actually, we can handle a C++ exception if only ObjC                                                                                                                                                                                            
                // exceptions are in-flight                                                                                                                                                                                                                               
                abort();
        }

so I suspect something is not cleaning out td->caughtExceptions properly, but I'm not familiar enough with libobjc2 internals to say any more than that.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions