This is an archive of the discontinued LLVM Phabricator instance.

[analyzer] Detect duplicate [super dealloc] calls
ClosedPublic

Authored by dcoughlin on Sep 7 2014, 8:14 PM.

Details

Summary

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).

Diff Detail

Repository
rL LLVM

Event Timeline

ddkilzer updated this revision to Diff 13380.Sep 7 2014, 8:14 PM
ddkilzer retitled this revision from to [analyzer] Detect duplicate [super dealloc] calls.
ddkilzer updated this object.
ddkilzer edited the test plan for this revision. (Show Details)
ddkilzer added a subscriber: Unknown Object (MLST).
ddkilzer updated this object.Sep 7 2014, 8:20 PM

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();
ddkilzer updated this revision to Diff 13425.Sep 8 2014, 3:49 PM
ddkilzer updated this object.
  • 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.
ddkilzer updated this object.Sep 8 2014, 3:52 PM
zaks.anna edited edge metadata.Oct 2 2014, 4:35 PM

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
156 ↗(On Diff #13425)

Is this done for performance purposes? Can [super dealloc] be called from functions? isSuperDeallocMessage check seems faster.

161 ↗(On Diff #13425)

It would be better to do an early return here as well, unless you plan to expand this for handling other calls.
if (!isSuperDeallocMessage(M))

return;
182 ↗(On Diff #13425)

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
156 ↗(On Diff #13425)

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.

161 ↗(On Diff #13425)

Sure, will fix.

182 ↗(On Diff #13425)

Got it! Will fix.

dcoughlin commandeered this revision.Jan 22 2016, 11:51 AM
dcoughlin added a reviewer: ddkilzer.
dcoughlin updated this revision to Diff 47412.Feb 9 2016, 6:26 PM
dcoughlin edited edge metadata.

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
11 ↗(On Diff #47412)

Please, be more specific about the properties we are checking for.

91 ↗(On Diff #47412)

Not sure if the extra check buys us much (right?), but it's not on a hot path, so we could keep it.

102 ↗(On Diff #47412)

maybe remove "originally"?

112 ↗(On Diff #47412)

"should never be" -> "should not be"?
"twice" -> "more than once"

126 ↗(On Diff #47412)

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.

155 ↗(On Diff #47412)

Maybe we should say "more than once" or "again" in case we've missed a call to [super] dealloc.

164 ↗(On Diff #47412)

You should consider extending the helper method that Gabor added for checking if a specific function has been called in
http://reviews.llvm.org/D15921

test/Analysis/DeallocUseAfterFreeErrors.m
271 ↗(On Diff #47412)

you can put expected-note on the next line using "expected-note@-1"

dcoughlin updated this revision to Diff 47514.Feb 10 2016, 1:32 PM

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.

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?

I will first extend this checker to check for uses of self after dealloc and then evaluate.

dcoughlin updated this revision to Diff 48447.Feb 18 2016, 6:25 PM

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.)
zaks.anna accepted this revision.Feb 19 2016, 2:09 PM
zaks.anna edited edge metadata.
This revision is now accepted and ready to land.Feb 19 2016, 2:09 PM
This revision was automatically updated to reflect the committed changes.