realm / realm-browser-osx

DEPRECATED - Realm Browser for Mac OS X has been replaced by realm-studio which is cross platform.
https://realm.io
503 stars 54 forks source link

More stable reproduction case for #297 #311

Closed stel closed 7 years ago

stel commented 7 years ago

While trying different workarounds for #297 found more stable way to reproduce the issue:

  1. Install and run Realm Object Server and create an admin account
  2. Open the Browser, select "Connect to Object Server..." and specify admin username and password, click "Connect".
  3. Try to open __admin realm from the server browser window.

Even if the realm is opened it becomes corrupted. Otherwise there is a crash.

Few notes about what's going on there:

  1. When Browser starts it deletes realm-object-server directory with all cached realms.
  2. When you press "Connect" Browser opens __admin realm, waits for the first notification (at this point the schema is loaded) then closes it and opens it again.
  3. When you open __admin realm by double clicking it, Browser does the same as in 2 but with the unique local file path for caching synced realm.

/cc @tgoyne, @jpsim

stel commented 7 years ago

After some more investigation it seems like the issue is related to realm size also, it doesn't reproduce for small realms.

I've made an example app that generates a realm file on the server with 10K records. Trying to open this URL (/crash) leads to crash.

generate.zip

austinzheng commented 7 years ago

It looks like something bad is happening to the info object on the Realm between creation and use. Note how the name field changes from "Widget" to something completely invalid between the two invocations.

Good good1

Bad bad1

austinzheng commented 7 years ago

My guess is that it has something to do with implicit conversions from std::string to StringData. I refactored realm::ObjectSchema in my pod to force conversions to StringData take place on a copy of the string, and it seems to work now.

austinzheng commented 7 years ago

Note that the correct fix is to figure out why the member variable is becoming corrupt to begin with, and fixing the underlying bug. That's what I'm working on.

austinzheng commented 7 years ago

Actually, running Instruments leads me to believe that the entire rlmObjectSchema (ed: should be objectSchema) may be invalid at the time the crash happens. (This makes sense; there are other changes in the printed-out description in addition to the bogus string.)

jpsim commented 7 years ago

That also makes sense at a higher level: schema changes are being applied after the RLMSchema & accessors have already been constructed, and schema changes that are being applied (by virtue of transactions being reapplied locally) are causing state to change and we're not reacting entirely defensively for something in those changes.

bdash commented 7 years ago

The realm::ObjectSchema pointed to by RLMClassInfo.objectSchema has definitely been destroyed.

The malloc_history output tells the story:

FREE  0x6080000bc620-0x6080000bc67f [size=96]: thread_100f643c0 | start | main |
NSApplicationMain | -[NSApplication run] | -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] |
_DPSNextEvent | _BlockUntilNextEventMatchingListInModeWithFilter | ReceiveNextEventCommon |
RunCurrentEventLoopInMode | CFRunLoopRunSpecific | __CFRunLoopRun | __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ |
_dispatch_main_queue_callback_4CF | _dispatch_client_callout | _dispatch_call_block_and_release |
__49-[RLMDynamicSchemaLoader schemaDidLoadWithError:]_block_invoke | __53-[RLMDocument loadWithCredentials:completionHandler:]_block_invoke_4 |
-[RLMDocument loadWithError:] | _NSSetLongLongValueAndNotify | -[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:] |
-[NSObject(NSKeyValueObservingPrivate) _changeValueForKeys:count:maybeOldValuesDict:usingBlock:] | NSKeyValueDidChange |
NSKeyValueNotifyObserver | -[RLMRealmBrowserWindowController observeValueForKeyPath:ofObject:change:context:] |
-[RLMRealmBrowserWindowController documentSchemaLoaded] | -[RLMRealmBrowserWindowController handleDocumentState] |
-[RLMRealmBrowserWindowController realmDidLoad] | -[RLMTypeOutlineViewController realmDidLoad] |
-[NSOutlineView expandItem:expandChildren:] | -[NSOutlineView _batchExpandItemsWithItemEntries:expandChildren:] |
-[NSTableRowData _doWorkAfterEndUpdates] | -[NSTableRowData _updateVisibleViewsBasedOnUpdateItemsAnimated] |
-[NSTableRowData _updateVisibleViewsBasedOnUpdateItems] | -[NSTableRowData _addRowViewForVisibleRow:withPriorView:] |
-[NSTableRowData _initializeRowView:atRow:] | -[NSTableRowData _addViewsToRowView:atRow:] |
-[NSTableRowData _addViewToRowView:atColumn:row:] | -[NSTableView(NSTableViewViewBased) makeViewForTableColumn:row:] |
-[RLMTypeOutlineViewController outlineView:viewForTableColumn:item:] | -[RLMClassNode cellViewForTableView:] |
-[RLMClassNode instanceCount] | -[RLMClassNode allObjects] | -[RLMRealm allObjects:] | RLMGetObjects |
RLMClassInfo::table() const | -[RLMRealm group] | realm::Realm::read_group() | realm::Realm::begin_read(realm::VersionID) |
realm::Realm::read_schema_from_group_if_needed() | realm::Schema::operator=(realm::Schema&&) |
std::__1::vector<realm::ObjectSchema, std::__1::allocator<realm::ObjectSchema> >::__move_assign(std::__1::vector<realm::ObjectSchema, std::__1::allocator<realm::ObjectSchema> >&, std::__1::integral_constant<bool, true>) |
std::__1::vector<realm::ObjectSchema, std::__1::allocator<realm::ObjectSchema> >::deallocate() 

It looks like the realm::ObjectSchema* that the RLMClassInfo holds points into the vector that is realm::Schema. When the schema is re-read by Realm::read_schema_from_group_if_needed(), it can invalidate any pointers into that object schema vector. It looks like this problem may be specific to the dynamic schema mode used by the Browser, as the non-dynamic schema case doesn't perform the assignment that is invalidating the vector.

bdash commented 7 years ago

I'm not sure what the best way is to address the broader design problem (the Cocoa binding RLMClassInfo storing a pointer to an element of a vector that can be reallocated when the schema changes), but since no schema change is occurring here it should be straightforward to prevent the crash.

stel commented 7 years ago

Thanks a lot @bdash, @austinzheng!

https://github.com/realm/realm-object-store/issues/450 works perfectly.

Closing this PR.