Skip to content

[ObjC] objc_getProperty / objc_setProperty should omit _cmd argument from their type #6944

@bdash

Description

@bdash

Version and Platform (required):

  • Binary Ninja Version: 5.1.7586-dev (73322f08)
  • OS: macOS 15.5
  • CPU Architecture: arm64

Bug Description:
The function type applied to objc_getProperty looks like so:

id _objc_getProperty(id self, SEL _cmd, int16_t offset, BOOL atomic)

This is correct per the Objective-C runtime headers, but in practice the _cmd argument is not used by the runtime function and Clang entirely skips initializing that argument in some situations.

If Clang does skip initializing the argument, the resulting decompilation can get weird as Binary Ninja tries to find a value to populate the argument with:

1952497cc    void -[NEIKEv2ChildSA reset](id_1 arg1, SEL_1 arg2)

1952497cc    {
1952497cc        if (!arg1)
1952497dc            return;
1952497dc        
1952497e0        id_1 x19_1 = arg1;
1952497e8        *(int64_t*)((char*)arg1 + 9) = 1;
1953bbcb4        SEL_1 _cmd_1 = _objc_setProperty_atomic(x19_1, 
1953bbcb4            _objc_setProperty_atomic(arg1, arg2, nullptr, 0x38), nullptr, 0xd0);
195249808        *(int64_t*)((char*)x19_1 + 8) = 1;
1953bbcb4        _cmd_1 = _objc_setProperty_atomic(x19_1, 
1953bbcb4            _objc_setProperty_atomic(x19_1, 
1953bbcb4                _objc_setProperty_atomic(x19_1, 
1953bbcb4                    _objc_setProperty_atomic(x19_1, 
1953bbcb4                        _objc_setProperty_atomic(x19_1, 
1953bbcb4                            _objc_setProperty_atomic(x19_1, 
1953bbcb4                                _objc_setProperty_atomic(x19_1, _cmd_1, nullptr, 0x40), 
1953bbcb4                                nullptr, 0x48), 
1953bbcb4                            nullptr, 0x50), 
1953bbcb4                        nullptr, 0x58), 
1953bbcb4                    nullptr, 0x68), 
1953bbcb4                nullptr, 0x70), 
1953bbcb4            nullptr, 0x78);
1953bbbc4        SEL_1 _cmd;
1953bbbc4        arg1 = _objc_getProperty(x19_1, 
1953bbbc4            _objc_setProperty_atomic(x19_1, 
1953bbbc4                _objc_setProperty_atomic(x19_1, 
1953bbbc4                    _objc_setProperty_atomic(x19_1, 
1953bbbc4                        _objc_setProperty_atomic(x19_1, 
1953bbbc4                            _objc_setProperty_atomic(x19_1, 
1953bbbc4                                _objc_setProperty_atomic(x19_1, _cmd_1, nullptr, 0x80), 
1953bbbc4                                nullptr, 0x88), 
1953bbbc4                            nullptr, 0x90), 
1953bbbc4                        nullptr, 0x98), 
1953bbbc4                    nullptr, 0xa0), 
1953bbbc4                nullptr, 0xa8), 
1953bbbc4            0xd8, 1);
1953bbbc4        
1952498ec        if (arg1) {
1953bbbc4            _objc_getProperty(x19_1, _cmd, 0xd8, 1);
1953bbcb4            /* tailcall */
1953bbcb4            return _objc_setProperty_atomic(x19_1, j__dispatch_channel_cancel(), nullptr, 
1953bbcb4                0xd8);
1952498ec        }
1952497cc    }

Steps To Reproduce:

  1. Download the macOS 25A5279m (26.0 beta 1) IPSW and extract its shared cache.
  2. Load NetworkExtension.framework.
  3. Go to -[NEIKEv2ChildSA reset].

Expected Behavior:
If I explicitly set the type of objc_getProperty / objc_setProperty_atomic to remove the SEL _cmd argument and use explicit register names for the remaining arguments:

id _objc_getProperty(id self, int16_t offset @ x2, BOOL atomic @ x3)

then the result starts to look sensible:

1952497cc    void -[NEIKEv2ChildSA reset](id_1 arg1)

1952497cc    {
1952497cc        if (!arg1)
1952497dc            return;
1952497dc        
1952497e0        id_1 x19_1 = arg1;
1952497e8        *(int64_t*)((char*)arg1 + 9) = 1;
1953bbcb4        _objc_setProperty_atomic(arg1, nullptr, 0x38);
1953bbcb4        _objc_setProperty_atomic(x19_1, nullptr, 0xd0);
195249808        *(int64_t*)((char*)x19_1 + 8) = 1;
1953bbcb4        _objc_setProperty_atomic(x19_1, nullptr, 0x40);
1953bbcb4        _objc_setProperty_atomic(x19_1, nullptr, 0x48);
1953bbcb4        _objc_setProperty_atomic(x19_1, nullptr, 0x50);
1953bbcb4        _objc_setProperty_atomic(x19_1, nullptr, 0x58);
1953bbcb4        _objc_setProperty_atomic(x19_1, nullptr, 0x68);
1953bbcb4        _objc_setProperty_atomic(x19_1, nullptr, 0x70);
1953bbcb4        _objc_setProperty_atomic(x19_1, nullptr, 0x78);
1953bbcb4        _objc_setProperty_atomic(x19_1, nullptr, 0x80);
1953bbcb4        _objc_setProperty_atomic(x19_1, nullptr, 0x88);
1953bbcb4        _objc_setProperty_atomic(x19_1, nullptr, 0x90);
1953bbcb4        _objc_setProperty_atomic(x19_1, nullptr, 0x98);
1953bbcb4        _objc_setProperty_atomic(x19_1, nullptr, 0xa0);
1953bbcb4        _objc_setProperty_atomic(x19_1, nullptr, 0xa8);
1953bbcb4        
1952498ec        if (!_objc_getProperty(x19_1, 0xd8, 1))
1953bbbc4            return;
1953bbbc4        
1953bbbc4        _objc_getProperty(x19_1, 0xd8, 1);
195249900        j__dispatch_channel_cancel();
1953bbcb4        /* tailcall */
1953bbcb4        return _objc_setProperty_atomic(x19_1, nullptr, 0xd8);
1952497cc    }

Additional Information:
I'm not entirely clear where the types for _objc_getProperty and friends come from. I had assumed a type library, but I don't see any messages about a type library being loaded.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions