ccgus / CocoaScript

JavaScript + the Cocoa frameworks, and then ObjC brackets show up to party as well.
Other
618 stars 58 forks source link

address sanitizer error (probably a false positive) #43

Open samdeane opened 8 years ago

samdeane commented 8 years ago

I’ve been debugging a Sketch script, and hit this guy in the debugger:

=================================================================
==30290==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000ac7d8 at pc 0x000102d2e62a bp 0x7fff5fbf9430 sp 0x7fff5fbf9428
READ of size 8 at 0x6020000ac7d8 thread T0
    #0 0x102d2e629 in +[MOFunctionArgument toJSValue:inContext:typeEncoding:fullTypeEncoding:storage:] MOFunctionArgument.m:870
    #1 0x102d30c1f in +[MOFunctionArgument structureToJSValue:inContext:cString:storage:initialValues:initialValueCount:convertedValueCount:] MOFunctionArgument.m:1049
    #2 0x102d2fdac in +[MOFunctionArgument structureToJSValue:inContext:cString:storage:] MOFunctionArgument.m:988
    #3 0x102d2ea00 in +[MOFunctionArgument toJSValue:inContext:typeEncoding:fullTypeEncoding:storage:] MOFunctionArgument.m:884
    #4 0x102d254e6 in -[MOFunctionArgument getValueAsJSValueInContext:dereference:] MOFunctionArgument.m:287
    #5 0x102e418a1 in _MOFunctionInvoke MOUtilities.m:631
    #6 0x102e44e5c in MOFunctionInvoke MOUtilities.m:674
    #7 0x102d5338d in MOFunction_callAsFunction MochaRuntime.m:1644
    #8 0x7fff89c63629 in JSC::JSCallbackObject<JSC::JSDestructibleObject>::call(JSC::ExecState*) (JavaScriptCore+0x49b629)
    #9 0x7fff8980cd8f in JSC::LLInt::setUpCall(JSC::ExecState*, JSC::Instruction*, JSC::CodeSpecializationKind, JSC::JSValue, JSC::LLIntCallLinkInfo*) (JavaScriptCore+0x44d8f)
    #10 0x7fff89d0964c in llint_entry (JavaScriptCore+0x54164c)
    #11 0x7fff89d09657 in llint_entry (JavaScriptCore+0x541657)
    #12 0x7fff89d09657 in llint_entry (JavaScriptCore+0x541657)
    #13 0x7fff89d09657 in llint_entry (JavaScriptCore+0x541657)
    #14 0x7fff89d09657 in llint_entry (JavaScriptCore+0x541657)
    #15 0x7fff89d09657 in llint_entry (JavaScriptCore+0x541657)
    #16 0x7fff89d03ac8 in vmEntryToJavaScript (JavaScriptCore+0x53bac8)
    #17 0x7fff89c317b8 in JSC::JITCode::execute(JSC::VM*, JSC::ProtoCallFrame*) (JavaScriptCore+0x4697b8)
    #18 0x7fff8980f5ec in JSC::Interpreter::executeCall(JSC::ExecState*, JSC::JSObject*, JSC::CallType, JSC::CallData const&, JSC::JSValue, JSC::ArgList const&) (JavaScriptCore+0x475ec)
    #19 0x7fff8980f3ed in JSC::call(JSC::ExecState*, JSC::JSValue, JSC::CallType, JSC::CallData const&, JSC::JSValue, JSC::ArgList const&) (JavaScriptCore+0x473ed)
    #20 0x7fff8980f318 in JSObjectCallAsFunction (JavaScriptCore+0x47318)
    #21 0x102d5ce6d in -[Mocha callJSFunction:withArgumentsInArray:] MochaRuntime.m:669
    #22 0x102d5c7d9 in -[Mocha callJSFunctionWithName:withArgumentsInArray:] MochaRuntime.m:650
    #23 0x102d5bf4f in -[Mocha callFunctionWithName:withArgumentsInArray:] MochaRuntime.m:624
    #24 0x102d785f5 in -[COScript callFunctionNamed:withArguments:] COScript.m:391
    #25 0x100854c45 in -[MSPluginCommand runHandler:context:] MSPluginCommand.m:193
    #26 0x100855fa2 in -[MSPluginCommand runHandlerWithKey:context:manager:] MSPluginCommand.m:221
    #27 0x1008553e9 in -[MSPluginCommand run:manager:] MSPluginCommand.m:202
    #28 0x100339044 in __58-[AppController(PluginSupport) runPluginCommand:fromMenu:]_block_invoke AppController+Scripting.m:153
    #29 0x100c08450 in -[MSActionController(ActionObserving) performFakeActionWithID:context:block:] MSActionController.m:114
    #30 0x100338b22 in -[AppController(PluginSupport) runPluginCommand:fromMenu:] AppController+Scripting.m:152
    #31 0x1003347e0 in -[AppController(PluginSupport) runPlugin:] AppController+Scripting.m:45
    #32 0x7fff9408b079 in _os_activity_initiate (libsystem_trace.dylib+0x2079)
    #33 0x7fff995bfdbc in -[NSApplication sendAction:to:from:] (AppKit+0x2b1dbc)
    #34 0x7fff995bfb56 in -[NSMenuItem _corePerformAction] (AppKit+0x2b1b56)
    #35 0x7fff995bf8b6 in -[NSCarbonMenuImpl performActionWithHighlightingForItemAtIndex:] (AppKit+0x2b18b6)
    #36 0x7fff9408b079 in _os_activity_initiate (libsystem_trace.dylib+0x2079)
    #37 0x7fff99651e53 in -[NSMenu performActionForItemAtIndex:] (AppKit+0x343e53)
    #38 0x7fff99651dc6 in -[NSMenu _internalPerformActionForItemAtIndex:] (AppKit+0x343dc6)
    #39 0x7fff99651c1e in -[NSCarbonMenuImpl _carbonCommandProcessEvent:handlerCallRef:] (AppKit+0x343c1e)
    #40 0x7fff994f6fd8 in NSSLMMenuEventHandler (AppKit+0x1e8fd8)
    #41 0x7fff8cab97bd in DispatchEventToHandlers(EventTargetRec*, OpaqueEventRef*, HandlerCallRec*) (HIToolbox+0x87bd)
    #42 0x7fff8cab8c47 in SendEventToEventTargetInternal(OpaqueEventRef*, OpaqueEventTargetRef*, HandlerCallRec*) (HIToolbox+0x7c47)
    #43 0x7fff8cace9e5 in SendEventToEventTarget (HIToolbox+0x1d9e5)
    #44 0x7fff8cb18999 in SendHICommandEvent(unsigned int, HICommand const*, unsigned int, unsigned int, unsigned char, void const*, OpaqueEventTargetRef*, OpaqueEventTargetRef*, OpaqueEventRef**) (HIToolbox+0x67999)
    #45 0x7fff8cb43d5a in SendMenuCommandWithContextAndModifiers (HIToolbox+0x92d5a)
    #46 0x7fff8cb43d0b in SendMenuItemSelectedEvent (HIToolbox+0x92d0b)
    #47 0x7fff8cb43be7 in FinishMenuSelection(SelectionData*, MenuResult*, MenuResult*) (HIToolbox+0x92be7)
    #48 0x7fff8cb44595 in MenuSelectCore(MenuData*, Point, double, unsigned int, OpaqueMenuRef**, unsigned short*) (HIToolbox+0x93595)
    #49 0x7fff8cb4422f in _HandleMenuSelection2 (HIToolbox+0x9322f)
    #50 0x7fff994e20f9 in _NSHandleCarbonMenuEvent (AppKit+0x1d40f9)
    #51 0x7fff9935713c in _DPSNextEvent (AppKit+0x4913c)
    #52 0x7fff99356225 in -[NSApplication _nextEventMatchingEventMask:untilDate:inMode:dequeue:] (AppKit+0x48225)
    #53 0x7fff9934ad7f in -[NSApplication run] (AppKit+0x3cd7f)
    #54 0x7fff99314367 in NSApplicationMain (AppKit+0x6367)
    #55 0x1000b7691 in main main.m:17
    #56 0x7fff968785ac in start (libdyld.dylib+0x35ac)
    #57 0x2  (Sketch Xcode Debug)+0x2)

0x6020000ac7d8 is located 0 bytes to the right of 8-byte region [0x6020000ac7d0,0x6020000ac7d8)
allocated by thread T0 here:
    #0 0x101f639c0 in wrap_malloc (libclang_rt.asan_osx_dynamic.dylib+0x489c0)
    #1 0x102d23e1a in -[MOFunctionArgument allocateStorage] MOFunctionArgument.m:220
    #2 0x102d2226d in -[MOFunctionArgument setPointerTypeEncoding:withCustomStorage:] MOFunctionArgument.m:121
    #3 0x102d21e9b in -[MOFunctionArgument setPointerTypeEncoding:] MOFunctionArgument.m:109
    #4 0x102e43dd7 in MOParseObjCMethodEncoding MOUtilities.m:786
    #5 0x102e3cf24 in _MOFunctionInvoke MOUtilities.m:365
    #6 0x102e44e5c in MOFunctionInvoke MOUtilities.m:674
    #7 0x102d5338d in MOFunction_callAsFunction MochaRuntime.m:1644
    #8 0x7fff89c63629 in JSC::JSCallbackObject<JSC::JSDestructibleObject>::call(JSC::ExecState*) (JavaScriptCore+0x49b629)
    #9 0x7fff8980cd8f in JSC::LLInt::setUpCall(JSC::ExecState*, JSC::Instruction*, JSC::CodeSpecializationKind, JSC::JSValue, JSC::LLIntCallLinkInfo*) (JavaScriptCore+0x44d8f)
    #10 0x7fff89d0964c in llint_entry (JavaScriptCore+0x54164c)
    #11 0x7fff89d09657 in llint_entry (JavaScriptCore+0x541657)
    #12 0x7fff89d09657 in llint_entry (JavaScriptCore+0x541657)
    #13 0x7fff89d09657 in llint_entry (JavaScriptCore+0x541657)
    #14 0x7fff89d09657 in llint_entry (JavaScriptCore+0x541657)
    #15 0x7fff89d09657 in llint_entry (JavaScriptCore+0x541657)
    #16 0x7fff89d03ac8 in vmEntryToJavaScript (JavaScriptCore+0x53bac8)
    #17 0x7fff89c317b8 in JSC::JITCode::execute(JSC::VM*, JSC::ProtoCallFrame*) (JavaScriptCore+0x4697b8)
    #18 0x7fff8980f5ec in JSC::Interpreter::executeCall(JSC::ExecState*, JSC::JSObject*, JSC::CallType, JSC::CallData const&, JSC::JSValue, JSC::ArgList const&) (JavaScriptCore+0x475ec)
    #19 0x7fff8980f3ed in JSC::call(JSC::ExecState*, JSC::JSValue, JSC::CallType, JSC::CallData const&, JSC::JSValue, JSC::ArgList const&) (JavaScriptCore+0x473ed)
    #20 0x7fff8980f318 in JSObjectCallAsFunction (JavaScriptCore+0x47318)
    #21 0x102d5ce6d in -[Mocha callJSFunction:withArgumentsInArray:] MochaRuntime.m:669
    #22 0x102d5c7d9 in -[Mocha callJSFunctionWithName:withArgumentsInArray:] MochaRuntime.m:650
    #23 0x102d5bf4f in -[Mocha callFunctionWithName:withArgumentsInArray:] MochaRuntime.m:624
    #24 0x102d785f5 in -[COScript callFunctionNamed:withArguments:] COScript.m:391
    #25 0x100854c45 in -[MSPluginCommand runHandler:context:] MSPluginCommand.m:193
    #26 0x100855fa2 in -[MSPluginCommand runHandlerWithKey:context:manager:] MSPluginCommand.m:221
    #27 0x1008553e9 in -[MSPluginCommand run:manager:] MSPluginCommand.m:202
    #28 0x100339044 in __58-[AppController(PluginSupport) runPluginCommand:fromMenu:]_block_invoke AppController+Scripting.m:153
    #29 0x100c08450 in -[MSActionController(ActionObserving) performFakeActionWithID:context:block:] MSActionController.m:114

SUMMARY: AddressSanitizer: heap-buffer-overflow MOFunctionArgument.m:870 in +[MOFunctionArgument toJSValue:inContext:typeEncoding:fullTypeEncoding:storage:]
Shadow bytes around the buggy address:
  0x1c04000158a0: fa fa fd fd fa fa fd fd fa fa fd fa fa fa 00 00
  0x1c04000158b0: fa fa fd fa fa fa 00 00 fa fa fd fa fa fa 00 00
  0x1c04000158c0: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa fd fa
  0x1c04000158d0: fa fa 00 fa fa fa fd fd fa fa 00 fa fa fa fd fa
  0x1c04000158e0: fa fa 00 00 fa fa fd fa fa fa 00 00 fa fa 00 fa
=>0x1c04000158f0: fa fa fd fa fa fa 00 00 fa fa 00[fa]fa fa fd fa
  0x1c0400015900: fa fa 00 00 fa fa 00 02 fa fa fd fd fa fa 00 06
  0x1c0400015910: fa fa 00 fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c0400015920: fa fa fa fa fa fa fa fa fa fa fa fa fa fa 00 00
  0x1c0400015930: fa fa 00 00 fa fa 00 00 fa fa fa fa fa fa fa fa
  0x1c0400015940: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==30290==ABORTING
AddressSanitizer report breakpoint hit. Use 'thread info -s' to get extended information about the report.

It’s probably a false positive, but it is in an interesting area.

I’m fairly sure that the function being called is a glyphRangeForCharacterRange:actualCharacterRange which takes a pointer to an NSRange. The script in question is passing a new MOPointer for this.

The failure seems to be happening whilst trying to read the second field (length) of the range structure, presumably after the call has completed.

As far as I can see, the code is allocating storage of the correct size, and is calculating the correct offsets, but asan seems to disagree (and I’m not 100% clear how/when the MOPointerValue storage gets allocated in this situation).

samdeane commented 8 years ago

It’s a long-shot, and I’m working on the assumption that it’s probably asan getting confused, but if it rings any bells @ccgus (or @logancollins), let me know!

samdeane commented 8 years ago

The line it’s actually hitting is: https://github.com/BohemianCoding/CocoaScript/blob/develop/src/framework/mocha/Utilities/MOFunctionArgument.m#L870

ccgus commented 8 years ago

No bells are ringing, sorry.

logancollins commented 8 years ago

Yeah, not ringing any bells for me either. I haven’t run any of my projects using it with the address sanitizer.

Logan

On Sep 6, 2016, at 10:35 AM, ccgus notifications@github.com wrote:

No bells are ringing, sorry.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ccgus/CocoaScript/issues/43#issuecomment-245027775, or mute the thread https://github.com/notifications/unsubscribe-auth/AArmBmpg0dNtRgIVR_DOVN6lC-60eBuPks5qnaRPgaJpZM4J2AXB.

samdeane commented 8 years ago

I've got a small test script which can reproduce this in the coscript tool:

framework("Foundation");

var string = NSMutableString.alloc().initWithString_("&#x2766;&#x2766;&#x2766;&#x2766;&#x2766;&#x2766;")
var range = NSMakeRange(0, 13)
var updatedRange = MOPointer.new()

var ok = string.applyTransform_reverse_range_updatedRange_(NSStringTransformToXMLHex, true, range, updatedRange)
print("result was " + ok)
print(updatedRange.location)

Perhaps that's a misunderstanding abut how MOPointer is supposed to be used? If not, I think there's a genuine bug here.

What seems to happen is that storage is allocated for the updatedRange argument, and it's the size of a pointer.

When Mocha is unpacking the results it gets back from the function call, it tries to unpack the NSRange structure from the storage that was allocated for the pointer, as if it was actually the NSRange structure.

ASAN reports a buffer overflow when it tries to read from the second double of the NSRange, since the storage is actually only for a pointer.

samdeane commented 8 years ago

If I instead change updatedRange to have a value passed in:

var updatedRange = MOPointer.alloc().initWithValue_(range)

then ASAN instead falls over when the value is being set, before the function invocation.

In this case it's a similar problem in reverse - the code is trying to copy the value into _storage as if it is an NSRange, but in fact it's only the size of a pointer.

samdeane commented 8 years ago

Part of the problem I think seems to be the dereference arguments in setValueAsJSValue and getValueAsJSValueInContext.

When these are true, the type encoding is treated as if the argument is actually the structure, and not a pointer to the structure. But no modification is made to the pointer, which still points at _storage, which is still the size of a pointer.

I'm not sure whether the error is actually that this code is wrong, or that _storage should have somehow have been set up to be the correct size by then.

samdeane commented 8 years ago

Any hints gratefully received @logancollins 😁

samdeane commented 7 years ago

I've been tracing through a similar crash recently, and I think I've satisfied myself that there is a bug in Mocha around this.

When setting up the arguments for a function call, if an argument is supposed to be a pointer to a structure (eg the second argument to CGPathCreateCopyByTransformingPath), and if I pass in an MOPointer initialised with a structure value, what seems to happen is that storage is allocated for the pointer to the structure, but then the value of the structure is written into that storage... likely overwriting the end of the block. As one would expect, sometimes it survives this, sometimes it doesn't.

Hacking the malloc in MOFunctionArgument allocStorage to allocate 1024 bytes more than it needs is enough to fix my crasher. Clearly not a proper fix, and perhaps it's just a coincidence, but it suggests that I'm right about the overwrite.

If I am correct, I'm not quite sure what the fix is. Either the type encoding being used in allocateStorage is wrong in this case, or (I think more likely), it is right, but we need to allocate another block somewhere/somehow to contain the struct value, and then write the pointer to that block into the original storage.

SheffieldKevin commented 6 years ago

This can be closed. The example code that Sam has above we now realise can be fixed with the following.

framework("Foundation");

var string = NSMutableString.alloc().initWithString_("&#x2766;&#x2766;&#x2766;&#x2766;&#x2766;&#x2766;")
var range = NSMakeRange(0, 13)
var updatedRange = NSMakeRange(0, 0)
var updatedRangePtr = MOPointer.new(updatedRange)

var ok = string.applyTransform_reverse_range_updatedRange_(NSStringTransformToXMLHex, true, range, updatedRangePtr)
print("result was " + ok)
print(updatedRange.location)
samdeane commented 6 years ago

Interesting. So in the example, I was incorrectly using the MOPointer object?

My reading of it was that it was supposed to be able to allocate its own storage for the thing it was pointing to (in the case where you were using it to receive a value). The usage pattern you're using suggests that I got that wrong?

SheffieldKevin commented 6 years ago

Hi Sam 👋 It doesn't seem to. @mathieudutour tried adding some type information to the pointer so that CocoaScript new the type of object that the pointer needed to be pointing to to but the address sanitizer still fired. The above fixed the problem. Perhaps there is a problem in CocoaScript but in this instance the problem is now fixed.