1 | //=== BuiltinFunctionChecker.cpp --------------------------------*- C++ -*-===// |
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 checker evaluates "standalone" clang builtin functions that are not |
10 | // just special-cased variants of well-known non-builtin functions. |
11 | // Builtin functions like __builtin_memcpy and __builtin_alloca should be |
12 | // evaluated by the same checker that handles their non-builtin variant to |
13 | // ensure that the two variants are handled consistently. |
14 | // |
15 | //===----------------------------------------------------------------------===// |
16 | |
17 | #include "clang/Basic/Builtins.h" |
18 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
19 | #include "clang/StaticAnalyzer/Core/Checker.h" |
20 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
21 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" |
22 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
23 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
24 | #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" |
25 | |
26 | using namespace clang; |
27 | using namespace ento; |
28 | |
29 | namespace { |
30 | |
31 | class BuiltinFunctionChecker : public Checker<eval::Call> { |
32 | public: |
33 | bool evalCall(const CallEvent &Call, CheckerContext &C) const; |
34 | |
35 | private: |
36 | // From: clang/include/clang/Basic/Builtins.def |
37 | // C++ standard library builtins in namespace 'std'. |
38 | const CallDescriptionSet BuiltinLikeStdFunctions{ |
39 | {CDM::SimpleFunc, {"std" , "addressof" }}, // |
40 | {CDM::SimpleFunc, {"std" , "__addressof" }}, // |
41 | {CDM::SimpleFunc, {"std" , "as_const" }}, // |
42 | {CDM::SimpleFunc, {"std" , "forward" }}, // |
43 | {CDM::SimpleFunc, {"std" , "forward_like" }}, // |
44 | {CDM::SimpleFunc, {"std" , "move" }}, // |
45 | {CDM::SimpleFunc, {"std" , "move_if_noexcept" }}, // |
46 | }; |
47 | |
48 | bool isBuiltinLikeFunction(const CallEvent &Call) const; |
49 | }; |
50 | |
51 | } // namespace |
52 | |
53 | bool BuiltinFunctionChecker::isBuiltinLikeFunction( |
54 | const CallEvent &Call) const { |
55 | const auto *FD = llvm::dyn_cast_or_null<FunctionDecl>(Val: Call.getDecl()); |
56 | if (!FD || FD->getNumParams() != 1) |
57 | return false; |
58 | |
59 | if (QualType RetTy = FD->getReturnType(); |
60 | !RetTy->isPointerType() && !RetTy->isReferenceType()) |
61 | return false; |
62 | |
63 | if (QualType ParmTy = FD->getParamDecl(i: 0)->getType(); |
64 | !ParmTy->isPointerType() && !ParmTy->isReferenceType()) |
65 | return false; |
66 | |
67 | return BuiltinLikeStdFunctions.contains(Call); |
68 | } |
69 | |
70 | bool BuiltinFunctionChecker::evalCall(const CallEvent &Call, |
71 | CheckerContext &C) const { |
72 | ProgramStateRef state = C.getState(); |
73 | const auto *FD = dyn_cast_or_null<FunctionDecl>(Val: Call.getDecl()); |
74 | if (!FD) |
75 | return false; |
76 | |
77 | const LocationContext *LCtx = C.getLocationContext(); |
78 | const Expr *CE = Call.getOriginExpr(); |
79 | |
80 | if (isBuiltinLikeFunction(Call)) { |
81 | C.addTransition(State: state->BindExpr(S: CE, LCtx, V: Call.getArgSVal(Index: 0))); |
82 | return true; |
83 | } |
84 | |
85 | switch (FD->getBuiltinID()) { |
86 | default: |
87 | return false; |
88 | |
89 | case Builtin::BI__builtin_assume: |
90 | case Builtin::BI__assume: { |
91 | assert (Call.getNumArgs() > 0); |
92 | SVal Arg = Call.getArgSVal(Index: 0); |
93 | if (Arg.isUndef()) |
94 | return true; // Return true to model purity. |
95 | |
96 | state = state->assume(Cond: Arg.castAs<DefinedOrUnknownSVal>(), Assumption: true); |
97 | // FIXME: do we want to warn here? Not right now. The most reports might |
98 | // come from infeasible paths, thus being false positives. |
99 | if (!state) { |
100 | C.generateSink(State: C.getState(), Pred: C.getPredecessor()); |
101 | return true; |
102 | } |
103 | |
104 | C.addTransition(State: state); |
105 | return true; |
106 | } |
107 | |
108 | case Builtin::BI__builtin_unpredictable: |
109 | case Builtin::BI__builtin_expect: |
110 | case Builtin::BI__builtin_expect_with_probability: |
111 | case Builtin::BI__builtin_assume_aligned: |
112 | case Builtin::BI__builtin_addressof: |
113 | case Builtin::BI__builtin_function_start: { |
114 | // For __builtin_unpredictable, __builtin_expect, |
115 | // __builtin_expect_with_probability and __builtin_assume_aligned, |
116 | // just return the value of the subexpression. |
117 | // __builtin_addressof is going from a reference to a pointer, but those |
118 | // are represented the same way in the analyzer. |
119 | assert (Call.getNumArgs() > 0); |
120 | SVal Arg = Call.getArgSVal(Index: 0); |
121 | C.addTransition(State: state->BindExpr(S: CE, LCtx, V: Arg)); |
122 | return true; |
123 | } |
124 | |
125 | case Builtin::BI__builtin_dynamic_object_size: |
126 | case Builtin::BI__builtin_object_size: |
127 | case Builtin::BI__builtin_constant_p: { |
128 | // This must be resolvable at compile time, so we defer to the constant |
129 | // evaluator for a value. |
130 | SValBuilder &SVB = C.getSValBuilder(); |
131 | SVal V = UnknownVal(); |
132 | Expr::EvalResult EVResult; |
133 | if (CE->EvaluateAsInt(Result&: EVResult, Ctx: C.getASTContext(), AllowSideEffects: Expr::SE_NoSideEffects)) { |
134 | // Make sure the result has the correct type. |
135 | llvm::APSInt Result = EVResult.Val.getInt(); |
136 | BasicValueFactory &BVF = SVB.getBasicValueFactory(); |
137 | BVF.getAPSIntType(T: CE->getType()).apply(Value&: Result); |
138 | V = SVB.makeIntVal(integer: Result); |
139 | } |
140 | |
141 | if (FD->getBuiltinID() == Builtin::BI__builtin_constant_p) { |
142 | // If we didn't manage to figure out if the value is constant or not, |
143 | // it is safe to assume that it's not constant and unsafe to assume |
144 | // that it's constant. |
145 | if (V.isUnknown()) |
146 | V = SVB.makeIntVal(integer: 0, type: CE->getType()); |
147 | } |
148 | |
149 | C.addTransition(State: state->BindExpr(S: CE, LCtx, V)); |
150 | return true; |
151 | } |
152 | } |
153 | } |
154 | |
155 | void ento::registerBuiltinFunctionChecker(CheckerManager &mgr) { |
156 | mgr.registerChecker<BuiltinFunctionChecker>(); |
157 | } |
158 | |
159 | bool ento::shouldRegisterBuiltinFunctionChecker(const CheckerManager &mgr) { |
160 | return true; |
161 | } |
162 | |