Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
175 changes: 105 additions & 70 deletions clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/raw_ostream.h"

using namespace clang;
Expand Down Expand Up @@ -179,59 +180,11 @@ static void describeUninitializedArgumentInCall(const CallEvent &Call,
}
}

bool CallAndMessageChecker::uninitRefOrPointer(
CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx,
const BugType &BT, const ParmVarDecl *ParamDecl, int ArgumentNumber) const {

// The pointee being uninitialized is a sign of code smell, not a bug, no need
// to sink here.
if (!ChecksEnabled[CK_ArgPointeeInitializedness])
return false;

// No parameter declaration available, i.e. variadic function argument.
if(!ParamDecl)
return false;

// If parameter is declared as pointer to const in function declaration,
// then check if corresponding argument in function call is
// pointing to undefined symbol value (uninitialized memory).
SmallString<200> Buf;
llvm::raw_svector_ostream Os(Buf);

if (ParamDecl->getType()->isPointerType()) {
Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
<< " function call argument is a pointer to uninitialized value";
} else if (ParamDecl->getType()->isReferenceType()) {
Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
<< " function call argument is an uninitialized value";
} else
return false;

if(!ParamDecl->getType()->getPointeeType().isConstQualified())
return false;

if (const MemRegion *SValMemRegion = V.getAsRegion()) {
const ProgramStateRef State = C.getState();
const SVal PSV = State->getSVal(SValMemRegion, C.getASTContext().CharTy);
if (PSV.isUndef()) {
if (ExplodedNode *N = C.generateErrorNode()) {
auto R = std::make_unique<PathSensitiveBugReport>(BT, Os.str(), N);
R->addRange(ArgRange);
if (ArgEx)
bugreporter::trackExpressionValue(N, ArgEx, *R);

C.emitReport(std::move(R));
}
return true;
}
}
return false;
}

namespace {
class FindUninitializedField {
public:
SmallVector<const FieldDecl *, 10> FieldChain;
using FieldChainTy = SmallVector<const FieldDecl *, 10>;
FieldChainTy FieldChain;

private:
StoreManager &StoreMgr;
Expand Down Expand Up @@ -271,6 +224,104 @@ class FindUninitializedField {
};
} // namespace

namespace llvm {
template <> struct format_provider<FindUninitializedField::FieldChainTy> {
static void format(const FindUninitializedField::FieldChainTy &V,
raw_ostream &Stream, StringRef Style) {
if (V.size() == 1)
Stream << "(e.g., field: '" << *V[0] << "')";
else {
Stream << "(e.g., via the field chain: '";
bool First = true;
for (FindUninitializedField::FieldChainTy::const_iterator DI = V.begin(),
DE = V.end();
DI != DE; ++DI) {
if (First)
First = false;
else
Stream << '.';
Stream << **DI;
}
Stream << "')";
}
}
};
} // namespace llvm

bool CallAndMessageChecker::uninitRefOrPointer(
CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx,
const BugType &BT, const ParmVarDecl *ParamDecl, int ArgumentNumber) const {

if (!ChecksEnabled[CK_ArgPointeeInitializedness])
return false;

// No parameter declaration available, i.e. variadic function argument.
if (!ParamDecl)
return false;

QualType ParamT = ParamDecl->getType();
if (!ParamT->isPointerOrReferenceType())
return false;

QualType PointeeT = ParamT->getPointeeType();
if (!PointeeT.isConstQualified())
return false;

const MemRegion *SValMemRegion = V.getAsRegion();
if (!SValMemRegion)
return false;

// If parameter is declared as pointer to const in function declaration,
// then check if corresponding argument in function call is
// pointing to undefined symbol value (uninitialized memory).

const ProgramStateRef State = C.getState();
if (PointeeT->isVoidType())
PointeeT = C.getASTContext().CharTy;
const SVal PointeeV = State->getSVal(SValMemRegion, PointeeT);

if (PointeeV.isUndef()) {
if (ExplodedNode *N = C.generateErrorNode()) {
std::string Msg = llvm::formatv(
"{0}{1} function call argument is {2} uninitialized value",
ArgumentNumber + 1, llvm::getOrdinalSuffix(ArgumentNumber + 1),
ParamT->isPointerType() ? "a pointer to" : "an");
auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
R->addRange(ArgRange);
if (ArgEx)
bugreporter::trackExpressionValue(N, ArgEx, *R);

C.emitReport(std::move(R));
}
return true;
}

if (auto LV = PointeeV.getAs<nonloc::LazyCompoundVal>()) {
const LazyCompoundValData *D = LV->getCVData();
FindUninitializedField F(C.getState()->getStateManager().getStoreManager(),
C.getSValBuilder().getRegionManager(),
D->getStore());

if (F.Find(D->getRegion())) {
if (ExplodedNode *N = C.generateErrorNode()) {
std::string Msg = llvm::formatv(
"{0}{1} function call argument {2} an uninitialized value {3}",
(ArgumentNumber + 1), llvm::getOrdinalSuffix(ArgumentNumber + 1),
ParamT->isPointerType() ? "points to" : "references", F.FieldChain);
auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
R->addRange(ArgRange);
if (ArgEx)
bugreporter::trackExpressionValue(N, ArgEx, *R);

C.emitReport(std::move(R));
}
return true;
}
}

return false;
}

bool CallAndMessageChecker::PreVisitProcessArg(
CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx,
int ArgumentNumber, bool CheckUninitFields, const CallEvent &Call,
Expand Down Expand Up @@ -313,28 +364,12 @@ bool CallAndMessageChecker::PreVisitProcessArg(
return true;
}
if (ExplodedNode *N = C.generateErrorNode()) {
SmallString<512> Str;
llvm::raw_svector_ostream os(Str);
os << "Passed-by-value struct argument contains uninitialized data";

if (F.FieldChain.size() == 1)
os << " (e.g., field: '" << *F.FieldChain[0] << "')";
else {
os << " (e.g., via the field chain: '";
bool first = true;
for (SmallVectorImpl<const FieldDecl *>::iterator
DI = F.FieldChain.begin(), DE = F.FieldChain.end(); DI!=DE;++DI){
if (first)
first = false;
else
os << '.';
os << **DI;
}
os << "')";
}
std::string Msg = llvm::formatv(
"Passed-by-value struct argument contains uninitialized data {0}",
F.FieldChain);

// Generate a report for this bug.
auto R = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
R->addRange(ArgRange);

if (ArgEx)
Expand Down
78 changes: 78 additions & 0 deletions clang/test/Analysis/call-and-message-cpp.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// RUN: %clang_analyze_cc1 %s -verify \
// RUN: -analyzer-checker=core \
// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=true \
// RUN: -analyzer-config core.CallAndMessage:ArgInitializedness=false

struct S1 {
char c;
};

struct S {
int a;
S1 b;
};

S GlobalS;

void doStuffP(const S *);
void doStuffR(const S &);

void uninit_val_p() {
S s;
doStuffP(&s); // expected-warning{{1st function call argument points to an uninitialized value (e.g., field: 'a')}}
}

void uninit_val_r() {
S s;
s.a = 0;
doStuffR(s); // expected-warning{{1st function call argument references an uninitialized value (e.g., via the field chain: 'b.c')}}
}

S *uninit_new() {
S *s = new S;
doStuffP(s); // expected-warning{{1st function call argument points to an uninitialized value (e.g., field: 'a')}}
return s;
}

void uninit_ctr() {
S s = S();
doStuffP(&s);
}

void uninit_init() {
S s{};
doStuffP(&s);
}

void uninit_init_val() {
S s{1, {2}};
doStuffP(&s);
}

void uninit_parm_ptr(S *s) {
doStuffP(s);
}

void uninit_parm_val(S s) {
doStuffP(&s);
}

void uninit_parm_ref(S &s) {
doStuffP(&s);
}

void init_val() {
S s;
s.a = 1;
s.b.c = 1;
doStuffP(&s);
}

void uninit_global() {
doStuffP(&GlobalS);
}

void uninit_static() {
static S s;
doStuffP(&s);
}
28 changes: 28 additions & 0 deletions clang/test/Analysis/call-and-message.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,34 @@ void pointee_uninit(void) {
doStuff_pointerToConstInt(p); // expected-warning{{1st function call argument is a pointer to uninitialized value [core.CallAndMessage]}}
}

typedef struct S {
int a;
short b;
} S;

void doStuff_pointerToConstStruct(const S *s){};
void pointee_uninit_struct(void) {
S s;
S *p = &s;
doStuff_pointerToConstStruct(p); // expected-warning{{1st function call argument points to an uninitialized value (e.g., field: 'a') [core.CallAndMessage]}}
}
void pointee_uninit_struct_1(void) {
S s;
s.a = 2;
doStuff_pointerToConstStruct(&s); // expected-warning{{1st function call argument points to an uninitialized value (e.g., field: 'b') [core.CallAndMessage]}}
}
void pointee_uninit_struct_2(void) {
S s = {};
doStuff_pointerToConstStruct(&s);
}
void pointee_uninit_struct_3(S *s) {
doStuff_pointerToConstStruct(s);
}
void pointee_uninit_struct_4(void) {
S s = {1, 2};
doStuff_pointerToConstStruct(&s);
}

// TODO: If this hash ever changes, turn
// core.CallAndMessage:ArgPointeeInitializedness from a checker option into a
// checker, as described in the CallAndMessage comments!
Expand Down