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/CallEvent.h" |
22 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
23 | #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" |
24 | |
25 | using namespace clang; |
26 | using namespace ento; |
27 | |
28 | namespace { |
29 | |
30 | class BuiltinFunctionChecker : public Checker<eval::Call> { |
31 | public: |
32 | bool evalCall(const CallEvent &Call, CheckerContext &C) const; |
33 | }; |
34 | |
35 | } |
36 | |
37 | bool BuiltinFunctionChecker::evalCall(const CallEvent &Call, |
38 | CheckerContext &C) const { |
39 | ProgramStateRef state = C.getState(); |
40 | const auto *FD = dyn_cast_or_null<FunctionDecl>(Val: Call.getDecl()); |
41 | if (!FD) |
42 | return false; |
43 | |
44 | const LocationContext *LCtx = C.getLocationContext(); |
45 | const Expr *CE = Call.getOriginExpr(); |
46 | |
47 | switch (FD->getBuiltinID()) { |
48 | default: |
49 | return false; |
50 | |
51 | case Builtin::BI__builtin_assume: |
52 | case Builtin::BI__assume: { |
53 | assert (Call.getNumArgs() > 0); |
54 | SVal Arg = Call.getArgSVal(Index: 0); |
55 | if (Arg.isUndef()) |
56 | return true; // Return true to model purity. |
57 | |
58 | state = state->assume(Cond: Arg.castAs<DefinedOrUnknownSVal>(), Assumption: true); |
59 | // FIXME: do we want to warn here? Not right now. The most reports might |
60 | // come from infeasible paths, thus being false positives. |
61 | if (!state) { |
62 | C.generateSink(State: C.getState(), Pred: C.getPredecessor()); |
63 | return true; |
64 | } |
65 | |
66 | C.addTransition(State: state); |
67 | return true; |
68 | } |
69 | |
70 | case Builtin::BI__builtin_unpredictable: |
71 | case Builtin::BI__builtin_expect: |
72 | case Builtin::BI__builtin_expect_with_probability: |
73 | case Builtin::BI__builtin_assume_aligned: |
74 | case Builtin::BI__builtin_addressof: |
75 | case Builtin::BI__builtin_function_start: { |
76 | // For __builtin_unpredictable, __builtin_expect, |
77 | // __builtin_expect_with_probability and __builtin_assume_aligned, |
78 | // just return the value of the subexpression. |
79 | // __builtin_addressof is going from a reference to a pointer, but those |
80 | // are represented the same way in the analyzer. |
81 | assert (Call.getNumArgs() > 0); |
82 | SVal Arg = Call.getArgSVal(Index: 0); |
83 | C.addTransition(State: state->BindExpr(CE, LCtx, Arg)); |
84 | return true; |
85 | } |
86 | |
87 | case Builtin::BI__builtin_dynamic_object_size: |
88 | case Builtin::BI__builtin_object_size: |
89 | case Builtin::BI__builtin_constant_p: { |
90 | // This must be resolvable at compile time, so we defer to the constant |
91 | // evaluator for a value. |
92 | SValBuilder &SVB = C.getSValBuilder(); |
93 | SVal V = UnknownVal(); |
94 | Expr::EvalResult EVResult; |
95 | if (CE->EvaluateAsInt(Result&: EVResult, Ctx: C.getASTContext(), AllowSideEffects: Expr::SE_NoSideEffects)) { |
96 | // Make sure the result has the correct type. |
97 | llvm::APSInt Result = EVResult.Val.getInt(); |
98 | BasicValueFactory &BVF = SVB.getBasicValueFactory(); |
99 | BVF.getAPSIntType(T: CE->getType()).apply(Value&: Result); |
100 | V = SVB.makeIntVal(integer: Result); |
101 | } |
102 | |
103 | if (FD->getBuiltinID() == Builtin::BI__builtin_constant_p) { |
104 | // If we didn't manage to figure out if the value is constant or not, |
105 | // it is safe to assume that it's not constant and unsafe to assume |
106 | // that it's constant. |
107 | if (V.isUnknown()) |
108 | V = SVB.makeIntVal(integer: 0, type: CE->getType()); |
109 | } |
110 | |
111 | C.addTransition(State: state->BindExpr(CE, LCtx, V)); |
112 | return true; |
113 | } |
114 | } |
115 | } |
116 | |
117 | void ento::registerBuiltinFunctionChecker(CheckerManager &mgr) { |
118 | mgr.registerChecker<BuiltinFunctionChecker>(); |
119 | } |
120 | |
121 | bool ento::shouldRegisterBuiltinFunctionChecker(const CheckerManager &mgr) { |
122 | return true; |
123 | } |
124 | |