1 | //== CheckerContext.cpp - Context info for path-sensitive checkers-----------=// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file defines CheckerContext that provides contextual info for |
10 | // path-sensitive checkers. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
15 | #include "clang/Basic/Builtins.h" |
16 | #include "clang/Lex/Lexer.h" |
17 | #include "llvm/ADT/StringExtras.h" |
18 | |
19 | using namespace clang; |
20 | using namespace ento; |
21 | |
22 | const FunctionDecl *CheckerContext::getCalleeDecl(const CallExpr *CE) const { |
23 | const FunctionDecl *D = CE->getDirectCallee(); |
24 | if (D) |
25 | return D; |
26 | |
27 | const Expr *Callee = CE->getCallee(); |
28 | SVal L = Pred->getSVal(S: Callee); |
29 | return L.getAsFunctionDecl(); |
30 | } |
31 | |
32 | StringRef CheckerContext::getCalleeName(const FunctionDecl *FunDecl) const { |
33 | if (!FunDecl) |
34 | return StringRef(); |
35 | IdentifierInfo *funI = FunDecl->getIdentifier(); |
36 | if (!funI) |
37 | return StringRef(); |
38 | return funI->getName(); |
39 | } |
40 | |
41 | StringRef CheckerContext::getDeclDescription(const Decl *D) { |
42 | if (isa<ObjCMethodDecl, CXXMethodDecl>(Val: D)) |
43 | return "method" ; |
44 | if (isa<BlockDecl>(Val: D)) |
45 | return "anonymous block" ; |
46 | return "function" ; |
47 | } |
48 | |
49 | bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD, |
50 | StringRef Name) { |
51 | // To avoid false positives (Ex: finding user defined functions with |
52 | // similar names), only perform fuzzy name matching when it's a builtin. |
53 | // Using a string compare is slow, we might want to switch on BuiltinID here. |
54 | unsigned BId = FD->getBuiltinID(); |
55 | if (BId != 0) { |
56 | if (Name.empty()) |
57 | return true; |
58 | StringRef BName = FD->getASTContext().BuiltinInfo.getName(ID: BId); |
59 | size_t start = BName.find(Str: Name); |
60 | if (start != StringRef::npos) { |
61 | // Accept exact match. |
62 | if (BName.size() == Name.size()) |
63 | return true; |
64 | |
65 | // v-- match starts here |
66 | // ...xxxxx... |
67 | // _xxxxx_ |
68 | // ^ ^ lookbehind and lookahead characters |
69 | |
70 | const auto MatchPredecessor = [=]() -> bool { |
71 | return start <= 0 || !llvm::isAlpha(C: BName[start - 1]); |
72 | }; |
73 | const auto MatchSuccessor = [=]() -> bool { |
74 | std::size_t LookbehindPlace = start + Name.size(); |
75 | return LookbehindPlace >= BName.size() || |
76 | !llvm::isAlpha(C: BName[LookbehindPlace]); |
77 | }; |
78 | |
79 | if (MatchPredecessor() && MatchSuccessor()) |
80 | return true; |
81 | } |
82 | } |
83 | |
84 | const IdentifierInfo *II = FD->getIdentifier(); |
85 | // If this is a special C++ name without IdentifierInfo, it can't be a |
86 | // C library function. |
87 | if (!II) |
88 | return false; |
89 | |
90 | // C library functions are either declared directly within a TU (the common |
91 | // case) or they are accessed through the namespace `std` (when they are used |
92 | // in C++ via headers like <cstdlib>). |
93 | const DeclContext *DC = FD->getDeclContext()->getRedeclContext(); |
94 | if (!(DC->isTranslationUnit() || DC->isStdNamespace())) |
95 | return false; |
96 | |
97 | // If this function is not externally visible, it is not a C library function. |
98 | // Note that we make an exception for inline functions, which may be |
99 | // declared in header files without external linkage. |
100 | if (!FD->isInlined() && !FD->isExternallyVisible()) |
101 | return false; |
102 | |
103 | if (Name.empty()) |
104 | return true; |
105 | |
106 | StringRef FName = II->getName(); |
107 | if (FName == Name) |
108 | return true; |
109 | |
110 | if (FName.starts_with(Prefix: "__inline" ) && FName.contains(Other: Name)) |
111 | return true; |
112 | |
113 | return false; |
114 | } |
115 | |
116 | bool CheckerContext::isHardenedVariantOf(const FunctionDecl *FD, |
117 | StringRef Name) { |
118 | const IdentifierInfo *II = FD->getIdentifier(); |
119 | if (!II) |
120 | return false; |
121 | |
122 | auto CompletelyMatchesParts = [II](auto... Parts) -> bool { |
123 | StringRef FName = II->getName(); |
124 | return (FName.consume_front(Prefix: Parts) && ...) && FName.empty(); |
125 | }; |
126 | |
127 | return CompletelyMatchesParts("__" , Name, "_chk" ) || |
128 | CompletelyMatchesParts("__builtin_" , "__" , Name, "_chk" ); |
129 | } |
130 | |
131 | StringRef CheckerContext::getMacroNameOrSpelling(SourceLocation &Loc) { |
132 | if (Loc.isMacroID()) |
133 | return Lexer::getImmediateMacroName(Loc, SM: getSourceManager(), |
134 | LangOpts: getLangOpts()); |
135 | SmallString<16> buf; |
136 | return Lexer::getSpelling(loc: Loc, buffer&: buf, SM: getSourceManager(), options: getLangOpts()); |
137 | } |
138 | |
139 | /// Evaluate comparison and return true if it's known that condition is true |
140 | static bool evalComparison(SVal LHSVal, BinaryOperatorKind ComparisonOp, |
141 | SVal RHSVal, ProgramStateRef State) { |
142 | if (LHSVal.isUnknownOrUndef()) |
143 | return false; |
144 | ProgramStateManager &Mgr = State->getStateManager(); |
145 | if (!isa<NonLoc>(Val: LHSVal)) { |
146 | LHSVal = Mgr.getStoreManager().getBinding(store: State->getStore(), |
147 | loc: LHSVal.castAs<Loc>()); |
148 | if (LHSVal.isUnknownOrUndef() || !isa<NonLoc>(Val: LHSVal)) |
149 | return false; |
150 | } |
151 | |
152 | SValBuilder &Bldr = Mgr.getSValBuilder(); |
153 | SVal Eval = Bldr.evalBinOp(state: State, op: ComparisonOp, lhs: LHSVal, rhs: RHSVal, |
154 | type: Bldr.getConditionType()); |
155 | if (Eval.isUnknownOrUndef()) |
156 | return false; |
157 | ProgramStateRef StTrue, StFalse; |
158 | std::tie(args&: StTrue, args&: StFalse) = State->assume(Cond: Eval.castAs<DefinedSVal>()); |
159 | return StTrue && !StFalse; |
160 | } |
161 | |
162 | bool CheckerContext::isGreaterOrEqual(const Expr *E, unsigned long long Val) { |
163 | DefinedSVal V = getSValBuilder().makeIntVal(integer: Val, type: getASTContext().LongLongTy); |
164 | return evalComparison(LHSVal: getSVal(S: E), ComparisonOp: BO_GE, RHSVal: V, State: getState()); |
165 | } |
166 | |
167 | bool CheckerContext::isNegative(const Expr *E) { |
168 | DefinedSVal V = getSValBuilder().makeIntVal(integer: 0, isUnsigned: false); |
169 | return evalComparison(LHSVal: getSVal(S: E), ComparisonOp: BO_LT, RHSVal: V, State: getState()); |
170 | } |
171 | |