Skip to content

Commit

Permalink
Function Summary Config Option
Browse files Browse the repository at this point in the history
A new yaml config file is introduced
for the stdlibrary checker,
so the user can define
argument constraints and function summaries
in runtime.
  • Loading branch information
dkrupp committed Nov 10, 2022
1 parent 04fb3e3 commit faffbca
Show file tree
Hide file tree
Showing 4 changed files with 305 additions and 2 deletions.
174 changes: 172 additions & 2 deletions clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,102 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"

#include "Yaml.h"
#include "llvm/Support/YAMLTraits.h"

#include <string>

using namespace clang;
using namespace clang::ento;



//SUMMARY CONFIGURATION PARSING


struct SummaryConfiguration{
enum class ArgConstraintType{
NotNull,
BufferSize
};
struct ArgConstraint{
ArgConstraintType type;
int arg; //Arg count in case of NotNull constraint type
int bufferArg; // Arg count of the destination buffer
int sizeArg; //arg count of the size argument
int countArg; //arg count of the element count argument
};
struct Signature{
std::vector<std::string> argTypes;
std::string returnType;
};
enum class EvaluationType{
NoEvalCall,
EvalCallAsPure
};
struct Summary{
std::string name;
EvaluationType evaluationType;
Signature signature;
std::vector<ArgConstraint> argConstraints;

};
std::vector<Summary> summaries;
};

LLVM_YAML_IS_SEQUENCE_VECTOR(SummaryConfiguration::Summary)
LLVM_YAML_IS_SEQUENCE_VECTOR(SummaryConfiguration::ArgConstraint)

// YAML CONFIG PARSING
namespace llvm {
namespace yaml {
template <> struct MappingTraits<SummaryConfiguration> {
static void mapping(IO &IO, SummaryConfiguration &Config) {
IO.mapRequired("Summaries", Config.summaries);
}
};
template <> struct MappingTraits<SummaryConfiguration::Summary> {
static void mapping(IO &IO, SummaryConfiguration::Summary &Config) {
IO.mapRequired("Name", Config.name);
IO.mapRequired("EvaluationType", Config.evaluationType);
IO.mapRequired("Signature", Config.signature);
IO.mapRequired("ArgConstraints", Config.argConstraints);
}
};
template <> struct ScalarEnumerationTraits<SummaryConfiguration::EvaluationType> {
static void enumeration(IO &IO, SummaryConfiguration::EvaluationType &Config) {
IO.enumCase(Config, "NoEvalCall", SummaryConfiguration::EvaluationType::NoEvalCall);
IO.enumCase(Config, "EvalCallAsPure", SummaryConfiguration::EvaluationType::EvalCallAsPure);
}
};
template <> struct MappingTraits<SummaryConfiguration::Signature> {
static void mapping(IO &IO, SummaryConfiguration::Signature &Config) {
IO.mapRequired("ArgTypes", Config.argTypes);
IO.mapRequired("RetType", Config.returnType);
}
};

template <> struct ScalarEnumerationTraits<SummaryConfiguration::ArgConstraintType> {
static void enumeration(IO &IO, SummaryConfiguration::ArgConstraintType &Config) {
IO.enumCase(Config, "NotNull", SummaryConfiguration::ArgConstraintType::NotNull);
IO.enumCase(Config, "BufferSize", SummaryConfiguration::ArgConstraintType::BufferSize);
}
};

template <> struct MappingTraits<SummaryConfiguration::ArgConstraint> {
static void mapping(IO &IO, SummaryConfiguration::ArgConstraint &Config) {
IO.mapRequired("type", Config.type);
IO.mapOptional("arg", Config.arg);
IO.mapOptional("bufferArg", Config.bufferArg);
IO.mapOptional("sizeArg", Config.sizeArg);
IO.mapOptional("countArg", Config.countArg);
}
};

} // namespace yaml
} // namespace llvm


namespace {
class StdLibraryFunctionsChecker
: public Checker<check::PreCall, check::PostCall, eval::Call> {
Expand Down Expand Up @@ -1357,6 +1448,84 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Optional<QualType> FilePtrTy = getPointerTy(FileTy);
Optional<QualType> FilePtrRestrictTy = getRestrictTy(FilePtrTy);

////////READING SUMMARY CONFIG/////////////////////

// User-provided summary configuration.
CheckerManager *Mgr = C.getAnalysisManager().getCheckerManager();
std::string Option{"Config"};
/*StringRef ConfigFile =
Mgr->getAnalyzerOptions().getCheckerStringOption(this, Option);*/
StringRef ConfigFile = "/local/workspace/llvm-project/clang/test/Analysis/Inputs/fread-summary.yaml";
llvm::Optional<SummaryConfiguration> Config =
getConfiguration<SummaryConfiguration>(*Mgr, this, Option, ConfigFile);
llvm::errs()<<"Config :"<<Config.hasValue()<<"\n";
if (Config.has_value()){
for (const SummaryConfiguration::Summary &s : Config->summaries){
llvm::errs()<<"Config :"<<s.name<<"\n";

ArgTypes args;
for (const std::string &t: s.signature.argTypes){
auto ltype = lookupTy(t);
if (ltype.has_value()){
llvm::errs()<<"type string:"<<t<<"\n";
ltype->dump();
args.push_back(lookupTy(t));
}
}
RetType rt = lookupTy(s.signature.returnType);
auto GetSummary = [s]() {
switch (s.evaluationType) {
case SummaryConfiguration::EvaluationType::NoEvalCall:
return Summary(NoEvalCall);
case SummaryConfiguration::EvaluationType::EvalCallAsPure:
return Summary(EvalCallAsPure);
}
};
Summary summary = GetSummary();

for (const SummaryConfiguration::ArgConstraint &ac: s.argConstraints){
switch (ac.type){
case SummaryConfiguration::ArgConstraintType::NotNull:
summary.ArgConstraint(NotNull(ac.arg));
break;
case SummaryConfiguration::ArgConstraintType::BufferSize:
summary.ArgConstraint(BufferSize(ac.bufferArg,ac.sizeArg, ac.countArg));
break;
}
}

addToFunctionSummaryMap(
s.name,
Signature(args,
rt),
summary);
}
}
/*
WE want to add this
auto FreadSummary =
Summary(NoEvalCall)
.Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
ReturnValueCondition(WithinRange, Range(0, SizeMax))},
ErrnoIrrelevant)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(3)))
.ArgConstraint(BufferSize(ArgNo(0), ArgNo(1),
ArgNo(2)));
*/
// size_t fread(void *restrict ptr, size_t size, size_t nitems,
// FILE *restrict stream);
/*addToFunctionSummaryMap(
"fread",
Signature(ArgTypes{VoidPtrRestrictTy, SizeTy, SizeTy, FilePtrRestrictTy},
RetType{SizeTy}),
FreadSummary);*/


////////////////////////////////////////////////////



// We are finally ready to define specifications for all supported functions.
//
// Argument ranges should always cover all variants. If return value
Expand Down Expand Up @@ -1591,11 +1760,11 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(

// size_t fread(void *restrict ptr, size_t size, size_t nitems,
// FILE *restrict stream);
addToFunctionSummaryMap(
/*addToFunctionSummaryMap(
"fread",
Signature(ArgTypes{VoidPtrRestrictTy, SizeTy, SizeTy, FilePtrRestrictTy},
RetType{SizeTy}),
FreadSummary);
FreadSummary);*/
// size_t fwrite(const void *restrict ptr, size_t size, size_t nitems,
// FILE *restrict stream);
addToFunctionSummaryMap("fwrite",
Expand Down Expand Up @@ -3015,3 +3184,4 @@ bool ento::shouldRegisterStdCLibraryFunctionsChecker(

REGISTER_CHECKER(StdCLibraryFunctionArgsChecker)
REGISTER_CHECKER(StdCLibraryFunctionsTesterChecker)

25 changes: 25 additions & 0 deletions clang/test/Analysis/Inputs/fread-summary.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# size_t fread(void *restrict ptr, size_t size, size_t nitems,
# FILE *restrict stream);
Summaries:
- Name: "fread"
Signature:
ArgTypes:
- "size_t"
- "size_t"
- "size_t"
- "FILE *restrict"
RetType: "size_t"
EvaluationType: "NoEvalCall"
ArgConstraints: # We give an error if this is violated
-
type: "NotNull"
arg: 0

-
type: "NotNull"
arg: 3
-
type: "BufferSize"
bufferArg: 0
sizeArg: 1
countArg: 2
38 changes: 38 additions & 0 deletions clang/test/Analysis/Inputs/isalnum-summary.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#list of summaries
- Name: "isalnum" #int isalnum(int)
Signature:
ArgTypes:
- "int"
RetType: "int"
EvaluationType: "EvalCallAsPure" # or NoEvalCall
#case1
Summary: #This models the function behaviour
- ArgumentCondition:
arg: 0
type: "WithinRange"
ranges: [['0','9'],['A', 'Z'], ['a', 'z']]
ReturnValueCondition:
type: "OutOfRange"
ranges: [0,0]
Errno: "ErrnoIrrelevant"
AssumptionNote: "Assuming the character is alphanumeric"
- ArgumentCondition:
arg: 0
type: "WithinRange"
ranges: [128,"UCharRangeMax"]
Errno: "ErrnoIrrelevant"
- ArgumentCondition:
arg: 0
type: "OutOfRange"
ranges: [['0','9'],['A', 'Z'], ['a', 'z'],[128,"UCharRangeMax"]]
ReturnValueCondition:
type: "WithinRange"
ranges: [0,0]
Errno: "ErrnoIrrelevant"
AssumptionNote: "Assuming the character is non-alphanumeric"
ArgConstraint: # We give an error if this is violated
- ArgumentCondition:
arg: 0
type: "WithinRange"
ranges: [["EOFv","EOFv"],[0,"UCharRangeMax"]]

70 changes: 70 additions & 0 deletions clang/test/Analysis/std-c-library-functions-config.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Check the basic reporting/warning and the application of constraints.
// RUN: %clang_analyze_cc1 %s \
// RUN: -analyzer-checker=core \
// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions \
// RUN: -analyzer-checker=alpha.unix.StdCLibraryFunctionArgs \
// RUN: -analyzer-checker=debug.StdCLibraryFunctionsTester \
// RUN: -analyzer-checker=debug.ExprInspection \
// RUN: -triple x86_64-unknown-linux-gnu \
// RUN: -verify=report

// Check the bugpath related to the reports.
// RUN: %clang_analyze_cc1 %s \
// RUN: -analyzer-checker=core \
// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions \
// RUN: -analyzer-checker=alpha.unix.StdCLibraryFunctionArgs \
// RUN: -analyzer-checker=debug.StdCLibraryFunctionsTester \
// RUN: -analyzer-checker=debug.ExprInspection \
// RUN: -triple x86_64-unknown-linux-gnu \
// RUN: -analyzer-output=text \
// RUN: -verify=bugpath

void clang_analyzer_eval(int);

typedef struct FILE FILE;
typedef typeof(sizeof(int)) size_t;
size_t fread(void *restrict, size_t, size_t, FILE *restrict);
void test_notnull_concrete(FILE *fp) {
fread(0, sizeof(int), 10, fp); // \
// report-warning{{Function argument constraint is not satisfied}} \
// report-note{{}} \
// bugpath-warning{{Function argument constraint is not satisfied}} \
// bugpath-note{{}} \
// bugpath-note{{Function argument constraint is not satisfied}}
}
void test_notnull_symbolic(FILE *fp, int *buf) {
fread(buf, sizeof(int), 10, fp);
clang_analyzer_eval(buf != 0); // \
// report-warning{{TRUE}} \
// bugpath-warning{{TRUE}} \
// bugpath-note{{TRUE}} \
// bugpath-note{{'buf' is not equal to null}}
}
void test_notnull_symbolic2(FILE *fp, int *buf) {
if (!buf) // bugpath-note{{Assuming 'buf' is null}} \
// bugpath-note{{Taking true branch}}
fread(buf, sizeof(int), 10, fp); // \
// report-warning{{Function argument constraint is not satisfied}} \
// report-note{{}} \
// bugpath-warning{{Function argument constraint is not satisfied}} \
// bugpath-note{{}} \
// bugpath-note{{Function argument constraint is not satisfied}}
}
typedef __WCHAR_TYPE__ wchar_t;
// This is one test case for the ARR38-C SEI-CERT rule.
void ARR38_C_F(FILE *file) {
enum { BUFFER_SIZE = 1024 };
wchar_t wbuf[BUFFER_SIZE]; // bugpath-note{{'wbuf' initialized here}}

const size_t size = sizeof(*wbuf); // bugpath-note{{'size' initialized to}}
const size_t nitems = sizeof(wbuf); // bugpath-note{{'nitems' initialized to}}

// The 3rd parameter should be the number of elements to read, not
// the size in bytes.
fread(wbuf, size, nitems, file); // \
// report-warning{{Function argument constraint is not satisfied}} \
// report-note{{}} \
// bugpath-warning{{Function argument constraint is not satisfied}} \
// bugpath-note{{}} \
// bugpath-note{{Function argument constraint is not satisfied}}
}

0 comments on commit faffbca

Please sign in to comment.