Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
176 changes: 107 additions & 69 deletions clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,59 +179,6 @@ 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 {

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();
QualType T = ParamDecl->getType()->getPointeeType();
if (T->isVoidType())
T = C.getASTContext().CharTy;
const SVal PSV = State->getSVal(SValMemRegion, T);
bool IsUndef = PSV.isUndef();
if (auto LCV = PSV.getAs<nonloc::LazyCompoundVal>())
IsUndef = LCV->getStore() == nullptr;
if (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:
Expand Down Expand Up @@ -272,9 +219,115 @@ class FindUninitializedField {

return false;
}

void printFieldChain(llvm::raw_ostream &OS) {
if (FieldChain.size() == 1)
OS << " (e.g., field: '" << *FieldChain[0] << "')";
else {
OS << " (e.g., via the field chain: '";
bool First = true;
for (SmallVectorImpl<const FieldDecl *>::iterator DI = FieldChain.begin(),
DE = FieldChain.end();
DI != DE; ++DI) {
if (First)
First = false;
else
OS << '.';
OS << **DI;
}
OS << "')";
}
}
};
} // namespace

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()) {
SmallString<200> Buf;
llvm::raw_svector_ostream Os(Buf);
Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
<< " function call argument is ";
if (ParamT->isPointerType())
Os << "a pointer to uninitialized value";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be "a const pointer to uninitialized value" because the constness of the pointer is the reason why this is a bug.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can change the text (it was part of the existing code).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(The plan is to extend this check to functions that have in-out parameters which must be initialized.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, sorry, I didn't notice that this message was already part of the existing code.

Then I think the best approach would be keeping the message text unchanged in this commit, and updating it in a later follow-up change.

else
Os << "an uninitialized value";
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;
}

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()) {
SmallString<512> Buf;
llvm::raw_svector_ostream Os(Buf);
Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
<< " function call argument";
if (ParamT->isPointerType())
Os << " points to";
else
Os << " references";
Os << " an uninitialized value";

F.printFieldChain(Os);

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;
}

bool CallAndMessageChecker::PreVisitProcessArg(
CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx,
int ArgumentNumber, bool CheckUninitFields, const CallEvent &Call,
Expand Down Expand Up @@ -320,22 +373,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(
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 << "')";
}
F.printFieldChain(os);

// Generate a report for this bug.
auto R = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
Expand Down
4 changes: 2 additions & 2 deletions clang/test/Analysis/PR40625.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
void f(const int *end);

void g(const int (&arrr)[10]) {
f(arrr);
f(arrr); // expected-warning{{1st function call argument is a pointer to uninitialized value}}
}

void h() {
int arr[10];

g(arr); // expected-warning{{1st function call argument is an uninitialized value}}
g(arr);
}
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);
}
18 changes: 17 additions & 1 deletion clang/test/Analysis/call-and-message.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,23 @@ 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 is a pointer to uninitialized value [core.CallAndMessage]}}
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
Expand Down
Loading