martong created this revision.
martong added a reviewer: NoQ.
Herald added subscribers: cfe-commits, Charusso, gamesh411, dkrupp, donat.nagy,
Szelethus, mikhail.ramalho, a.sidorin, rnkovacs, szepet, baloghadamsoftware,
xazax.hun, whisperity.
Herald added a project: clang.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D73898
Files:
clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
clang/test/Analysis/std-c-library-functions-arg-constraints.c
Index: clang/test/Analysis/std-c-library-functions-arg-constraints.c
===================================================================
--- /dev/null
+++ clang/test/Analysis/std-c-library-functions-arg-constraints.c
@@ -0,0 +1,22 @@
+// RUN: %clang_analyze_cc1 %s \
+// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions \
+// RUN: -analyzer-checker=debug.ExprInspection \
+// RUN: -triple x86_64-unknown-linux-gnu
+
+void clang_analyzer_eval(int);
+
+int glob;
+
+typedef struct FILE FILE;
+#define EOF -1
+
+int isalnum(int);
+void test_alnum_concrete() {
+ int ret = isalnum(256); // expected-warning{{Function argument constraint is not satisfied}}
+ (void)ret;
+}
+void test_alnum_symbolic(int x) {
+ int ret = isalnum(x);
+ (void)ret;
+ clang_analyzer_eval(EOF <= x && x <= 255); // expected-warning{{TRUE}}
+}
Index: clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -51,6 +51,7 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
@@ -60,7 +61,8 @@
using namespace clang::ento;
namespace {
-class StdLibraryFunctionsChecker : public Checker<check::PostCall, eval::Call> {
+class StdLibraryFunctionsChecker
+ : public Checker<check::PreCall, check::PostCall, eval::Call> {
/// Below is a series of typedefs necessary to define function specs.
/// We avoid nesting types here because each additional qualifier
/// would need to be repeated in every function spec.
@@ -142,6 +144,10 @@
applyAsComparesToArgument(ProgramStateRef State, const CallEvent &Call,
const FunctionSummaryTy &Summary) const;
+ void checkAsWithinRange(ProgramStateRef State, const CallEvent &Call,
+ const FunctionSummaryTy &Summary, const BugType &BT,
+ CheckerContext &C) const;
+
public:
ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
const FunctionSummaryTy &Summary) const {
@@ -155,6 +161,21 @@
}
llvm_unreachable("Unknown ValueRange kind!");
}
+
+ void check(ProgramStateRef State, const CallEvent &Call,
+ const FunctionSummaryTy &Summary, const BugType &BT,
+ CheckerContext &C) const {
+ switch (Kind) {
+ case OutOfRange:
+ llvm_unreachable("Not implemented yet!");
+ case WithinRange:
+ checkAsWithinRange(State, Call, Summary, BT, C);
+ return;
+ case ComparesToArgument:
+ llvm_unreachable("Not implemented yet!");
+ }
+ llvm_unreachable("Unknown ValueRange kind!");
+ }
};
/// The complete list of ranges that defines a single branch.
@@ -164,15 +185,21 @@
using RetTypeTy = QualType;
using RangesTy = std::vector<ValueRangeSet>;
- /// Includes information about function prototype (which is necessary to
- /// ensure we're modeling the right function and casting values properly),
- /// approach to invalidation, and a list of branches - essentially, a list
- /// of list of ranges - essentially, a list of lists of lists of segments.
+ /// Includes information about
+ /// * function prototype (which is necessary to
+ /// ensure we're modeling the right function and casting values properly),
+ /// * approach to invalidation,
+ /// * a list of branches - a list of list of ranges -
+ /// i.e. a list of lists of lists of segments,
+ /// * a list of argument constraints, that must be true on every branch.
+ /// If these constraints are not satisfied that means a fatal error
+ /// usually resulting in undefined behaviour.
struct FunctionSummaryTy {
const ArgTypesTy ArgTypes;
const RetTypeTy RetType;
const InvalidationKindTy InvalidationKind;
RangesTy Ranges;
+ ValueRangeSet ArgConstraints;
FunctionSummaryTy(ArgTypesTy ArgTypes, RetTypeTy RetType,
InvalidationKindTy InvalidationKind)
@@ -183,6 +210,10 @@
Ranges.push_back(VRS);
return *this;
}
+ FunctionSummaryTy &ArgConstraint(ValueRange VR) {
+ ArgConstraints.push_back(VR);
+ return *this;
+ }
private:
static void assertTypeSuitableForSummary(QualType T) {
@@ -219,6 +250,8 @@
typedef llvm::StringMap<FunctionVariantsTy> FunctionSummaryMapTy;
mutable FunctionSummaryMapTy FunctionSummaryMap;
+ BugType BT{this, "Unsatisfied argument constraints", categories::LogicError};
+
// Auxiliary functions to support ArgNoTy within all structures
// in a unified manner.
static QualType getArgType(const FunctionSummaryTy &Summary, ArgNoTy ArgNo) {
@@ -237,6 +270,7 @@
}
public:
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
@@ -350,6 +384,62 @@
return State;
}
+void StdLibraryFunctionsChecker::ValueRange::checkAsWithinRange(
+ ProgramStateRef State, const CallEvent &Call,
+ const FunctionSummaryTy &Summary, const BugType &BT,
+ CheckerContext &C) const {
+
+ ProgramStateManager &Mgr = State->getStateManager();
+ SValBuilder &SVB = Mgr.getSValBuilder();
+ BasicValueFactory &BVF = SVB.getBasicValueFactory();
+ QualType T = getArgType(Summary, getArgNo());
+ SVal V = getArgSVal(Call, getArgNo());
+ switch (V.getSubKind()) {
+ default:
+ // FIXME Handle other cases.
+ return;
+ case nonloc::ConcreteIntKind: {
+ const llvm::APSInt &IntVal = V.castAs<nonloc::ConcreteInt>().getValue();
+ const IntRangeVectorTy &R = getRanges();
+ const llvm::APSInt &Min = BVF.getValue(R[0].first, T);
+ const llvm::APSInt &Max = BVF.getValue(R[0].second, T);
+ assert(Min <= Max);
+
+ // Out of range.
+ if (IntVal < Min || IntVal > Max) {
+ if (ExplodedNode *N = C.generateErrorNode(State)) {
+ // FIXME Add detailed diagnostic.
+ std::string Msg = "Function argument constraint is not satisfied";
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
+ bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *R);
+ C.emitReport(std::move(R));
+ }
+ }
+ }
+ } // end switch
+}
+
+void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ if (!FD)
+ return;
+
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ Optional<FunctionSummaryTy> FoundSummary = findFunctionSummary(FD, CE, C);
+ if (!FoundSummary)
+ return;
+
+ const FunctionSummaryTy &Summary = *FoundSummary;
+ ProgramStateRef State = C.getState();
+ for (const auto &VR : Summary.ArgConstraints) {
+ VR.check(State, Call, Summary, BT, C);
+ }
+}
+
void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
@@ -368,6 +458,7 @@
const FunctionSummaryTy &Summary = *FoundSummary;
ProgramStateRef State = C.getState();
+ // Apply specifications.
for (const auto &VRS: Summary.Ranges) {
ProgramStateRef NewState = State;
for (const auto &VR: VRS) {
@@ -376,6 +467,11 @@
break;
}
+ // Apply argument constraints as well.
+ for (const auto &VR : Summary.ArgConstraints)
+ if (NewState)
+ NewState = VR.apply(NewState, Call, Summary);
+
if (NewState && NewState != State)
C.addTransition(NewState);
}
@@ -598,6 +694,9 @@
FunctionSummaryMap = {
// The isascii() family of functions.
+ // The behavior is undefined if the value of the argument is not
+ // representable as unsigned char or is not equal to EOF. See e.g. C99
+ // 7.4.1.2 The isalpha function (p: 181-182).
{
"isalnum",
FunctionVariantsTy{
@@ -616,7 +715,9 @@
{ArgumentCondition(
0U, OutOfRange,
{{'0', '9'}, {'A', 'Z'}, {'a', 'z'}, {128, 255}}),
- ReturnValueCondition(WithinRange, SingleValue(0))})},
+ ReturnValueCondition(WithinRange, SingleValue(0))})
+ .ArgConstraint(
+ ArgumentCondition(0U, WithinRange, {{-1, 255}}))},
},
{
"isalpha",
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits