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
12 changes: 8 additions & 4 deletions clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,6 @@ 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;

Expand Down Expand Up @@ -212,8 +210,14 @@ bool CallAndMessageChecker::uninitRefOrPointer(

if (const MemRegion *SValMemRegion = V.getAsRegion()) {
const ProgramStateRef State = C.getState();
const SVal PSV = State->getSVal(SValMemRegion, C.getASTContext().CharTy);
if (PSV.isUndef()) {
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;
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 did not find information about if this is the correct way of detecting that the value is not initialized.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Certainly more conditions are needed, the current version is LCV->getStore() == nullptr && isa<NonParamVarRegion>(LCV->getRegion()). Still at some C++ cases (for example memory allocated with new) the uninitialized struct is still not detected.

Copy link
Contributor

Choose a reason for hiding this comment

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

There is no way you can ask for the underlying value of an LCV, so by extension you also can't detect if it's uninitialized or not. Here you would need to know if any of the bindings in the cluster of the region of the LCV in the Store of the LCV refers to undef.
That can be undef for 2 reasons: 1) the default binding at least partially covering that region is undef., or 2) there is a direct binding of Undef that at least partially overlaps with that region.

iterBindings could give you the list of direct bindings, but I don't think there is a way to query a default binding. NVM, there is one api, getDefaultBinding. But I start to really hate these LCVs and the regionstore.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Is it possible that there is no binding at all? Even with iterBindings over subregions and super-regions, no binding is found. LazyCoupoundValue is not necessary for this search (memregion and store is available from elsewhere). The LCV can contain a null Store pointer (and exactly this case looks interesting).

Copy link
Contributor

Choose a reason for hiding this comment

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

If there is no cluster for the region, than it means that we saw no stores to that place.
Reading from such a place might be still valid for global storage duration objects such as: globals, local statics, thread local objects. This can be inferred from the memory space associated with the region.

if (IsUndef) {
if (ExplodedNode *N = C.generateErrorNode()) {
auto R = std::make_unique<PathSensitiveBugReport>(BT, Os.str(), N);
R->addRange(ArgRange);
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); // expected-warning{{1st function call argument is a pointer to uninitialized value}}
f(arrr);
}

void h() {
int arr[10];

g(arr);
g(arr); // expected-warning{{1st function call argument is an uninitialized value}}
}
12 changes: 12 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,18 @@ 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 is a pointer to uninitialized value [core.CallAndMessage]}}
Copy link
Contributor

Choose a reason for hiding this comment

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

Do I understand it correctly that this testcase wouldn't pass without your change in the checker?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, this was not detected before.

}

// 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