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: avoid crash when clear all stubed object #722

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

cchicken
Copy link

there is a crash when some stubed object has been release during one testcase

crash stack:

1. KWInterceptedObjectKey()
2. KWObjectClassRestored()
3. KWClearAllObjectStubs()
...

reason

  1. when stubedObject has been release, key() at line 3 in KWClearAllObjectStubs() will return nil
void KWClearAllObjectStubs(void) {
    for (KWInterceptedObjectBlock key in KWObjectStubs) {
        id stubbedObject = key();
        if (KWObjectClassRestored(stubbedObject)) {
            continue;
        }
        KWRestoreOriginalClass(stubbedObject);
    }
    [KWObjectStubs removeAllObjects];
}
  1. KWObjectClassRestored() invoke KWInterceptedObjectKey() with nil
BOOL KWObjectClassRestored(id anObject) {
    return [KWRestoredObjects containsObject:KWInterceptedObjectKey(anObject)];
}
  1. objc_setAssociatedObject() will crash when anObject is nil, and key is not nil
KWInterceptedObjectBlock KWInterceptedObjectKey(id anObject) {
    KWInterceptedObjectBlock key = objc_getAssociatedObject(anObject, kKWInterceptedObjectKey);
    if (key == nil) {
        __weak id weakobj = anObject;
        key = ^{ return weakobj; };
        objc_setAssociatedObject(anObject, kKWInterceptedObjectKey, [key copy], OBJC_ASSOCIATION_COPY);
    }
    return key;
}

according to https://opensource.apple.com/source/objc4/objc4-781/runtime/objc-references.mm

void
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
    // This code used to work when nil was passed for object and key. Some code
    // probably relies on that to not crash. Check and handle it explicitly.
    // rdar://problem/44094390
    if (!object && !value) return;

    if (object->getIsa()->forbidsAssociatedObjects())
        _objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
    ...

I guess if (!object && !value) return; should be if (!object) return;

solution

check anObject is non nil before invoke objc_setAssociatedObject()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant