Skip to content

segfault in objc_alloc_init() #432

Closed

Description

@rfm

Seen on a Rocky Linux 10.2 VM, with libobjc2 build today and clang version 21.1.8 (RESF 21.1.8-1.el10)
The crash occurs when +allocWithZone: returns nil
Minimal test program:
#import <Foundation/Foundation.h>

@interface MyClass : NSObject
@EnD
@implementation MyClass

  • (instancetype) allocWithZone: (NSZone*)z
    {
    return nil;
    }
    @EnD
    int main()
    {
    id o = [MyClass new];

    [o release];
    return 0;
    }

Now the allocation returns nil and this is passed to the inline function isSmallObject() which segfaults.

Here is a stack trace:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff77d77b4 in classForObject (obj=0x0)
at /home/richard/gnustep-ng/build/libobjc2/class.h:407
407 if (UNLIKELY(isSmallObject(obj)))
(gdb) where
#0 0x00007ffff77d77b4 in classForObject (obj=0x0)
at /home/richard/gnustep-ng/build/libobjc2/class.h:407
#1 objc_alloc_init (cls=)
at /home/richard/gnustep-ng/build/libobjc2/fast_paths.m:68
#2 0x00007ffff793a969 in +[NSObject new] (
self=0x4030d0 <._OBJC_CLASS_MyClass>,
_cmd=0x4031f0 <objc_selector_new_160:8>) at NSObject.m:1306
#3 0x00000000004004c5 in main () at a.m:13

Probably the code should be checking for the case where allocation returns nil

Activity

  1. rfm commented on Oct 15, 2026

    @rfm
    ContributorAuthor

    I was able to reproduce the issue on a couple of other platforms.

  2. rfm commented on Oct 15, 2026

    @rfm
    ContributorAuthor

    There may be a better solution, but a simple check for a nil return from the allocation, immediately before the call to 'classForObject(instance)' fixed it for me:

        // If +alloc was overwritten, it is not guaranteed that it returns
        // an instance of cls.
        if (UNLIKELY(instance == nil))
        {
                return nil;
        }
        cls = classForObject(instance);
    
  3. davidchisnall commented on Oct 15, 2026

    @davidchisnall
    Member

    This is interesting.

    I think the problem is actually here. This check should be folded into the previous ones. We should be opt classes out of the fast paths if they implement ‘+allocWithWithZone:(which we do) but then we opt them back in if they *don’t* override-init` (which is a bug).

    I think I originally had alloc and init as separate flags. We could also do that. If we keep them as the same, we can simplify objc_alloc_init to do the test once and either do the fast path of class_createInstance then nothing, or the slower path of both message sends. If we separate them, we should handle the null return.

    It would be useful if someone could do some performance comparison of the two options to see which is faster on real workloads.

  4. davidchisnall commented on Oct 18, 2026

    @davidchisnall
    Member

    Ah, we do track them separately because the init one is on the class and the alloc one on the metaclass.

    I've reworked the implementation in #435. The new one should be both faster and more correct.

  5. added a commit that references this issue on Oct 18, 2026
    2ca8ae2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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