xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp (revision d99bd55a5e092774214ba31fc5a871bfc31e711c)
1 //=== VLASizeChecker.cpp - Undefined dereference checker --------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This defines VLASizeChecker, a builtin check in ExprEngine that
11 // performs checks for declaration of VLA of undefined or zero size.
12 // In addition, VLASizeChecker is responsible for defining the extent
13 // of the MemRegion that represents a VLA.
14 //
15 //===----------------------------------------------------------------------===//
16 
17 #include "ExprEngineInternalChecks.h"
18 #include "clang/AST/CharUnits.h"
19 #include "clang/StaticAnalyzer/BugReporter/BugType.h"
20 #include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h"
21 #include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h"
22 
23 using namespace clang;
24 using namespace ento;
25 
26 namespace {
27 class VLASizeChecker : public CheckerVisitor<VLASizeChecker> {
28   BugType *BT_zero;
29   BugType *BT_undef;
30 
31 public:
32   VLASizeChecker() : BT_zero(0), BT_undef(0) {}
33   static void *getTag() { static int tag = 0; return &tag; }
34   void PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS);
35 };
36 } // end anonymous namespace
37 
38 void ento::RegisterVLASizeChecker(ExprEngine &Eng) {
39   Eng.registerCheck(new VLASizeChecker());
40 }
41 
42 void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) {
43   if (!DS->isSingleDecl())
44     return;
45 
46   const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
47   if (!VD)
48     return;
49 
50   ASTContext &Ctx = C.getASTContext();
51   const VariableArrayType *VLA = Ctx.getAsVariableArrayType(VD->getType());
52   if (!VLA)
53     return;
54 
55   // FIXME: Handle multi-dimensional VLAs.
56   const Expr* SE = VLA->getSizeExpr();
57   const GRState *state = C.getState();
58   SVal sizeV = state->getSVal(SE);
59 
60   if (sizeV.isUndef()) {
61     // Generate an error node.
62     ExplodedNode *N = C.generateSink();
63     if (!N)
64       return;
65 
66     if (!BT_undef)
67       BT_undef = new BuiltinBug("Declared variable-length array (VLA) uses a "
68                                 "garbage value as its size");
69 
70     EnhancedBugReport *report =
71       new EnhancedBugReport(*BT_undef, BT_undef->getName(), N);
72     report->addRange(SE->getSourceRange());
73     report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE);
74     C.EmitReport(report);
75     return;
76   }
77 
78   // See if the size value is known. It can't be undefined because we would have
79   // warned about that already.
80   if (sizeV.isUnknown())
81     return;
82 
83   // Check if the size is zero.
84   DefinedSVal sizeD = cast<DefinedSVal>(sizeV);
85 
86   const GRState *stateNotZero, *stateZero;
87   llvm::tie(stateNotZero, stateZero) = state->assume(sizeD);
88 
89   if (stateZero && !stateNotZero) {
90     ExplodedNode* N = C.generateSink(stateZero);
91     if (!BT_zero)
92       BT_zero = new BuiltinBug("Declared variable-length array (VLA) has zero "
93                                "size");
94 
95     EnhancedBugReport *report =
96       new EnhancedBugReport(*BT_zero, BT_zero->getName(), N);
97     report->addRange(SE->getSourceRange());
98     report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE);
99     C.EmitReport(report);
100     return;
101   }
102 
103   // From this point on, assume that the size is not zero.
104   state = stateNotZero;
105 
106   // VLASizeChecker is responsible for defining the extent of the array being
107   // declared. We do this by multiplying the array length by the element size,
108   // then matching that with the array region's extent symbol.
109 
110   // Convert the array length to size_t.
111   SValBuilder &svalBuilder = C.getSValBuilder();
112   QualType SizeTy = Ctx.getSizeType();
113   NonLoc ArrayLength = cast<NonLoc>(svalBuilder.evalCast(sizeD, SizeTy,
114                                                          SE->getType()));
115 
116   // Get the element size.
117   CharUnits EleSize = Ctx.getTypeSizeInChars(VLA->getElementType());
118   SVal EleSizeVal = svalBuilder.makeIntVal(EleSize.getQuantity(), SizeTy);
119 
120   // Multiply the array length by the element size.
121   SVal ArraySizeVal = svalBuilder.evalBinOpNN(state, BO_Mul, ArrayLength,
122                                               cast<NonLoc>(EleSizeVal), SizeTy);
123 
124   // Finally, assume that the array's extent matches the given size.
125   const LocationContext *LC = C.getPredecessor()->getLocationContext();
126   DefinedOrUnknownSVal Extent =
127     state->getRegion(VD, LC)->getExtent(svalBuilder);
128   DefinedOrUnknownSVal ArraySize = cast<DefinedOrUnknownSVal>(ArraySizeVal);
129   DefinedOrUnknownSVal sizeIsKnown =
130     svalBuilder.evalEQ(state, Extent, ArraySize);
131   state = state->assume(sizeIsKnown, true);
132 
133   // Assume should not fail at this point.
134   assert(state);
135 
136   // Remember our assumptions!
137   C.addTransition(state);
138 }
139