1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
28 | |
29 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
30 | #include "clang/StaticAnalyzer/Core/Checker.h" |
31 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
32 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" |
33 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
34 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
35 | |
36 | #include "llvm/ADT/StringExtras.h" |
37 | #include "llvm/Support/Path.h" |
38 | |
39 | using namespace clang; |
40 | using namespace ento; |
41 | |
42 | namespace { |
43 | |
44 | |
45 | |
46 | |
47 | |
48 | |
49 | Nullability getMostNullable(Nullability Lhs, Nullability Rhs) { |
50 | return static_cast<Nullability>( |
51 | std::min(static_cast<char>(Lhs), static_cast<char>(Rhs))); |
52 | } |
53 | |
54 | const char *getNullabilityString(Nullability Nullab) { |
55 | switch (Nullab) { |
56 | case Nullability::Contradicted: |
57 | return "contradicted"; |
58 | case Nullability::Nullable: |
59 | return "nullable"; |
60 | case Nullability::Unspecified: |
61 | return "unspecified"; |
62 | case Nullability::Nonnull: |
63 | return "nonnull"; |
64 | } |
65 | llvm_unreachable("Unexpected enumeration."); |
66 | return ""; |
67 | } |
68 | |
69 | |
70 | enum class ErrorKind : int { |
71 | NilAssignedToNonnull, |
72 | NilPassedToNonnull, |
73 | NilReturnedToNonnull, |
74 | NullableAssignedToNonnull, |
75 | NullableReturnedToNonnull, |
76 | NullableDereferenced, |
77 | NullablePassedToNonnull |
78 | }; |
79 | |
80 | class NullabilityChecker |
81 | : public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>, |
82 | check::PostCall, check::PostStmt<ExplicitCastExpr>, |
83 | check::PostObjCMessage, check::DeadSymbols, |
84 | check::Event<ImplicitNullDerefEvent>> { |
85 | mutable std::unique_ptr<BugType> BT; |
86 | |
87 | public: |
88 | |
89 | |
90 | |
91 | |
92 | |
93 | |
94 | DefaultBool ; |
95 | |
96 | void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; |
97 | void checkPostStmt(const ExplicitCastExpr *CE, CheckerContext &C) const; |
98 | void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; |
99 | void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; |
100 | void checkPostCall(const CallEvent &Call, CheckerContext &C) const; |
101 | void checkPreCall(const CallEvent &Call, CheckerContext &C) const; |
102 | void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; |
103 | void checkEvent(ImplicitNullDerefEvent Event) const; |
104 | |
105 | void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, |
106 | const char *Sep) const override; |
107 | |
108 | struct NullabilityChecksFilter { |
109 | DefaultBool CheckNullPassedToNonnull; |
110 | DefaultBool CheckNullReturnedFromNonnull; |
111 | DefaultBool CheckNullableDereferenced; |
112 | DefaultBool CheckNullablePassedToNonnull; |
113 | DefaultBool CheckNullableReturnedFromNonnull; |
114 | |
115 | CheckName ; |
116 | CheckName ; |
117 | CheckName ; |
118 | CheckName ; |
119 | CheckName ; |
120 | }; |
121 | |
122 | NullabilityChecksFilter Filter; |
123 | |
124 | |
125 | |
126 | |
127 | DefaultBool NeedTracking; |
128 | |
129 | private: |
130 | class NullabilityBugVisitor : public BugReporterVisitor { |
131 | public: |
132 | NullabilityBugVisitor(const MemRegion *M) : Region(M) {} |
133 | |
134 | void Profile(llvm::FoldingSetNodeID &ID) const override { |
135 | static int X = 0; |
136 | ID.AddPointer(&X); |
137 | ID.AddPointer(Region); |
138 | } |
139 | |
140 | std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, |
141 | BugReporterContext &BRC, |
142 | BugReport &BR) override; |
143 | |
144 | private: |
145 | |
146 | const MemRegion *Region; |
147 | }; |
148 | |
149 | |
150 | |
151 | |
152 | |
153 | |
154 | void reportBugIfInvariantHolds(StringRef Msg, ErrorKind Error, |
155 | ExplodedNode *N, const MemRegion *Region, |
156 | CheckerContext &C, |
157 | const Stmt *ValueExpr = nullptr, |
158 | bool SuppressPath = false) const; |
159 | |
160 | void reportBug(StringRef Msg, ErrorKind Error, ExplodedNode *N, |
161 | const MemRegion *Region, BugReporter &BR, |
162 | const Stmt *ValueExpr = nullptr) const { |
163 | if (!BT) |
164 | BT.reset(new BugType(this, "Nullability", categories::MemoryError)); |
165 | |
166 | auto R = llvm::make_unique<BugReport>(*BT, Msg, N); |
167 | if (Region) { |
168 | R->markInteresting(Region); |
169 | R->addVisitor(llvm::make_unique<NullabilityBugVisitor>(Region)); |
170 | } |
171 | if (ValueExpr) { |
172 | R->addRange(ValueExpr->getSourceRange()); |
173 | if (Error == ErrorKind::NilAssignedToNonnull || |
174 | Error == ErrorKind::NilPassedToNonnull || |
175 | Error == ErrorKind::NilReturnedToNonnull) |
176 | if (const auto *Ex = dyn_cast<Expr>(ValueExpr)) |
177 | bugreporter::trackExpressionValue(N, Ex, *R); |
178 | } |
179 | BR.emitReport(std::move(R)); |
180 | } |
181 | |
182 | |
183 | |
184 | const SymbolicRegion *getTrackRegion(SVal Val, |
185 | bool CheckSuperRegion = false) const; |
186 | |
187 | |
188 | |
189 | bool isDiagnosableCall(const CallEvent &Call) const { |
190 | if (NoDiagnoseCallsToSystemHeaders && Call.isInSystemHeader()) |
191 | return false; |
192 | |
193 | return true; |
194 | } |
195 | }; |
196 | |
197 | class NullabilityState { |
198 | public: |
199 | NullabilityState(Nullability Nullab, const Stmt *Source = nullptr) |
200 | : Nullab(Nullab), Source(Source) {} |
201 | |
202 | const Stmt *getNullabilitySource() const { return Source; } |
203 | |
204 | Nullability getValue() const { return Nullab; } |
205 | |
206 | void Profile(llvm::FoldingSetNodeID &ID) const { |
207 | ID.AddInteger(static_cast<char>(Nullab)); |
208 | ID.AddPointer(Source); |
209 | } |
210 | |
211 | void print(raw_ostream &Out) const { |
212 | Out << getNullabilityString(Nullab) << "\n"; |
213 | } |
214 | |
215 | private: |
216 | Nullability Nullab; |
217 | |
218 | |
219 | |
220 | |
221 | const Stmt *Source; |
222 | }; |
223 | |
224 | bool operator==(NullabilityState Lhs, NullabilityState Rhs) { |
225 | return Lhs.getValue() == Rhs.getValue() && |
226 | Lhs.getNullabilitySource() == Rhs.getNullabilitySource(); |
227 | } |
228 | |
229 | } |
230 | |
231 | REGISTER_MAP_WITH_PROGRAMSTATE(NullabilityMap, const MemRegion *, |
232 | NullabilityState) |
233 | |
234 | |
235 | |
236 | |
237 | |
238 | |
239 | |
240 | |
241 | |
242 | |
243 | |
244 | |
245 | |
246 | |
247 | |
248 | |
249 | |
250 | |
251 | |
252 | |
253 | |
254 | |
255 | |
256 | |
257 | |
258 | REGISTER_TRAIT_WITH_PROGRAMSTATE(InvariantViolated, bool) |
259 | |
260 | enum class NullConstraint { IsNull, IsNotNull, Unknown }; |
261 | |
262 | static NullConstraint getNullConstraint(DefinedOrUnknownSVal Val, |
263 | ProgramStateRef State) { |
264 | ConditionTruthVal Nullness = State->isNull(Val); |
265 | if (Nullness.isConstrainedFalse()) |
266 | return NullConstraint::IsNotNull; |
267 | if (Nullness.isConstrainedTrue()) |
268 | return NullConstraint::IsNull; |
269 | return NullConstraint::Unknown; |
270 | } |
271 | |
272 | const SymbolicRegion * |
273 | NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const { |
274 | if (!NeedTracking) |
275 | return nullptr; |
276 | |
277 | auto RegionSVal = Val.getAs<loc::MemRegionVal>(); |
278 | if (!RegionSVal) |
279 | return nullptr; |
280 | |
281 | const MemRegion *Region = RegionSVal->getRegion(); |
282 | |
283 | if (CheckSuperRegion) { |
284 | if (auto FieldReg = Region->getAs<FieldRegion>()) |
285 | return dyn_cast<SymbolicRegion>(FieldReg->getSuperRegion()); |
286 | if (auto ElementReg = Region->getAs<ElementRegion>()) |
287 | return dyn_cast<SymbolicRegion>(ElementReg->getSuperRegion()); |
288 | } |
289 | |
290 | return dyn_cast<SymbolicRegion>(Region); |
291 | } |
292 | |
293 | std::shared_ptr<PathDiagnosticPiece> |
294 | NullabilityChecker::NullabilityBugVisitor::VisitNode(const ExplodedNode *N, |
295 | BugReporterContext &BRC, |
296 | BugReport &BR) { |
297 | ProgramStateRef State = N->getState(); |
298 | ProgramStateRef StatePrev = N->getFirstPred()->getState(); |
299 | |
300 | const NullabilityState *TrackedNullab = State->get<NullabilityMap>(Region); |
301 | const NullabilityState *TrackedNullabPrev = |
302 | StatePrev->get<NullabilityMap>(Region); |
303 | if (!TrackedNullab) |
304 | return nullptr; |
305 | |
306 | if (TrackedNullabPrev && |
307 | TrackedNullabPrev->getValue() == TrackedNullab->getValue()) |
308 | return nullptr; |
309 | |
310 | |
311 | const Stmt *S = TrackedNullab->getNullabilitySource(); |
312 | if (!S || S->getBeginLoc().isInvalid()) { |
313 | S = PathDiagnosticLocation::getStmt(N); |
314 | } |
315 | |
316 | if (!S) |
317 | return nullptr; |
318 | |
319 | std::string InfoText = |
320 | (llvm::Twine("Nullability '") + |
321 | getNullabilityString(TrackedNullab->getValue()) + "' is inferred") |
322 | .str(); |
323 | |
324 | |
325 | PathDiagnosticLocation Pos(S, BRC.getSourceManager(), |
326 | N->getLocationContext()); |
327 | return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true, |
328 | nullptr); |
329 | } |
330 | |
331 | |
332 | |
333 | static bool checkValueAtLValForInvariantViolation(ProgramStateRef State, |
334 | SVal LV, QualType T) { |
335 | if (getNullabilityAnnotation(T) != Nullability::Nonnull) |
336 | return false; |
337 | |
338 | auto RegionVal = LV.getAs<loc::MemRegionVal>(); |
339 | if (!RegionVal) |
340 | return false; |
341 | |
342 | |
343 | |
344 | |
345 | |
346 | |
347 | |
348 | auto StoredVal = State->getSVal(*RegionVal).getAs<loc::MemRegionVal>(); |
349 | if (!StoredVal || !isa<SymbolicRegion>(StoredVal->getRegion())) |
350 | return false; |
351 | |
352 | if (getNullConstraint(*StoredVal, State) == NullConstraint::IsNull) |
353 | return true; |
354 | |
355 | return false; |
356 | } |
357 | |
358 | static bool |
359 | checkParamsForPreconditionViolation(ArrayRef<ParmVarDecl *> Params, |
360 | ProgramStateRef State, |
361 | const LocationContext *LocCtxt) { |
362 | for (const auto *ParamDecl : Params) { |
363 | if (ParamDecl->isParameterPack()) |
364 | break; |
365 | |
366 | SVal LV = State->getLValue(ParamDecl, LocCtxt); |
367 | if (checkValueAtLValForInvariantViolation(State, LV, |
368 | ParamDecl->getType())) { |
369 | return true; |
370 | } |
371 | } |
372 | return false; |
373 | } |
374 | |
375 | static bool |
376 | checkSelfIvarsForInvariantViolation(ProgramStateRef State, |
377 | const LocationContext *LocCtxt) { |
378 | auto *MD = dyn_cast<ObjCMethodDecl>(LocCtxt->getDecl()); |
379 | if (!MD || !MD->isInstanceMethod()) |
380 | return false; |
381 | |
382 | const ImplicitParamDecl *SelfDecl = LocCtxt->getSelfDecl(); |
383 | if (!SelfDecl) |
384 | return false; |
385 | |
386 | SVal SelfVal = State->getSVal(State->getRegion(SelfDecl, LocCtxt)); |
387 | |
388 | const ObjCObjectPointerType *SelfType = |
389 | dyn_cast<ObjCObjectPointerType>(SelfDecl->getType()); |
390 | if (!SelfType) |
391 | return false; |
392 | |
393 | const ObjCInterfaceDecl *ID = SelfType->getInterfaceDecl(); |
394 | if (!ID) |
395 | return false; |
396 | |
397 | for (const auto *IvarDecl : ID->ivars()) { |
398 | SVal LV = State->getLValue(IvarDecl, SelfVal); |
399 | if (checkValueAtLValForInvariantViolation(State, LV, IvarDecl->getType())) { |
400 | return true; |
401 | } |
402 | } |
403 | return false; |
404 | } |
405 | |
406 | static bool checkInvariantViolation(ProgramStateRef State, ExplodedNode *N, |
407 | CheckerContext &C) { |
408 | if (State->get<InvariantViolated>()) |
409 | return true; |
410 | |
411 | const LocationContext *LocCtxt = C.getLocationContext(); |
412 | const Decl *D = LocCtxt->getDecl(); |
413 | if (!D) |
414 | return false; |
415 | |
416 | ArrayRef<ParmVarDecl*> Params; |
417 | if (const auto *BD = dyn_cast<BlockDecl>(D)) |
418 | Params = BD->parameters(); |
419 | else if (const auto *FD = dyn_cast<FunctionDecl>(D)) |
420 | Params = FD->parameters(); |
421 | else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) |
422 | Params = MD->parameters(); |
423 | else |
424 | return false; |
425 | |
426 | if (checkParamsForPreconditionViolation(Params, State, LocCtxt) || |
427 | checkSelfIvarsForInvariantViolation(State, LocCtxt)) { |
428 | if (!N->isSink()) |
429 | C.addTransition(State->set<InvariantViolated>(true), N); |
430 | return true; |
431 | } |
432 | return false; |
433 | } |
434 | |
435 | void NullabilityChecker::reportBugIfInvariantHolds(StringRef Msg, |
436 | ErrorKind Error, ExplodedNode *N, const MemRegion *Region, |
437 | CheckerContext &C, const Stmt *ValueExpr, bool SuppressPath) const { |
438 | ProgramStateRef OriginalState = N->getState(); |
439 | |
440 | if (checkInvariantViolation(OriginalState, N, C)) |
441 | return; |
442 | if (SuppressPath) { |
443 | OriginalState = OriginalState->set<InvariantViolated>(true); |
444 | N = C.addTransition(OriginalState, N); |
445 | } |
446 | |
447 | reportBug(Msg, Error, N, Region, C.getBugReporter(), ValueExpr); |
448 | } |
449 | |
450 | |
451 | void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR, |
452 | CheckerContext &C) const { |
453 | ProgramStateRef State = C.getState(); |
454 | NullabilityMapTy Nullabilities = State->get<NullabilityMap>(); |
455 | for (NullabilityMapTy::iterator I = Nullabilities.begin(), |
456 | E = Nullabilities.end(); |
457 | I != E; ++I) { |
458 | const auto *Region = I->first->getAs<SymbolicRegion>(); |
459 | (0) . __assert_fail ("Region && \"Non-symbolic region is tracked.\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp", 459, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(Region && "Non-symbolic region is tracked."); |
460 | if (SR.isDead(Region->getSymbol())) { |
461 | State = State->remove<NullabilityMap>(I->first); |
462 | } |
463 | } |
464 | |
465 | |
466 | |
467 | |
468 | if (checkInvariantViolation(State, C.getPredecessor(), C)) |
469 | return; |
470 | C.addTransition(State); |
471 | } |
472 | |
473 | |
474 | |
475 | |
476 | void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const { |
477 | if (Event.SinkNode->getState()->get<InvariantViolated>()) |
478 | return; |
479 | |
480 | const MemRegion *Region = |
481 | getTrackRegion(Event.Location, ); |
482 | if (!Region) |
483 | return; |
484 | |
485 | ProgramStateRef State = Event.SinkNode->getState(); |
486 | const NullabilityState *TrackedNullability = |
487 | State->get<NullabilityMap>(Region); |
488 | |
489 | if (!TrackedNullability) |
490 | return; |
491 | |
492 | if (Filter.CheckNullableDereferenced && |
493 | TrackedNullability->getValue() == Nullability::Nullable) { |
494 | BugReporter &BR = *Event.BR; |
495 | |
496 | |
497 | if (Event.IsDirectDereference) |
498 | reportBug("Nullable pointer is dereferenced", |
499 | ErrorKind::NullableDereferenced, Event.SinkNode, Region, BR); |
500 | else { |
501 | reportBug("Nullable pointer is passed to a callee that requires a " |
502 | "non-null", ErrorKind::NullablePassedToNonnull, |
503 | Event.SinkNode, Region, BR); |
504 | } |
505 | } |
506 | } |
507 | |
508 | |
509 | |
510 | |
511 | |
512 | static const Expr *lookThroughImplicitCasts(const Expr *E) { |
513 | assert(E); |
514 | |
515 | while (auto *ICE = dyn_cast<ImplicitCastExpr>(E)) { |
516 | E = ICE->getSubExpr(); |
517 | } |
518 | |
519 | return E; |
520 | } |
521 | |
522 | |
523 | |
524 | void NullabilityChecker::checkPreStmt(const ReturnStmt *S, |
525 | CheckerContext &C) const { |
526 | auto RetExpr = S->getRetValue(); |
527 | if (!RetExpr) |
528 | return; |
529 | |
530 | if (!RetExpr->getType()->isAnyPointerType()) |
531 | return; |
532 | |
533 | ProgramStateRef State = C.getState(); |
534 | if (State->get<InvariantViolated>()) |
535 | return; |
536 | |
537 | auto RetSVal = C.getSVal(S).getAs<DefinedOrUnknownSVal>(); |
538 | if (!RetSVal) |
539 | return; |
540 | |
541 | bool InSuppressedMethodFamily = false; |
542 | |
543 | QualType RequiredRetType; |
544 | AnalysisDeclContext *DeclCtxt = |
545 | C.getLocationContext()->getAnalysisDeclContext(); |
546 | const Decl *D = DeclCtxt->getDecl(); |
547 | if (auto *MD = dyn_cast<ObjCMethodDecl>(D)) { |
548 | |
549 | |
550 | |
551 | |
552 | ObjCMethodFamily Family = MD->getMethodFamily(); |
553 | if (OMF_init == Family || OMF_copy == Family || OMF_mutableCopy == Family) |
554 | InSuppressedMethodFamily = true; |
555 | |
556 | RequiredRetType = MD->getReturnType(); |
557 | } else if (auto *FD = dyn_cast<FunctionDecl>(D)) { |
558 | RequiredRetType = FD->getReturnType(); |
559 | } else { |
560 | return; |
561 | } |
562 | |
563 | NullConstraint Nullness = getNullConstraint(*RetSVal, State); |
564 | |
565 | Nullability RequiredNullability = getNullabilityAnnotation(RequiredRetType); |
566 | |
567 | |
568 | |
569 | |
570 | |
571 | |
572 | Nullability RetExprTypeLevelNullability = |
573 | getNullabilityAnnotation(lookThroughImplicitCasts(RetExpr)->getType()); |
574 | |
575 | bool NullReturnedFromNonNull = (RequiredNullability == Nullability::Nonnull && |
576 | Nullness == NullConstraint::IsNull); |
577 | if (Filter.CheckNullReturnedFromNonnull && |
578 | NullReturnedFromNonNull && |
579 | RetExprTypeLevelNullability != Nullability::Nonnull && |
580 | !InSuppressedMethodFamily && |
581 | C.getLocationContext()->inTopFrame()) { |
582 | static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull"); |
583 | ExplodedNode *N = C.generateErrorNode(State, &Tag); |
584 | if (!N) |
585 | return; |
586 | |
587 | SmallString<256> SBuf; |
588 | llvm::raw_svector_ostream OS(SBuf); |
589 | OS << (RetExpr->getType()->isObjCObjectPointerType() ? "nil" : "Null"); |
590 | OS << " returned from a " << C.getDeclDescription(D) << |
591 | " that is expected to return a non-null value"; |
592 | reportBugIfInvariantHolds(OS.str(), |
593 | ErrorKind::NilReturnedToNonnull, N, nullptr, C, |
594 | RetExpr); |
595 | return; |
596 | } |
597 | |
598 | |
599 | |
600 | if (NullReturnedFromNonNull) { |
601 | State = State->set<InvariantViolated>(true); |
602 | C.addTransition(State); |
603 | return; |
604 | } |
605 | |
606 | const MemRegion *Region = getTrackRegion(*RetSVal); |
607 | if (!Region) |
608 | return; |
609 | |
610 | const NullabilityState *TrackedNullability = |
611 | State->get<NullabilityMap>(Region); |
612 | if (TrackedNullability) { |
613 | Nullability TrackedNullabValue = TrackedNullability->getValue(); |
614 | if (Filter.CheckNullableReturnedFromNonnull && |
615 | Nullness != NullConstraint::IsNotNull && |
616 | TrackedNullabValue == Nullability::Nullable && |
617 | RequiredNullability == Nullability::Nonnull) { |
618 | static CheckerProgramPointTag Tag(this, "NullableReturnedFromNonnull"); |
619 | ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag); |
620 | |
621 | SmallString<256> SBuf; |
622 | llvm::raw_svector_ostream OS(SBuf); |
623 | OS << "Nullable pointer is returned from a " << C.getDeclDescription(D) << |
624 | " that is expected to return a non-null value"; |
625 | |
626 | reportBugIfInvariantHolds(OS.str(), |
627 | ErrorKind::NullableReturnedToNonnull, N, |
628 | Region, C); |
629 | } |
630 | return; |
631 | } |
632 | if (RequiredNullability == Nullability::Nullable) { |
633 | State = State->set<NullabilityMap>(Region, |
634 | NullabilityState(RequiredNullability, |
635 | S)); |
636 | C.addTransition(State); |
637 | } |
638 | } |
639 | |
640 | |
641 | |
642 | void NullabilityChecker::checkPreCall(const CallEvent &Call, |
643 | CheckerContext &C) const { |
644 | if (!Call.getDecl()) |
645 | return; |
646 | |
647 | ProgramStateRef State = C.getState(); |
648 | if (State->get<InvariantViolated>()) |
649 | return; |
650 | |
651 | ProgramStateRef OrigState = State; |
652 | |
653 | unsigned Idx = 0; |
654 | for (const ParmVarDecl *Param : Call.parameters()) { |
655 | if (Param->isParameterPack()) |
656 | break; |
657 | |
658 | if (Idx >= Call.getNumArgs()) |
659 | break; |
660 | |
661 | const Expr *ArgExpr = Call.getArgExpr(Idx); |
662 | auto ArgSVal = Call.getArgSVal(Idx++).getAs<DefinedOrUnknownSVal>(); |
663 | if (!ArgSVal) |
664 | continue; |
665 | |
666 | if (!Param->getType()->isAnyPointerType() && |
667 | !Param->getType()->isReferenceType()) |
668 | continue; |
669 | |
670 | NullConstraint Nullness = getNullConstraint(*ArgSVal, State); |
671 | |
672 | Nullability RequiredNullability = |
673 | getNullabilityAnnotation(Param->getType()); |
674 | Nullability ArgExprTypeLevelNullability = |
675 | getNullabilityAnnotation(ArgExpr->getType()); |
676 | |
677 | unsigned ParamIdx = Param->getFunctionScopeIndex() + 1; |
678 | |
679 | if (Filter.CheckNullPassedToNonnull && Nullness == NullConstraint::IsNull && |
680 | ArgExprTypeLevelNullability != Nullability::Nonnull && |
681 | RequiredNullability == Nullability::Nonnull && |
682 | isDiagnosableCall(Call)) { |
683 | ExplodedNode *N = C.generateErrorNode(State); |
684 | if (!N) |
685 | return; |
686 | |
687 | SmallString<256> SBuf; |
688 | llvm::raw_svector_ostream OS(SBuf); |
689 | OS << (Param->getType()->isObjCObjectPointerType() ? "nil" : "Null"); |
690 | OS << " passed to a callee that requires a non-null " << ParamIdx |
691 | << llvm::getOrdinalSuffix(ParamIdx) << " parameter"; |
692 | reportBugIfInvariantHolds(OS.str(), ErrorKind::NilPassedToNonnull, N, |
693 | nullptr, C, |
694 | ArgExpr, ); |
695 | return; |
696 | } |
697 | |
698 | const MemRegion *Region = getTrackRegion(*ArgSVal); |
699 | if (!Region) |
700 | continue; |
701 | |
702 | const NullabilityState *TrackedNullability = |
703 | State->get<NullabilityMap>(Region); |
704 | |
705 | if (TrackedNullability) { |
706 | if (Nullness == NullConstraint::IsNotNull || |
707 | TrackedNullability->getValue() != Nullability::Nullable) |
708 | continue; |
709 | |
710 | if (Filter.CheckNullablePassedToNonnull && |
711 | RequiredNullability == Nullability::Nonnull && |
712 | isDiagnosableCall(Call)) { |
713 | ExplodedNode *N = C.addTransition(State); |
714 | SmallString<256> SBuf; |
715 | llvm::raw_svector_ostream OS(SBuf); |
716 | OS << "Nullable pointer is passed to a callee that requires a non-null " |
717 | << ParamIdx << llvm::getOrdinalSuffix(ParamIdx) << " parameter"; |
718 | reportBugIfInvariantHolds(OS.str(), |
719 | ErrorKind::NullablePassedToNonnull, N, |
720 | Region, C, ArgExpr, ); |
721 | return; |
722 | } |
723 | if (Filter.CheckNullableDereferenced && |
724 | Param->getType()->isReferenceType()) { |
725 | ExplodedNode *N = C.addTransition(State); |
726 | reportBugIfInvariantHolds("Nullable pointer is dereferenced", |
727 | ErrorKind::NullableDereferenced, N, Region, |
728 | C, ArgExpr, ); |
729 | return; |
730 | } |
731 | continue; |
732 | } |
733 | |
734 | if (ArgExprTypeLevelNullability != Nullability::Nullable) |
735 | continue; |
736 | State = State->set<NullabilityMap>( |
737 | Region, NullabilityState(ArgExprTypeLevelNullability, ArgExpr)); |
738 | } |
739 | if (State != OrigState) |
740 | C.addTransition(State); |
741 | } |
742 | |
743 | |
744 | void NullabilityChecker::checkPostCall(const CallEvent &Call, |
745 | CheckerContext &C) const { |
746 | auto Decl = Call.getDecl(); |
747 | if (!Decl) |
748 | return; |
749 | |
750 | if (Call.getKind() == CE_ObjCMessage) |
751 | return; |
752 | const FunctionType *FuncType = Decl->getFunctionType(); |
753 | if (!FuncType) |
754 | return; |
755 | QualType ReturnType = FuncType->getReturnType(); |
756 | if (!ReturnType->isAnyPointerType()) |
757 | return; |
758 | ProgramStateRef State = C.getState(); |
759 | if (State->get<InvariantViolated>()) |
760 | return; |
761 | |
762 | const MemRegion *Region = getTrackRegion(Call.getReturnValue()); |
763 | if (!Region) |
764 | return; |
765 | |
766 | |
767 | |
768 | const SourceManager &SM = C.getSourceManager(); |
769 | StringRef FilePath = SM.getFilename(SM.getSpellingLoc(Decl->getBeginLoc())); |
770 | if (llvm::sys::path::filename(FilePath).startswith("CG")) { |
771 | State = State->set<NullabilityMap>(Region, Nullability::Contradicted); |
772 | C.addTransition(State); |
773 | return; |
774 | } |
775 | |
776 | const NullabilityState *TrackedNullability = |
777 | State->get<NullabilityMap>(Region); |
778 | |
779 | if (!TrackedNullability && |
780 | getNullabilityAnnotation(ReturnType) == Nullability::Nullable) { |
781 | State = State->set<NullabilityMap>(Region, Nullability::Nullable); |
782 | C.addTransition(State); |
783 | } |
784 | } |
785 | |
786 | static Nullability getReceiverNullability(const ObjCMethodCall &M, |
787 | ProgramStateRef State) { |
788 | if (M.isReceiverSelfOrSuper()) { |
789 | |
790 | |
791 | return Nullability::Nonnull; |
792 | } |
793 | |
794 | SVal Receiver = M.getReceiverSVal(); |
795 | if (auto DefOrUnknown = Receiver.getAs<DefinedOrUnknownSVal>()) { |
796 | |
797 | |
798 | NullConstraint Nullness = getNullConstraint(*DefOrUnknown, State); |
799 | if (Nullness == NullConstraint::IsNotNull) |
800 | return Nullability::Nonnull; |
801 | } |
802 | auto ValueRegionSVal = Receiver.getAs<loc::MemRegionVal>(); |
803 | if (ValueRegionSVal) { |
804 | const MemRegion *SelfRegion = ValueRegionSVal->getRegion(); |
805 | assert(SelfRegion); |
806 | |
807 | const NullabilityState *TrackedSelfNullability = |
808 | State->get<NullabilityMap>(SelfRegion); |
809 | if (TrackedSelfNullability) |
810 | return TrackedSelfNullability->getValue(); |
811 | } |
812 | return Nullability::Unspecified; |
813 | } |
814 | |
815 | |
816 | |
817 | |
818 | void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M, |
819 | CheckerContext &C) const { |
820 | auto Decl = M.getDecl(); |
821 | if (!Decl) |
822 | return; |
823 | QualType RetType = Decl->getReturnType(); |
824 | if (!RetType->isAnyPointerType()) |
825 | return; |
826 | |
827 | ProgramStateRef State = C.getState(); |
828 | if (State->get<InvariantViolated>()) |
829 | return; |
830 | |
831 | const MemRegion *ReturnRegion = getTrackRegion(M.getReturnValue()); |
832 | if (!ReturnRegion) |
833 | return; |
834 | |
835 | auto Interface = Decl->getClassInterface(); |
836 | auto Name = Interface ? Interface->getName() : ""; |
837 | |
838 | |
839 | |
840 | if (Name.startswith("NS")) { |
841 | |
842 | |
843 | |
844 | |
845 | |
846 | |
847 | |
848 | |
849 | if (M.isInstanceMessage() && Name.contains("Dictionary")) { |
850 | State = |
851 | State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted); |
852 | C.addTransition(State); |
853 | return; |
854 | } |
855 | |
856 | StringRef FirstSelectorSlot = M.getSelector().getNameForSlot(0); |
857 | if (Name.contains("Array") && |
858 | (FirstSelectorSlot == "firstObject" || |
859 | FirstSelectorSlot == "lastObject")) { |
860 | State = |
861 | State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted); |
862 | C.addTransition(State); |
863 | return; |
864 | } |
865 | |
866 | |
867 | |
868 | |
869 | |
870 | if (Name.contains("String")) { |
871 | for (auto Param : M.parameters()) { |
872 | if (Param->getName() == "encoding") { |
873 | State = State->set<NullabilityMap>(ReturnRegion, |
874 | Nullability::Contradicted); |
875 | C.addTransition(State); |
876 | return; |
877 | } |
878 | } |
879 | } |
880 | } |
881 | |
882 | const ObjCMessageExpr *Message = M.getOriginExpr(); |
883 | Nullability SelfNullability = getReceiverNullability(M, State); |
884 | |
885 | const NullabilityState *NullabilityOfReturn = |
886 | State->get<NullabilityMap>(ReturnRegion); |
887 | |
888 | if (NullabilityOfReturn) { |
889 | |
890 | |
891 | |
892 | Nullability RetValTracked = NullabilityOfReturn->getValue(); |
893 | Nullability ComputedNullab = |
894 | getMostNullable(RetValTracked, SelfNullability); |
895 | if (ComputedNullab != RetValTracked && |
896 | ComputedNullab != Nullability::Unspecified) { |
897 | const Stmt *NullabilitySource = |
898 | ComputedNullab == RetValTracked |
899 | ? NullabilityOfReturn->getNullabilitySource() |
900 | : Message->getInstanceReceiver(); |
901 | State = State->set<NullabilityMap>( |
902 | ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource)); |
903 | C.addTransition(State); |
904 | } |
905 | return; |
906 | } |
907 | |
908 | |
909 | Nullability RetNullability = getNullabilityAnnotation(RetType); |
910 | |
911 | |
912 | |
913 | |
914 | |
915 | if (M.getMessageKind() == OCM_PropertyAccess && !C.wasInlined) |
916 | RetNullability = Nullability::Nonnull; |
917 | |
918 | Nullability ComputedNullab = getMostNullable(RetNullability, SelfNullability); |
919 | if (ComputedNullab == Nullability::Nullable) { |
920 | const Stmt *NullabilitySource = ComputedNullab == RetNullability |
921 | ? Message |
922 | : Message->getInstanceReceiver(); |
923 | State = State->set<NullabilityMap>( |
924 | ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource)); |
925 | C.addTransition(State); |
926 | } |
927 | } |
928 | |
929 | |
930 | |
931 | |
932 | |
933 | void NullabilityChecker::checkPostStmt(const ExplicitCastExpr *CE, |
934 | CheckerContext &C) const { |
935 | QualType OriginType = CE->getSubExpr()->getType(); |
936 | QualType DestType = CE->getType(); |
937 | if (!OriginType->isAnyPointerType()) |
938 | return; |
939 | if (!DestType->isAnyPointerType()) |
940 | return; |
941 | |
942 | ProgramStateRef State = C.getState(); |
943 | if (State->get<InvariantViolated>()) |
944 | return; |
945 | |
946 | Nullability DestNullability = getNullabilityAnnotation(DestType); |
947 | |
948 | |
949 | |
950 | if (DestNullability == Nullability::Unspecified) |
951 | return; |
952 | |
953 | auto RegionSVal = C.getSVal(CE).getAs<DefinedOrUnknownSVal>(); |
954 | const MemRegion *Region = getTrackRegion(*RegionSVal); |
955 | if (!Region) |
956 | return; |
957 | |
958 | |
959 | if (DestNullability == Nullability::Nonnull) { |
960 | NullConstraint Nullness = getNullConstraint(*RegionSVal, State); |
961 | if (Nullness == NullConstraint::IsNull) { |
962 | State = State->set<NullabilityMap>(Region, Nullability::Contradicted); |
963 | C.addTransition(State); |
964 | return; |
965 | } |
966 | } |
967 | |
968 | const NullabilityState *TrackedNullability = |
969 | State->get<NullabilityMap>(Region); |
970 | |
971 | if (!TrackedNullability) { |
972 | if (DestNullability != Nullability::Nullable) |
973 | return; |
974 | State = State->set<NullabilityMap>(Region, |
975 | NullabilityState(DestNullability, CE)); |
976 | C.addTransition(State); |
977 | return; |
978 | } |
979 | |
980 | if (TrackedNullability->getValue() != DestNullability && |
981 | TrackedNullability->getValue() != Nullability::Contradicted) { |
982 | State = State->set<NullabilityMap>(Region, Nullability::Contradicted); |
983 | C.addTransition(State); |
984 | } |
985 | } |
986 | |
987 | |
988 | |
989 | static const Expr * matchValueExprForBind(const Stmt *S) { |
990 | |
991 | if (auto *BinOp = dyn_cast<BinaryOperator>(S)) { |
992 | if (BinOp->getOpcode() == BO_Assign) |
993 | return BinOp->getRHS(); |
994 | } |
995 | |
996 | |
997 | if (auto *DS = dyn_cast<DeclStmt>(S)) { |
998 | if (DS->isSingleDecl()) { |
999 | auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); |
1000 | if (!VD) |
1001 | return nullptr; |
1002 | |
1003 | if (const Expr *Init = VD->getInit()) |
1004 | return Init; |
1005 | } |
1006 | } |
1007 | |
1008 | return nullptr; |
1009 | } |
1010 | |
1011 | |
1012 | |
1013 | static bool isARCNilInitializedLocal(CheckerContext &C, const Stmt *S) { |
1014 | |
1015 | |
1016 | |
1017 | |
1018 | |
1019 | |
1020 | |
1021 | |
1022 | |
1023 | |
1024 | |
1025 | |
1026 | |
1027 | |
1028 | if (!C.getASTContext().getLangOpts().ObjCAutoRefCount) |
1029 | return false; |
1030 | |
1031 | auto *DS = dyn_cast<DeclStmt>(S); |
1032 | if (!DS || !DS->isSingleDecl()) |
1033 | return false; |
1034 | |
1035 | auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); |
1036 | if (!VD) |
1037 | return false; |
1038 | |
1039 | |
1040 | if(!VD->getType().getQualifiers().hasObjCLifetime()) |
1041 | return false; |
1042 | |
1043 | const Expr *Init = VD->getInit(); |
1044 | (0) . __assert_fail ("Init && \"ObjC local under ARC without initializer\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp", 1044, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(Init && "ObjC local under ARC without initializer"); |
1045 | |
1046 | |
1047 | if (!isa<ImplicitValueInitExpr>(Init)) |
1048 | return false; |
1049 | |
1050 | return true; |
1051 | } |
1052 | |
1053 | |
1054 | |
1055 | void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S, |
1056 | CheckerContext &C) const { |
1057 | const TypedValueRegion *TVR = |
1058 | dyn_cast_or_null<TypedValueRegion>(L.getAsRegion()); |
1059 | if (!TVR) |
1060 | return; |
1061 | |
1062 | QualType LocType = TVR->getValueType(); |
1063 | if (!LocType->isAnyPointerType()) |
1064 | return; |
1065 | |
1066 | ProgramStateRef State = C.getState(); |
1067 | if (State->get<InvariantViolated>()) |
1068 | return; |
1069 | |
1070 | auto ValDefOrUnknown = V.getAs<DefinedOrUnknownSVal>(); |
1071 | if (!ValDefOrUnknown) |
1072 | return; |
1073 | |
1074 | NullConstraint RhsNullness = getNullConstraint(*ValDefOrUnknown, State); |
1075 | |
1076 | Nullability ValNullability = Nullability::Unspecified; |
1077 | if (SymbolRef Sym = ValDefOrUnknown->getAsSymbol()) |
1078 | ValNullability = getNullabilityAnnotation(Sym->getType()); |
1079 | |
1080 | Nullability LocNullability = getNullabilityAnnotation(LocType); |
1081 | |
1082 | |
1083 | |
1084 | Nullability ValueExprTypeLevelNullability = Nullability::Unspecified; |
1085 | const Expr *ValueExpr = matchValueExprForBind(S); |
1086 | if (ValueExpr) { |
1087 | ValueExprTypeLevelNullability = |
1088 | getNullabilityAnnotation(lookThroughImplicitCasts(ValueExpr)->getType()); |
1089 | } |
1090 | |
1091 | bool NullAssignedToNonNull = (LocNullability == Nullability::Nonnull && |
1092 | RhsNullness == NullConstraint::IsNull); |
1093 | if (Filter.CheckNullPassedToNonnull && |
1094 | NullAssignedToNonNull && |
1095 | ValNullability != Nullability::Nonnull && |
1096 | ValueExprTypeLevelNullability != Nullability::Nonnull && |
1097 | !isARCNilInitializedLocal(C, S)) { |
1098 | static CheckerProgramPointTag Tag(this, "NullPassedToNonnull"); |
1099 | ExplodedNode *N = C.generateErrorNode(State, &Tag); |
1100 | if (!N) |
1101 | return; |
1102 | |
1103 | |
1104 | const Stmt *ValueStmt = S; |
1105 | if (ValueExpr) |
1106 | ValueStmt = ValueExpr; |
1107 | |
1108 | SmallString<256> SBuf; |
1109 | llvm::raw_svector_ostream OS(SBuf); |
1110 | OS << (LocType->isObjCObjectPointerType() ? "nil" : "Null"); |
1111 | OS << " assigned to a pointer which is expected to have non-null value"; |
1112 | reportBugIfInvariantHolds(OS.str(), |
1113 | ErrorKind::NilAssignedToNonnull, N, nullptr, C, |
1114 | ValueStmt); |
1115 | return; |
1116 | } |
1117 | |
1118 | |
1119 | |
1120 | if (NullAssignedToNonNull) { |
1121 | State = State->set<InvariantViolated>(true); |
1122 | C.addTransition(State); |
1123 | return; |
1124 | } |
1125 | |
1126 | |
1127 | |
1128 | |
1129 | const MemRegion *ValueRegion = getTrackRegion(*ValDefOrUnknown); |
1130 | if (!ValueRegion) |
1131 | return; |
1132 | |
1133 | const NullabilityState *TrackedNullability = |
1134 | State->get<NullabilityMap>(ValueRegion); |
1135 | |
1136 | if (TrackedNullability) { |
1137 | if (RhsNullness == NullConstraint::IsNotNull || |
1138 | TrackedNullability->getValue() != Nullability::Nullable) |
1139 | return; |
1140 | if (Filter.CheckNullablePassedToNonnull && |
1141 | LocNullability == Nullability::Nonnull) { |
1142 | static CheckerProgramPointTag Tag(this, "NullablePassedToNonnull"); |
1143 | ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag); |
1144 | reportBugIfInvariantHolds("Nullable pointer is assigned to a pointer " |
1145 | "which is expected to have non-null value", |
1146 | ErrorKind::NullableAssignedToNonnull, N, |
1147 | ValueRegion, C); |
1148 | } |
1149 | return; |
1150 | } |
1151 | |
1152 | const auto *BinOp = dyn_cast<BinaryOperator>(S); |
1153 | |
1154 | if (ValNullability == Nullability::Nullable) { |
1155 | |
1156 | |
1157 | const Stmt *NullabilitySource = BinOp ? BinOp->getRHS() : S; |
1158 | State = State->set<NullabilityMap>( |
1159 | ValueRegion, NullabilityState(ValNullability, NullabilitySource)); |
1160 | C.addTransition(State); |
1161 | return; |
1162 | } |
1163 | |
1164 | if (LocNullability == Nullability::Nullable) { |
1165 | const Stmt *NullabilitySource = BinOp ? BinOp->getLHS() : S; |
1166 | State = State->set<NullabilityMap>( |
1167 | ValueRegion, NullabilityState(LocNullability, NullabilitySource)); |
1168 | C.addTransition(State); |
1169 | } |
1170 | } |
1171 | |
1172 | void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State, |
1173 | const char *NL, const char *Sep) const { |
1174 | |
1175 | NullabilityMapTy B = State->get<NullabilityMap>(); |
1176 | |
1177 | if (State->get<InvariantViolated>()) |
1178 | Out << Sep << NL |
1179 | << "Nullability invariant was violated, warnings suppressed." << NL; |
1180 | |
1181 | if (B.isEmpty()) |
1182 | return; |
1183 | |
1184 | if (!State->get<InvariantViolated>()) |
1185 | Out << Sep << NL; |
1186 | |
1187 | for (NullabilityMapTy::iterator I = B.begin(), E = B.end(); I != E; ++I) { |
1188 | Out << I->first << " : "; |
1189 | I->second.print(Out); |
1190 | Out << NL; |
1191 | } |
1192 | } |
1193 | |
1194 | void ento::registerNullabilityBase(CheckerManager &mgr) { |
1195 | mgr.registerChecker<NullabilityChecker>(); |
1196 | } |
1197 | |
1198 | bool ento::shouldRegisterNullabilityBase(const LangOptions &LO) { |
1199 | return true; |
1200 | } |
1201 | |
1202 | #define REGISTER_CHECKER(name, trackingRequired) \ |
1203 | void ento::register##name##Checker(CheckerManager &mgr) { \ |
1204 | NullabilityChecker *checker = mgr.getChecker<NullabilityChecker>(); \ |
1205 | checker->Filter.Check##name = true; \ |
1206 | checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \ |
1207 | checker->NeedTracking = checker->NeedTracking || trackingRequired; \ |
1208 | checker->NoDiagnoseCallsToSystemHeaders = \ |
1209 | checker->NoDiagnoseCallsToSystemHeaders || \ |
1210 | mgr.getAnalyzerOptions().getCheckerBooleanOption( \ |
1211 | checker, "NoDiagnoseCallsToSystemHeaders", false, true); \ |
1212 | } \ |
1213 | \ |
1214 | bool ento::shouldRegister##name##Checker(const LangOptions &LO) { \ |
1215 | return true; \ |
1216 | } |
1217 | |
1218 | |
1219 | |
1220 | |
1221 | |
1222 | REGISTER_CHECKER(NullPassedToNonnull, false) |
1223 | REGISTER_CHECKER(NullReturnedFromNonnull, false) |
1224 | |
1225 | REGISTER_CHECKER(NullableDereferenced, true) |
1226 | REGISTER_CHECKER(NullablePassedToNonnull, true) |
1227 | REGISTER_CHECKER(NullableReturnedFromNonnull, true) |
1228 | |