Implement a path checker that warns about duplicate calls to [super dealloc]. This is the first step before re-implementing the use-after-free checks using a path checker (see D5042).
Details
Diff Detail
Event Timeline
FIXME: This path checker needs to discriminate between different 'self' symbols when recursing into the -dealloc method in the same class (for different objects). See test case.
So I need to test the implicit 'self' argument (for the current method) whenever I encounter a '[super dealloc]' call so I can differentiate the same '[super dealloc]' call for different objects. I'm not sure whether I should use an SVal or a SymbolRef (or maybe an ImplicitParamDecl) to store the 'self' symbol, though. Any tips?
I can probably switch to REGISTER_MAP_WITH_PROGRAMSTATE() and store the 'self' symbol in there as well.
Note that I posted this patch for review before fixing that bug to address any (early) feedback.
FIXME: This path checker needs to discriminate between different 'self' symbols when recursing into the -dealloc method in the same class (for different objects). See test case.
Fixed. I needed a SymbolRef (as expected after I thought about it some more). Wow, that was easy:
SymbolRef SelfSymbol = M.getSelfSVal().getAsSymbol();
- Updated help text in Checkers.td to be more generic.
- Fix false positive with classes using custom retain counting and containing an ivar that's the same type as the class. Added test case for this condition.
- Extracted duplicate code into isSuperDeallocMessage().
- Small tweaks to variable names.
- Add section headers for [super dealloc] test cases to explain what each one is testing.
David,
I've listed some small concerns throughout and have not looked at the tests in a lot of detail yet.
However, the main problem is that you store too much state in SuperDeallocState. (We try to keep the states as lean as possible since they are one of the major bottlenecks and have considerable impact on performance.) Most of what you store is only consumed by BugReporterVisitor. You might be able to work around storing this info by lazily reconstructing it at bug reporting time.
When bug is reported, the path is visited bottom up; this is when your visitor will get called. You can teach the visitor to detect the first node in which the SuperDeallocState for the given SymRef has been added (as the path is visited bottom up). At that point, you look up the statement associated with the ProgramPoint and if it's a message call, you found the node to which the diagnostic should be attached. (Take a look at other checkers such as MallocChecker; they are all based on this lazy approach.)
You might also want to play with Stack Hints in error reporting since your reports are likely to span multiple functions. These are the notes that get attached to the call site of the functions that contain the diagnostic. You should use -analyzer-output=text (and -analyzer-output=plist) when testing the full path experience. (Ex: See ./test/Analysis/keychainAPI-diagnostic-visitor.m)
lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp | ||
---|---|---|
157 | Is this done for performance purposes? Can [super dealloc] be called from functions? isSuperDeallocMessage check seems faster. | |
162 | It would be better to do an early return here as well, unless you plan to expand this for handling other calls. return; | |
183 | These should be guarded on isSuperDeallocMessage. Otherwise, we would do a lookup every time we see a method call. |
Thanks for the feedback!
I will look at reducing the state stored by SuperDeallocState.
I will also add stack hints like "[super dealloc] was called here first" for the first call, and "[super dealloc] was called a second time here", or similar.
lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp | ||
---|---|---|
157 | This check was modeled after ObjCSelfInitChecker::checkPostObjCMessage() in ObjCSelfInitChecker.cpp. Heh, I see. The checks in shouldRunOnFunctionOrMethod() are redundant because we're already passed a reference to an ObjCMethodCall. I don't believe [super dealloc] is valid inside a plain C function context. | |
162 | Sure, will fix. | |
183 | Got it! Will fix. |
Updated this to address Anna's comments.
- I've made the state smaller. It is just now a set of SymbolRefs for methods instances that have been dealloc'd.
- I've hoisted isSuperDeallocMessage() to early return when possible.
- I've added tests with -analyzer-output=text.
- I've moved the lazy initialization of the dealloc selector identifier to isSuperDeallocMessage()
Looks good, below are some comments which are mostly nits.
What's the plan for bringing this out of alpha? Is it pending evaluation on real code?
lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp | ||
---|---|---|
12 | Please, be more specific about the properties we are checking for. | |
92 | Not sure if the extra check buys us much (right?), but it's not on a hot path, so we could keep it. | |
103 | maybe remove "originally"? | |
113 | "should never be" -> "should not be"? | |
127 | So we are actually storing the symbol that refers to 'super', right? The receiver of 'dealloc', not the 'self' of the object whose methods are being analyzed. This was confusing reading the code top down; for example, in the bug visitor. | |
156 | Maybe we should say "more than once" or "again" in case we've missed a call to [super] dealloc. | |
165 | You should consider extending the helper method that Gabor added for checking if a specific function has been called in | |
test/Analysis/DeallocUseAfterFreeErrors.m | ||
272 | you can put expected-note on the next line using "expected-note@-1" |
Address more of Anna's comments.
- Add a more explicit comment about checker in header comment
- Changed the checker to always use the receiver symbol rather than the self symbol for clarity.
- Rework SuperDeallocBRVisitor::VisitNode() to add a note on the node where state transitioned from not having CalledSuperDealloc for the receiver symbol to having it set.
- Reworded diagnostic text.
I didn't extend Gabor's function helper to Objective-C. I'll do that in a later patch.
I will first extend this checker to check for uses of self after dealloc and then evaluate.
Addressed additional comments from Anna offline:
- "[super dealloc] called again" is OK as a path note but not good as an error message. I've changed it to "[super dealloc] should not be called multiple times".
- Added a comment about why we need both PreObjCMessage and PostObjCMessage to avoid warning when inling a call to [super dealloc]. (The SubclassCallingSuperDealloc test covers this.)
Please, be more specific about the properties we are checking for.