Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix segmentation fault when pushing a class object based on Realm.Object to a list #3610

Merged
merged 1 commit into from
Mar 8, 2021

Conversation

takameyer
Copy link
Contributor

@takameyer takameyer commented Feb 23, 2021

What, How & Why?

When creating a class that extends Realm.Object and pushing the instantiated object to a list, a segmentation fault would occur.

class TodoItem extends Realm.Object {
    constructor(description) {
        super()
        this.id = `${new ObjectId()}`
        this.description = description;
        this.done = false;
    }

    static schema = {
        name: "TodoItem",
        properties: {
            id: "string",
            description: "string",
            done: {type: "bool", default: false},
            deadline: "date?"
        },
        primaryKey: "id"
    }
}

class TodoList extends Realm.Object {

    constructor(name ) {
        super()
        this.id = `${new ObjectId()}`
        this.name = name;
        this.items = []
    }

    static schema = {
        name: "TodoList",
        properties: {
            id: "string",
            name: "string",
            items: "TodoItem[]"
        },
        primaryKey: "id"
    }
}

const realm = new Realm({schema: [TodoList, TodoItem]});
realm.write(() => {
    const list = realm.create(TodoList, new TodoList('My Todo List'))
    list.items.push(new TodoItem("Fix that bug"))

    TestCase.assertEqual(1, list.items.length)
})

The problem is that realm was assuming that if the object is of type Realm.Object that it has already been created. This assumption was causing a null pointer to be dereferenced which had caused the segmentation fault. The logic now checks for null and throws an exception.

Also attempting to create an object with an unmanged instance of Realm.Object will throw an exception. This is to avoid future attempts to create which fail, as the prototype has been changed.

☑️ ToDos

  • 📝 Changelog entry
  • 📝 Compatibility label is updated or copied from previous entry
  • 🚦 Tests
  • 📝 Public documentation PR created or is not necessary
  • 💥 Breaking label has been applied or is not necessary

If this PR adds or changes public API's:

  • typescript definitions file is updated
  • jsdoc files updated
  • Chrome debug API is updated if API is available on React Native

@takameyer takameyer force-pushed the andrew/list-push-segfault branch 2 times, most recently from d589ba7 to f27e77c Compare February 23, 2021 15:24
CHANGELOG.md Outdated Show resolved Hide resolved
@kraenhansen
Copy link
Member

Let's have a face 2 face conversation around this, as I don't think this is the right approach.
I agree that passing an instance of a class inheriting from Realm.Object shouldn't crash the app.
At the same time, I think it should throw a meaningful error instead of allowing object creation from this.

In my understanding of the current implementation (and limitations) of our "class-based schema" support, we do not support users to explicitly call the schema class's constructor, hence it's undefined behaviour and not something we should be providing work arounds for. I also agree that we should continue improving the class based support, ideally to the point where explicitly calling the class constructor is supported, but this requires more work than this PR suggests.

Copy link

@fronck fronck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@cesarvr cesarvr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@takameyer takameyer force-pushed the andrew/list-push-segfault branch 2 times, most recently from e9c0533 to 1853669 Compare February 25, 2021 10:44
@takameyer takameyer requested review from kneth and fronck February 25, 2021 10:44
@takameyer
Copy link
Contributor Author

@kraenhansen and I decided it would make more sense to throw an exception instead of implicitly creating the object on push. We also added an exception when someone tries to use the class constructor to create an object, as this breaks the second time.

In the future we will rethink class support.

Copy link
Contributor

@steffenagger steffenagger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's definitely better to have consistent behaviour & exceptions instead of crashes, but should we word this as temporary fixes?

CHANGELOG.md Outdated Show resolved Hide resolved
@takameyer takameyer force-pushed the andrew/list-push-segfault branch 2 times, most recently from 5fbdf42 to 0493f50 Compare March 1, 2021 09:27
Copy link

@fronck fronck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few nitpicks and a general question.

Other than that, LGTM

src/js_object_accessor.hpp Outdated Show resolved Hide resolved
src/js_realm.hpp Outdated Show resolved Hide resolved
}
}

TodoItem.schema = {
Copy link

@fronck fronck Mar 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Question] The tests usually put their schemas in schemas.js.. Is there any specific reason why these don't?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I would perhaps like to have a conversation about this. I find in tests it is best to not put such things in separate files, as they rarely are reused and it makes the reader of the test have a nicer time following what is happening. So I guess that's more my personal style and opinion.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong opinion on the subject myself..
@kneth ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are using the same schemas across tests, so we avoid code duplication with schemas.js.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are going to create a new issue for this where we will address refactor the tests and write a style guide.

When creating a class that extends Realm.Object and pushing the instantiated object to a list, a segmentation fault would occur.

The problem is that realm was assuming that if the object is of type Realm.Object that it has already been created. This assumption was causing a null pointer to be dereferenced which had caused the segmentation fault. The logic now checks for null and throws an exception.

Also attempting to create an object with an unmanged instance of Realm.Object will throw an exception. This is to avoid future attempts to create which fail, as the prototype has been changed.
@takameyer takameyer force-pushed the andrew/list-push-segfault branch from 0493f50 to 096d7e1 Compare March 4, 2021 15:54
@takameyer takameyer merged commit f4d68d6 into master Mar 8, 2021
@takameyer takameyer deleted the andrew/list-push-segfault branch March 8, 2021 10:11
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 16, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants