#include "clang/Basic/Builtins.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
using namespace clang;
using namespace ento;
namespace {
class BuiltinFunctionChecker : public Checker<eval::Call> {
public:
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
private:
const CallDescriptionSet BuiltinLikeStdFunctions{
{CDM::SimpleFunc, {"std", "addressof"}},
{CDM::SimpleFunc, {"std", "__addressof"}},
{CDM::SimpleFunc, {"std", "as_const"}},
{CDM::SimpleFunc, {"std", "forward"}},
{CDM::SimpleFunc, {"std", "forward_like"}},
{CDM::SimpleFunc, {"std", "move"}},
{CDM::SimpleFunc, {"std", "move_if_noexcept"}},
};
bool isBuiltinLikeFunction(const CallEvent &Call) const;
};
}
bool BuiltinFunctionChecker::isBuiltinLikeFunction(
const CallEvent &Call) const {
const auto *FD = llvm::dyn_cast_or_null<FunctionDecl>(Call.getDecl());
if (!FD || FD->getNumParams() != 1)
return false;
if (QualType RetTy = FD->getReturnType();
!RetTy->isPointerType() && !RetTy->isReferenceType())
return false;
if (QualType ParmTy = FD->getParamDecl(0)->getType();
!ParmTy->isPointerType() && !ParmTy->isReferenceType())
return false;
return BuiltinLikeStdFunctions.contains(Call);
}
bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef state = C.getState();
const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
if (!FD)
return false;
const LocationContext *LCtx = C.getLocationContext();
const Expr *CE = Call.getOriginExpr();
if (isBuiltinLikeFunction(Call)) {
C.addTransition(state->BindExpr(CE, LCtx, Call.getArgSVal(0)));
return true;
}
switch (FD->getBuiltinID()) {
default:
return false;
case Builtin::BI__builtin_assume:
case Builtin::BI__assume: {
assert (Call.getNumArgs() > 0);
SVal Arg = Call.getArgSVal(0);
if (Arg.isUndef())
return true;
state = state->assume(Arg.castAs<DefinedOrUnknownSVal>(), true);
if (!state) {
C.generateSink(C.getState(), C.getPredecessor());
return true;
}
C.addTransition(state);
return true;
}
case Builtin::BI__builtin_unpredictable:
case Builtin::BI__builtin_expect:
case Builtin::BI__builtin_expect_with_probability:
case Builtin::BI__builtin_assume_aligned:
case Builtin::BI__builtin_addressof:
case Builtin::BI__builtin_function_start: {
assert (Call.getNumArgs() > 0);
SVal Arg = Call.getArgSVal(0);
C.addTransition(state->BindExpr(CE, LCtx, Arg));
return true;
}
case Builtin::BI__builtin_dynamic_object_size:
case Builtin::BI__builtin_object_size:
case Builtin::BI__builtin_constant_p: {
SValBuilder &SVB = C.getSValBuilder();
SVal V = UnknownVal();
Expr::EvalResult EVResult;
if (CE->EvaluateAsInt(EVResult, C.getASTContext(), Expr::SE_NoSideEffects)) {
llvm::APSInt Result = EVResult.Val.getInt();
BasicValueFactory &BVF = SVB.getBasicValueFactory();
BVF.getAPSIntType(CE->getType()).apply(Result);
V = SVB.makeIntVal(Result);
}
if (FD->getBuiltinID() == Builtin::BI__builtin_constant_p) {
if (V.isUnknown())
V = SVB.makeIntVal(0, CE->getType());
}
C.addTransition(state->BindExpr(CE, LCtx, V));
return true;
}
}
}
void ento::registerBuiltinFunctionChecker(CheckerManager &mgr) {
mgr.registerChecker<BuiltinFunctionChecker>();
}
bool ento::shouldRegisterBuiltinFunctionChecker(const CheckerManager &mgr) {
return true;
}