Search code examples
objective-cxcodecocoallvmllvm-gcc

Why does this code crash when compiled with Apple LLVM, but not in LLVM/GCC?


I'm trying to get this code: http://code.google.com/p/switchcontrol/source/browse/trunk/code/AFSwitchControl.m compiling under Apple LLVM in Xcode 4.5.2. It works when compiled with LLVM/GCC, but crashes in the mouseDown method when switched to Apple LLVM on line 198:

NSRect knobRect = _AFSwitchControlKnobRectForInsetBackground(slotRect, _offset);

Because _offset is not set. It's suppose to be set in the bind method with this line:

[self setOffset:(CGFloat)[self state]];

But it appears that nothing is being set under LLVM for some reason. My binding call looks like:

[control bind:NSValueBinding toObject:self withKeyPath:@"isToggleSwitchOn" options:nil];

Any ideas why the control's state is not returning anything under LLVM? Thanks!


Solution

  • The problem is actually a few lines above, in the call to _AFSwitchControlPartRects.

    - (void)mouseDown:(NSEvent *)event {
        NSRect textRect, backgroundRect;
        _AFSwitchControlPartRects([self bounds], &textRect, &backgroundRect);
    
        NSRect slotRect = _AFSwitchControlInsetBackgroundRect(backgroundRect);
        NSRect knobRect = _AFSwitchControlKnobRectForInsetBackground(slotRect, _offset);
    

    The second argument to _AFSwitchControlPartRects, &textRect is a pointer to a rect.

    However in the implementation of the function, that parameter is supposed to be a pointer to enough space for two rects.

    NS_INLINE void _AFSwitchControlPartRects(NSRect bounds, NSRect *textRects, NSRect *backgroundRect) {
        NSDivideRect(bounds, textRects, backgroundRect, NSWidth(bounds)/5.0, NSMinXEdge);
    
        textRects[1] = _AFSwitchControlInsetTextRect(NSOffsetRect(textRects[0], NSWidth(*backgroundRect), 0));
        textRects[0] = _AFSwitchControlInsetTextRect(textRects[0]);
    

    When this writes to textRects[1], it's scribbling on -mouseDown's stack. Buffer overflow.

    It looks to me like it's happening to clobber self, so the next dereference of self will die. This happens to be the use of _offset.