Repository navigation
Only take back an autorelease that objc_autoreleaseReturnValue() made - #426
Closed
ashalkhakov wants to merge 1 commit into
Closed
ashalkhakov wants to merge 1 commit into
ashalkhakov wants to merge 1 commit into
Conversation
With the runtime's own autorelease pool, objc_retainAutoreleasedReturnValue()
decides that its argument was autoreleased by the callee's
objc_autoreleaseReturnValue() when the object on top of the pool is the
same object, and then pops it instead of retaining. That is not enough to
know. A callee need not have autoreleased what it returns at all: the
getter clang synthesizes for a nonatomic strong property returns its ivar
unretained, and its caller still calls objc_retainAutoreleasedReturnValue().
If the top of the pool holds the same object for another reason, that
autorelease is taken for the return value's: popped, not retained, and
released at the end of the caller's use. The object loses the reference
the pool was keeping for someone else.
It happens in the most ordinary error-reporting code:
- (id)doSomething: (NSError **)error
{
...
*error = self.error; // retained, autoreleased: on top of the pool
return self.error ? nil : ...; // the getter again: that autorelease is popped
}
The caller receives an error the pool no longer keeps, and once the
object that held it goes away, the error is freed while the caller still
has it. On macOS the same code is fine.
objc_autoreleaseReturnValue() now records the pool slot it filled, and
objc_retainAutoreleasedReturnValue() pops only that slot, and only while
it is still the top of the pool and still holds the object; the record is
cleared either way. This also stops the check from reading below the
start of a pool page that has just been started.
Test/AutoreleasedReturnValue_arc.m aborts without this change, in the
plain and the optimised builds, and passes with it; the rest of the
suite passes either way.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
With the runtime's own autorelease pool, objc_retainAutoreleasedReturnValue() decides that its argument was autoreleased by the callee's objc_autoreleaseReturnValue() when the object on top of the pool is the same object, and then pops it instead of retaining. That is not enough to know. A callee need not have autoreleased what it returns at all: the getter clang synthesizes for a nonatomic strong property returns its ivar unretained, and its caller still calls objc_retainAutoreleasedReturnValue(). If the top of the pool holds the same object for another reason, that autorelease is taken for the return value's: popped, not retained, and released at the end of the caller's use. The object loses the reference the pool was keeping for someone else.
It happens in the most ordinary error-reporting code:
The caller receives an error the pool no longer keeps, and once the object that held it goes away, the error is freed while the caller still has it. On macOS the same code is fine.
objc_autoreleaseReturnValue() now records the pool slot it filled, and objc_retainAutoreleasedReturnValue() pops only that slot, and only while it is still the top of the pool and still holds the object; the record is cleared either way. This also stops the check from reading below the start of a pool page that has just been started.
Test/AutoreleasedReturnValue_arc.m aborts without this change, in the plain and the optimised builds, and passes with it; the rest of the suite passes either way.
🤖 Generated with Claude Code