This is an archive of the discontinued LLVM Phabricator instance.

[Static Analyzer] The first implementation of nullability checker.
ClosedPublic

Authored by xazax.hun on Jul 23 2015, 11:55 AM.

Details

Summary

This is the first implementation of a checker that supposed to catch nullability errors.
Unfortunately the nullability qualifiers do not have well defined meaning, one can not assume that nonnull implies that the pointer can not be null.
In fact, the contract is, when the nullability preconditions on the parameter is not violated, then the nullability postconditions of the return value must not be violated either.
Right now the checker only checks simple rules, for example nullable pointers must be checked before dereferenced, should not pass null or unchecked nullable pointer to nonnul parameter, should not return null or unchecked nullable pointer from a nonnull returning function. The check will probably be relaxed, if one of the (not nullable) parameters known to be null, it will be ok to return null or unchecked nonnull pointer from a nonnull returning function.

Some details are still being worked out, how to define the nullability rules (in terms of this checker) to be able to both discover real issues and avoid false positives, while making it possible for users to suppress warnings (possibly using explicit casts). Once the rules are clear, supporting documentation will be provided.

Note: In order to pass the tests, the following patches should be applied first:
http://reviews.llvm.org/D11433
http://reviews.llvm.org/D11432

Diff Detail

Repository
rL LLVM

Event Timeline

xazax.hun updated this revision to Diff 30510.Jul 23 2015, 11:55 AM
xazax.hun retitled this revision from to [Static Analyzer] The first implementation of nullability checker..
xazax.hun updated this object.
xazax.hun updated this object.
xazax.hun added a subscriber: cfe-commits.
xazax.hun updated this revision to Diff 32869.Aug 21 2015, 3:01 PM
  • Updated to the latest trunk.
  • Relaxed an assert in ExprEngine which turned out to be unsound.
  • The individual checks can be turned on or off.
  • Added some framework specific heuristic to reduce the number of false positive results.
  • Refactoring.
zaks.anna edited edge metadata.Aug 21 2015, 5:39 PM

Partial review...

lib/StaticAnalyzer/Checkers/Checkers.td
137 ↗(On Diff #32869)

Should it be "pointer is used as a nonnull argument"?

149 ↗(On Diff #32869)

"nullable pointer is used as a nonnull argument"?

lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
12 ↗(On Diff #32869)

"is" -> "are"

How are we relying on this assumption? What happens if they are not fixed?

Also we should describe what we mean by nullability warnings, maybe refer to the annotations themselves here? It would be great to give a high level overflow of the algorithm and maybe even talk about the difference between the checks?

54 ↗(On Diff #32869)

??

70 ↗(On Diff #32869)

Same as above - there is no such thing as "nonnull pointer".

152 ↗(On Diff #32869)

I don't like that we assume ordering here. Someone might sort the values in the enum and break this. Please, change this.

179 ↗(On Diff #32869)

Please, add comments.
What is Source?

192 ↗(On Diff #32869)

Is this used for optimization purposes? Can you describe the rules we use here?

What's the benefit of not tracking self?

259 ↗(On Diff #32869)

Maybe rename to "getNullabilityAnnotation" ?

267 ↗(On Diff #32869)

shouldn't this be an llvm_unreachable?

298 ↗(On Diff #32869)

Are we sure that if "!TrackedNullability", the event complains about a dereference on the parent (like field access)?

317 ↗(On Diff #32869)

Let's use an existing helper here:
inline bool Type::isAnyPointerType() const {

return isPointerType() || isObjCObjectPointerType();

}

xazax.hun marked 11 inline comments as done.Aug 24 2015, 11:43 AM

Thank you for the review. I will upload the new version of the patch once the rest of it was reviewed.

lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
12 ↗(On Diff #32869)

There are no assumptions like that in the code of the checker right now. I updated the comment to reflect that.

54 ↗(On Diff #32869)

Added a comment to clarify what it is and how it is used.

192 ↗(On Diff #32869)

Initially it was not just an optimization. It was also intended to deal with the caching out issue, but eventually it turned out that caching out was ok, and not an error in the checker. I did not erase the code afterwards.

There are several possibilities now:

  • Leave this part as it is.
  • Only track symbolic regions.
  • Delete this function.

Which one do you prefer?

267 ↗(On Diff #32869)

There are several AttrKind.
Source: http://clang.llvm.org/doxygen/classclang_1_1AttributedType.html#ab4901f7a37f5539698cb5ebd706245ed

So that code is not unreachable.

298 ↗(On Diff #32869)

Sometimes when there is an expression like p[0] or p->field, the event contains the region for the element or the field region. However the tracked information is only available for p. For this reason I also check whether the super region has some tracked nullability and complain about that region in this case.

Another partial review.
Thanks!
Anna.

lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
312 ↗(On Diff #32869)

Please, explain what this method does and add a TODO comment if this needs got be improved in the future.

332 ↗(On Diff #32869)

You could cast and check "if (RetSVal.isUndef())" at the same time.

376 ↗(On Diff #32869)

Please, add a high-level comment about what this method does.

398 ↗(On Diff #32869)

Should you use the existing helper function here as well?

402 ↗(On Diff #32869)

A lot of code here looks like a copy and paste from the "checkPreStmt(const ReturnStmt *S," above. Please, factor into a subroutine.

453 ↗(On Diff #32869)

I do not understand this comment. You are setting the nullability below..

463 ↗(On Diff #32869)

Is calling a function/method with multiple arguments with set nullability tested?

Looks like testArgumentTracking could be used for this but it is never called.

475 ↗(On Diff #32869)

Use a subroutine.

496 ↗(On Diff #32869)

The next 11 lines could be factored out. They are the same as in the method above.

513 ↗(On Diff #32869)

Please, explain why we are ignoring these.
EX: Users might know that objectForKey and objectForKeyedSubscript of NSDictionary always return a non-null value. It's dynamic invariant that the analyzer cannot infer.

Why are we suppressing all methods on NSArray?

524 ↗(On Diff #32869)

Again, explain why we have this heuristic.

540 ↗(On Diff #32869)

Maybe getting nullability of the receiver could be split into a separate function to simplify readability?

542 ↗(On Diff #32869)

+comment
// If the receiver is super, assume it's nonnull.

545 ↗(On Diff #32869)

+comment
// Otherwise, look up the nullability info in the state.

558 ↗(On Diff #32869)

+ comment:
// If we know that the receiver is constrained to not be null, assume it's nullability is Nonnull, regardless of what the type is.

(Could this actually disagree with what's in the state?)

566 ↗(On Diff #32869)

TrackedNullability -> NullabilityOfReturn?

Where is the nullability of the method declaration is bound / stored into the nullability of the return value? (I don't see it happening in the preCall..) I might be missing something. Is this for inlined cases?

569 ↗(On Diff #32869)

+ comment

629 ↗(On Diff #32869)

Could all this code be factored into shouldTrackRegion helper function?
auto RegionSVal = ExprSVal.getAs<loc::MemRegionVal>();

if (!RegionSVal)
  return;

const MemRegion *Region = RegionSVal->getRegion();
if (!shouldTrackRegion(Region, C.getCurrentAnalysisDeclContext()))
  return;

Gabor, there is a lot of the same steps that all callbacks go through. I think refactoring those into subroutines will help with readability of the checker.

Looking forward to seeing a new version soon!
Anna.

lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
607 ↗(On Diff #32869)

+ comment

// We trust the explicit casts. If there is a disagreement in nullability annotations of destination and source or if '0' is casted to a nonnull type, track the value as having contradictory nullability. This will allow users to suppress nullability warnings.

618 ↗(On Diff #32869)

What is users try to suppress a FP using a cast to unspecified?

671 ↗(On Diff #32869)

We know that V is not undef here?

676 ↗(On Diff #32869)

This comment is not clear.

xazax.hun updated this revision to Diff 33151.Aug 25 2015, 4:18 PM
xazax.hun edited edge metadata.
xazax.hun marked 30 inline comments as done.

Addressed the comments.
Added some more tests.
Added a design document.

xazax.hun updated this revision to Diff 33239.Aug 26 2015, 1:31 PM
  • Fine tuned the heuristics for cocoa APIs.
  • Only track "nullable" and "contradicted" nullability for symbols. Use the rest statically.
  • Cleanups.
zaks.anna accepted this revision.Aug 26 2015, 3:42 PM
zaks.anna edited edge metadata.

Looks good overall. I might have additional post-commit comments.

This revision is now accepted and ready to land.Aug 26 2015, 3:42 PM
This revision was automatically updated to reflect the committed changes.