Skip to content

Summary #5427

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open

Summary #5427

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,11 @@ def StdCLibraryFunctionsChecker : Checker<"StdCLibraryFunctions">,
"If set to true, the checker models functions from the "
"POSIX standard.",
"false",
InAlpha>,
CmdLineOption<String,
"SummaryConfigPath",
"The path of the config file in YAML format",
"",
InAlpha>
]>,
Documentation<NotDocumented>,
Expand Down
188 changes: 186 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 @@ -683,6 +774,7 @@ class StdLibraryFunctionsChecker
bool DisplayLoadedSummaries = false;
bool ModelPOSIX = false;
bool ShouldAssumeControlledEnvironment = false;
std::string SummaryConfigPath;

private:
Optional<Summary> findFunctionSummary(const FunctionDecl *FD,
Expand Down Expand Up @@ -1357,6 +1449,96 @@ 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{"SummaryConfigPath"};
Mgr->getAnalyzerOptions().getCheckerStringOption(this, Option);
llvm::Optional<SummaryConfiguration> Config =
getConfiguration<SummaryConfiguration>(*Mgr, this, Option, SummaryConfigPath);
llvm::errs() << "Config :" << Config.has_value() << "\n";

auto GetTypeFromStr = [&](StringRef TypeName) {
Optional<QualType> LType = lookupTy(TypeName);
if (LType)
return *LType;

return llvm::StringSwitch<QualType>(TypeName)
.Case("void *", VoidPtrTy)
.Case("void * restrict", VoidPtrRestrictTy)
.Default(Irrelevant);
};

if (Config.has_value()) {
for (const SummaryConfiguration::Summary &s : Config->summaries) {
ArgTypes Args;
for (const std::string &TypeName : s.signature.argTypes) {
llvm::errs() << "arg type string:" << TypeName << "\n";
QualType Type = GetTypeFromStr(TypeName);
llvm::errs() << "arg type dump:";
Type.dump();
llvm::errs() << "\n";

Args.push_back(Type);
}

const std::string &RetTypeName = s.signature.returnType;
llvm::errs() << "ret type string:" << RetTypeName << "\n";
QualType RetType = GetTypeFromStr(RetTypeName);
llvm::errs() << "ret type dump:";

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, RetType), 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 +1773,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 @@ -2995,6 +3177,8 @@ void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) {
Checker->ModelPOSIX = Opts.getCheckerBooleanOption(Checker, "ModelPOSIX");
Checker->ShouldAssumeControlledEnvironment =
Opts.ShouldAssumeControlledEnvironment;
Checker->SummaryConfigPath =
Opts.getCheckerStringOption(Checker, "SummaryConfigPath");
}

bool ento::shouldRegisterStdCLibraryFunctionsChecker(
Expand Down
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:
- "void*"
- "size_t"
- "size_t"
- "FILE*"
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
37 changes: 37 additions & 0 deletions clang/test/Analysis/Inputs/isalnum-summary.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#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"]]
72 changes: 72 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,72 @@
// 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-config apiModeling.StdCLibraryFunctions:SummaryConfigPath="%S/Inputs/fread-summary.yaml" \
// 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-config apiModeling.StdCLibraryFunctions:SummaryConfigPath="%S/Inputs/fread-summary.yaml" \
// 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}}
}