Skip to content

Commit faffbca

Browse files
committed
Function Summary Config Option
A new yaml config file is introduced for the stdlibrary checker, so the user can define argument constraints and function summaries in runtime.
1 parent 04fb3e3 commit faffbca

File tree

4 files changed

+305
-2
lines changed

4 files changed

+305
-2
lines changed

clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp

Lines changed: 172 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,102 @@
5252
#include "llvm/ADT/SmallString.h"
5353
#include "llvm/ADT/StringExtras.h"
5454

55+
#include "Yaml.h"
56+
#include "llvm/Support/YAMLTraits.h"
57+
5558
#include <string>
5659

5760
using namespace clang;
5861
using namespace clang::ento;
5962

63+
64+
65+
//SUMMARY CONFIGURATION PARSING
66+
67+
68+
struct SummaryConfiguration{
69+
enum class ArgConstraintType{
70+
NotNull,
71+
BufferSize
72+
};
73+
struct ArgConstraint{
74+
ArgConstraintType type;
75+
int arg; //Arg count in case of NotNull constraint type
76+
int bufferArg; // Arg count of the destination buffer
77+
int sizeArg; //arg count of the size argument
78+
int countArg; //arg count of the element count argument
79+
};
80+
struct Signature{
81+
std::vector<std::string> argTypes;
82+
std::string returnType;
83+
};
84+
enum class EvaluationType{
85+
NoEvalCall,
86+
EvalCallAsPure
87+
};
88+
struct Summary{
89+
std::string name;
90+
EvaluationType evaluationType;
91+
Signature signature;
92+
std::vector<ArgConstraint> argConstraints;
93+
94+
};
95+
std::vector<Summary> summaries;
96+
};
97+
98+
LLVM_YAML_IS_SEQUENCE_VECTOR(SummaryConfiguration::Summary)
99+
LLVM_YAML_IS_SEQUENCE_VECTOR(SummaryConfiguration::ArgConstraint)
100+
101+
// YAML CONFIG PARSING
102+
namespace llvm {
103+
namespace yaml {
104+
template <> struct MappingTraits<SummaryConfiguration> {
105+
static void mapping(IO &IO, SummaryConfiguration &Config) {
106+
IO.mapRequired("Summaries", Config.summaries);
107+
}
108+
};
109+
template <> struct MappingTraits<SummaryConfiguration::Summary> {
110+
static void mapping(IO &IO, SummaryConfiguration::Summary &Config) {
111+
IO.mapRequired("Name", Config.name);
112+
IO.mapRequired("EvaluationType", Config.evaluationType);
113+
IO.mapRequired("Signature", Config.signature);
114+
IO.mapRequired("ArgConstraints", Config.argConstraints);
115+
}
116+
};
117+
template <> struct ScalarEnumerationTraits<SummaryConfiguration::EvaluationType> {
118+
static void enumeration(IO &IO, SummaryConfiguration::EvaluationType &Config) {
119+
IO.enumCase(Config, "NoEvalCall", SummaryConfiguration::EvaluationType::NoEvalCall);
120+
IO.enumCase(Config, "EvalCallAsPure", SummaryConfiguration::EvaluationType::EvalCallAsPure);
121+
}
122+
};
123+
template <> struct MappingTraits<SummaryConfiguration::Signature> {
124+
static void mapping(IO &IO, SummaryConfiguration::Signature &Config) {
125+
IO.mapRequired("ArgTypes", Config.argTypes);
126+
IO.mapRequired("RetType", Config.returnType);
127+
}
128+
};
129+
130+
template <> struct ScalarEnumerationTraits<SummaryConfiguration::ArgConstraintType> {
131+
static void enumeration(IO &IO, SummaryConfiguration::ArgConstraintType &Config) {
132+
IO.enumCase(Config, "NotNull", SummaryConfiguration::ArgConstraintType::NotNull);
133+
IO.enumCase(Config, "BufferSize", SummaryConfiguration::ArgConstraintType::BufferSize);
134+
}
135+
};
136+
137+
template <> struct MappingTraits<SummaryConfiguration::ArgConstraint> {
138+
static void mapping(IO &IO, SummaryConfiguration::ArgConstraint &Config) {
139+
IO.mapRequired("type", Config.type);
140+
IO.mapOptional("arg", Config.arg);
141+
IO.mapOptional("bufferArg", Config.bufferArg);
142+
IO.mapOptional("sizeArg", Config.sizeArg);
143+
IO.mapOptional("countArg", Config.countArg);
144+
}
145+
};
146+
147+
} // namespace yaml
148+
} // namespace llvm
149+
150+
60151
namespace {
61152
class StdLibraryFunctionsChecker
62153
: public Checker<check::PreCall, check::PostCall, eval::Call> {
@@ -1357,6 +1448,84 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
13571448
Optional<QualType> FilePtrTy = getPointerTy(FileTy);
13581449
Optional<QualType> FilePtrRestrictTy = getRestrictTy(FilePtrTy);
13591450

1451+
////////READING SUMMARY CONFIG/////////////////////
1452+
1453+
// User-provided summary configuration.
1454+
CheckerManager *Mgr = C.getAnalysisManager().getCheckerManager();
1455+
std::string Option{"Config"};
1456+
/*StringRef ConfigFile =
1457+
Mgr->getAnalyzerOptions().getCheckerStringOption(this, Option);*/
1458+
StringRef ConfigFile = "/local/workspace/llvm-project/clang/test/Analysis/Inputs/fread-summary.yaml";
1459+
llvm::Optional<SummaryConfiguration> Config =
1460+
getConfiguration<SummaryConfiguration>(*Mgr, this, Option, ConfigFile);
1461+
llvm::errs()<<"Config :"<<Config.hasValue()<<"\n";
1462+
if (Config.has_value()){
1463+
for (const SummaryConfiguration::Summary &s : Config->summaries){
1464+
llvm::errs()<<"Config :"<<s.name<<"\n";
1465+
1466+
ArgTypes args;
1467+
for (const std::string &t: s.signature.argTypes){
1468+
auto ltype = lookupTy(t);
1469+
if (ltype.has_value()){
1470+
llvm::errs()<<"type string:"<<t<<"\n";
1471+
ltype->dump();
1472+
args.push_back(lookupTy(t));
1473+
}
1474+
}
1475+
RetType rt = lookupTy(s.signature.returnType);
1476+
auto GetSummary = [s]() {
1477+
switch (s.evaluationType) {
1478+
case SummaryConfiguration::EvaluationType::NoEvalCall:
1479+
return Summary(NoEvalCall);
1480+
case SummaryConfiguration::EvaluationType::EvalCallAsPure:
1481+
return Summary(EvalCallAsPure);
1482+
}
1483+
};
1484+
Summary summary = GetSummary();
1485+
1486+
for (const SummaryConfiguration::ArgConstraint &ac: s.argConstraints){
1487+
switch (ac.type){
1488+
case SummaryConfiguration::ArgConstraintType::NotNull:
1489+
summary.ArgConstraint(NotNull(ac.arg));
1490+
break;
1491+
case SummaryConfiguration::ArgConstraintType::BufferSize:
1492+
summary.ArgConstraint(BufferSize(ac.bufferArg,ac.sizeArg, ac.countArg));
1493+
break;
1494+
}
1495+
}
1496+
1497+
addToFunctionSummaryMap(
1498+
s.name,
1499+
Signature(args,
1500+
rt),
1501+
summary);
1502+
}
1503+
}
1504+
/*
1505+
WE want to add this
1506+
auto FreadSummary =
1507+
Summary(NoEvalCall)
1508+
.Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
1509+
ReturnValueCondition(WithinRange, Range(0, SizeMax))},
1510+
ErrnoIrrelevant)
1511+
.ArgConstraint(NotNull(ArgNo(0)))
1512+
.ArgConstraint(NotNull(ArgNo(3)))
1513+
.ArgConstraint(BufferSize(ArgNo(0), ArgNo(1),
1514+
ArgNo(2)));
1515+
*/
1516+
// size_t fread(void *restrict ptr, size_t size, size_t nitems,
1517+
// FILE *restrict stream);
1518+
/*addToFunctionSummaryMap(
1519+
"fread",
1520+
Signature(ArgTypes{VoidPtrRestrictTy, SizeTy, SizeTy, FilePtrRestrictTy},
1521+
RetType{SizeTy}),
1522+
FreadSummary);*/
1523+
1524+
1525+
////////////////////////////////////////////////////
1526+
1527+
1528+
13601529
// We are finally ready to define specifications for all supported functions.
13611530
//
13621531
// Argument ranges should always cover all variants. If return value
@@ -1591,11 +1760,11 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
15911760

15921761
// size_t fread(void *restrict ptr, size_t size, size_t nitems,
15931762
// FILE *restrict stream);
1594-
addToFunctionSummaryMap(
1763+
/*addToFunctionSummaryMap(
15951764
"fread",
15961765
Signature(ArgTypes{VoidPtrRestrictTy, SizeTy, SizeTy, FilePtrRestrictTy},
15971766
RetType{SizeTy}),
1598-
FreadSummary);
1767+
FreadSummary);*/
15991768
// size_t fwrite(const void *restrict ptr, size_t size, size_t nitems,
16001769
// FILE *restrict stream);
16011770
addToFunctionSummaryMap("fwrite",
@@ -3015,3 +3184,4 @@ bool ento::shouldRegisterStdCLibraryFunctionsChecker(
30153184

30163185
REGISTER_CHECKER(StdCLibraryFunctionArgsChecker)
30173186
REGISTER_CHECKER(StdCLibraryFunctionsTesterChecker)
3187+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# size_t fread(void *restrict ptr, size_t size, size_t nitems,
2+
# FILE *restrict stream);
3+
Summaries:
4+
- Name: "fread"
5+
Signature:
6+
ArgTypes:
7+
- "size_t"
8+
- "size_t"
9+
- "size_t"
10+
- "FILE *restrict"
11+
RetType: "size_t"
12+
EvaluationType: "NoEvalCall"
13+
ArgConstraints: # We give an error if this is violated
14+
-
15+
type: "NotNull"
16+
arg: 0
17+
18+
-
19+
type: "NotNull"
20+
arg: 3
21+
-
22+
type: "BufferSize"
23+
bufferArg: 0
24+
sizeArg: 1
25+
countArg: 2
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#list of summaries
2+
- Name: "isalnum" #int isalnum(int)
3+
Signature:
4+
ArgTypes:
5+
- "int"
6+
RetType: "int"
7+
EvaluationType: "EvalCallAsPure" # or NoEvalCall
8+
#case1
9+
Summary: #This models the function behaviour
10+
- ArgumentCondition:
11+
arg: 0
12+
type: "WithinRange"
13+
ranges: [['0','9'],['A', 'Z'], ['a', 'z']]
14+
ReturnValueCondition:
15+
type: "OutOfRange"
16+
ranges: [0,0]
17+
Errno: "ErrnoIrrelevant"
18+
AssumptionNote: "Assuming the character is alphanumeric"
19+
- ArgumentCondition:
20+
arg: 0
21+
type: "WithinRange"
22+
ranges: [128,"UCharRangeMax"]
23+
Errno: "ErrnoIrrelevant"
24+
- ArgumentCondition:
25+
arg: 0
26+
type: "OutOfRange"
27+
ranges: [['0','9'],['A', 'Z'], ['a', 'z'],[128,"UCharRangeMax"]]
28+
ReturnValueCondition:
29+
type: "WithinRange"
30+
ranges: [0,0]
31+
Errno: "ErrnoIrrelevant"
32+
AssumptionNote: "Assuming the character is non-alphanumeric"
33+
ArgConstraint: # We give an error if this is violated
34+
- ArgumentCondition:
35+
arg: 0
36+
type: "WithinRange"
37+
ranges: [["EOFv","EOFv"],[0,"UCharRangeMax"]]
38+
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Check the basic reporting/warning and the application of constraints.
2+
// RUN: %clang_analyze_cc1 %s \
3+
// RUN: -analyzer-checker=core \
4+
// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions \
5+
// RUN: -analyzer-checker=alpha.unix.StdCLibraryFunctionArgs \
6+
// RUN: -analyzer-checker=debug.StdCLibraryFunctionsTester \
7+
// RUN: -analyzer-checker=debug.ExprInspection \
8+
// RUN: -triple x86_64-unknown-linux-gnu \
9+
// RUN: -verify=report
10+
11+
// Check the bugpath related to the reports.
12+
// RUN: %clang_analyze_cc1 %s \
13+
// RUN: -analyzer-checker=core \
14+
// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions \
15+
// RUN: -analyzer-checker=alpha.unix.StdCLibraryFunctionArgs \
16+
// RUN: -analyzer-checker=debug.StdCLibraryFunctionsTester \
17+
// RUN: -analyzer-checker=debug.ExprInspection \
18+
// RUN: -triple x86_64-unknown-linux-gnu \
19+
// RUN: -analyzer-output=text \
20+
// RUN: -verify=bugpath
21+
22+
void clang_analyzer_eval(int);
23+
24+
typedef struct FILE FILE;
25+
typedef typeof(sizeof(int)) size_t;
26+
size_t fread(void *restrict, size_t, size_t, FILE *restrict);
27+
void test_notnull_concrete(FILE *fp) {
28+
fread(0, sizeof(int), 10, fp); // \
29+
// report-warning{{Function argument constraint is not satisfied}} \
30+
// report-note{{}} \
31+
// bugpath-warning{{Function argument constraint is not satisfied}} \
32+
// bugpath-note{{}} \
33+
// bugpath-note{{Function argument constraint is not satisfied}}
34+
}
35+
void test_notnull_symbolic(FILE *fp, int *buf) {
36+
fread(buf, sizeof(int), 10, fp);
37+
clang_analyzer_eval(buf != 0); // \
38+
// report-warning{{TRUE}} \
39+
// bugpath-warning{{TRUE}} \
40+
// bugpath-note{{TRUE}} \
41+
// bugpath-note{{'buf' is not equal to null}}
42+
}
43+
void test_notnull_symbolic2(FILE *fp, int *buf) {
44+
if (!buf) // bugpath-note{{Assuming 'buf' is null}} \
45+
// bugpath-note{{Taking true branch}}
46+
fread(buf, sizeof(int), 10, fp); // \
47+
// report-warning{{Function argument constraint is not satisfied}} \
48+
// report-note{{}} \
49+
// bugpath-warning{{Function argument constraint is not satisfied}} \
50+
// bugpath-note{{}} \
51+
// bugpath-note{{Function argument constraint is not satisfied}}
52+
}
53+
typedef __WCHAR_TYPE__ wchar_t;
54+
// This is one test case for the ARR38-C SEI-CERT rule.
55+
void ARR38_C_F(FILE *file) {
56+
enum { BUFFER_SIZE = 1024 };
57+
wchar_t wbuf[BUFFER_SIZE]; // bugpath-note{{'wbuf' initialized here}}
58+
59+
const size_t size = sizeof(*wbuf); // bugpath-note{{'size' initialized to}}
60+
const size_t nitems = sizeof(wbuf); // bugpath-note{{'nitems' initialized to}}
61+
62+
// The 3rd parameter should be the number of elements to read, not
63+
// the size in bytes.
64+
fread(wbuf, size, nitems, file); // \
65+
// report-warning{{Function argument constraint is not satisfied}} \
66+
// report-note{{}} \
67+
// bugpath-warning{{Function argument constraint is not satisfied}} \
68+
// bugpath-note{{}} \
69+
// bugpath-note{{Function argument constraint is not satisfied}}
70+
}

0 commit comments

Comments
 (0)