forked from OSchip/llvm-project
[analyzer] Fix false positives in inner pointer checker (PR49628)
This patch supports std::data and std::addressof functions. rdar://73463300 Differential Revision: https://reviews.llvm.org/D99260
This commit is contained in:
parent
4b958dd6bc
commit
663ac91ed1
|
@ -34,9 +34,9 @@ namespace {
|
||||||
class InnerPointerChecker
|
class InnerPointerChecker
|
||||||
: public Checker<check::DeadSymbols, check::PostCall> {
|
: public Checker<check::DeadSymbols, check::PostCall> {
|
||||||
|
|
||||||
CallDescription AppendFn, AssignFn, ClearFn, CStrFn, DataFn, EraseFn,
|
CallDescription AppendFn, AssignFn, AddressofFn, ClearFn, CStrFn, DataFn,
|
||||||
InsertFn, PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn,
|
DataMemberFn, EraseFn, InsertFn, PopBackFn, PushBackFn, ReplaceFn,
|
||||||
ShrinkToFitFn, SwapFn;
|
ReserveFn, ResizeFn, ShrinkToFitFn, SwapFn;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class InnerPointerBRVisitor : public BugReporterVisitor {
|
class InnerPointerBRVisitor : public BugReporterVisitor {
|
||||||
|
@ -73,9 +73,10 @@ public:
|
||||||
InnerPointerChecker()
|
InnerPointerChecker()
|
||||||
: AppendFn({"std", "basic_string", "append"}),
|
: AppendFn({"std", "basic_string", "append"}),
|
||||||
AssignFn({"std", "basic_string", "assign"}),
|
AssignFn({"std", "basic_string", "assign"}),
|
||||||
|
AddressofFn({"std", "addressof"}),
|
||||||
ClearFn({"std", "basic_string", "clear"}),
|
ClearFn({"std", "basic_string", "clear"}),
|
||||||
CStrFn({"std", "basic_string", "c_str"}),
|
CStrFn({"std", "basic_string", "c_str"}), DataFn({"std", "data"}, 1),
|
||||||
DataFn({"std", "basic_string", "data"}),
|
DataMemberFn({"std", "basic_string", "data"}),
|
||||||
EraseFn({"std", "basic_string", "erase"}),
|
EraseFn({"std", "basic_string", "erase"}),
|
||||||
InsertFn({"std", "basic_string", "insert"}),
|
InsertFn({"std", "basic_string", "insert"}),
|
||||||
PopBackFn({"std", "basic_string", "pop_back"}),
|
PopBackFn({"std", "basic_string", "pop_back"}),
|
||||||
|
@ -90,6 +91,9 @@ public:
|
||||||
/// pointers referring to the container object's inner buffer.
|
/// pointers referring to the container object's inner buffer.
|
||||||
bool isInvalidatingMemberFunction(const CallEvent &Call) const;
|
bool isInvalidatingMemberFunction(const CallEvent &Call) const;
|
||||||
|
|
||||||
|
/// Check whether the called function returns a raw inner pointer.
|
||||||
|
bool isInnerPointerAccessFunction(const CallEvent &Call) const;
|
||||||
|
|
||||||
/// Mark pointer symbols associated with the given memory region released
|
/// Mark pointer symbols associated with the given memory region released
|
||||||
/// in the program state.
|
/// in the program state.
|
||||||
void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State,
|
void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State,
|
||||||
|
@ -130,6 +134,12 @@ bool InnerPointerChecker::isInvalidatingMemberFunction(
|
||||||
Call.isCalled(SwapFn));
|
Call.isCalled(SwapFn));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InnerPointerChecker::isInnerPointerAccessFunction(
|
||||||
|
const CallEvent &Call) const {
|
||||||
|
return (Call.isCalled(CStrFn) || Call.isCalled(DataFn) ||
|
||||||
|
Call.isCalled(DataMemberFn));
|
||||||
|
}
|
||||||
|
|
||||||
void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call,
|
void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call,
|
||||||
ProgramStateRef State,
|
ProgramStateRef State,
|
||||||
const MemRegion *MR,
|
const MemRegion *MR,
|
||||||
|
@ -172,6 +182,11 @@ void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call,
|
||||||
if (!ArgRegion)
|
if (!ArgRegion)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// std::addressof function accepts a non-const reference as an argument,
|
||||||
|
// but doesn't modify it.
|
||||||
|
if (Call.isCalled(AddressofFn))
|
||||||
|
continue;
|
||||||
|
|
||||||
markPtrSymbolsReleased(Call, State, ArgRegion, C);
|
markPtrSymbolsReleased(Call, State, ArgRegion, C);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,14 +210,33 @@ void InnerPointerChecker::checkPostCall(const CallEvent &Call,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
ProgramStateRef State = C.getState();
|
ProgramStateRef State = C.getState();
|
||||||
|
|
||||||
if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) {
|
|
||||||
// TODO: Do we need these to be typed?
|
// TODO: Do we need these to be typed?
|
||||||
const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>(
|
const TypedValueRegion *ObjRegion = nullptr;
|
||||||
|
|
||||||
|
if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) {
|
||||||
|
ObjRegion = dyn_cast_or_null<TypedValueRegion>(
|
||||||
ICall->getCXXThisVal().getAsRegion());
|
ICall->getCXXThisVal().getAsRegion());
|
||||||
|
|
||||||
|
// Check [string.require] / second point.
|
||||||
|
if (isInvalidatingMemberFunction(Call)) {
|
||||||
|
markPtrSymbolsReleased(Call, State, ObjRegion, C);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInnerPointerAccessFunction(Call)) {
|
||||||
|
|
||||||
|
if (isa<SimpleFunctionCall>(Call)) {
|
||||||
|
// NOTE: As of now, we only have one free access function: std::data.
|
||||||
|
// If we add more functions like this in the list, hardcoded
|
||||||
|
// argument index should be changed.
|
||||||
|
ObjRegion =
|
||||||
|
dyn_cast_or_null<TypedValueRegion>(Call.getArgSVal(0).getAsRegion());
|
||||||
|
}
|
||||||
|
|
||||||
if (!ObjRegion)
|
if (!ObjRegion)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) {
|
|
||||||
SVal RawPtr = Call.getReturnValue();
|
SVal RawPtr = Call.getReturnValue();
|
||||||
if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) {
|
if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) {
|
||||||
// Start tracking this raw pointer by adding it to the set of symbols
|
// Start tracking this raw pointer by adding it to the set of symbols
|
||||||
|
@ -217,15 +251,9 @@ void InnerPointerChecker::checkPostCall(const CallEvent &Call,
|
||||||
State = State->set<RawPtrMap>(ObjRegion, Set);
|
State = State->set<RawPtrMap>(ObjRegion, Set);
|
||||||
C.addTransition(State);
|
C.addTransition(State);
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check [string.require] / second point.
|
|
||||||
if (isInvalidatingMemberFunction(Call)) {
|
|
||||||
markPtrSymbolsReleased(Call, State, ObjRegion, C);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Check [string.require] / first point.
|
// Check [string.require] / first point.
|
||||||
checkFunctionArguments(Call, State, C);
|
checkFunctionArguments(Call, State, C);
|
||||||
|
|
|
@ -17,6 +17,11 @@ void func_value(T a);
|
||||||
string my_string = "default";
|
string my_string = "default";
|
||||||
void default_arg(int a = 42, string &b = my_string);
|
void default_arg(int a = 42, string &b = my_string);
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
T *addressof(T &arg);
|
||||||
|
|
||||||
|
char *data(std::string &c);
|
||||||
|
|
||||||
} // end namespace std
|
} // end namespace std
|
||||||
|
|
||||||
void consume(const char *) {}
|
void consume(const char *) {}
|
||||||
|
@ -273,6 +278,15 @@ void deref_after_swap() {
|
||||||
// expected-note@-1 {{Inner pointer of container used after re/deallocation}}
|
// expected-note@-1 {{Inner pointer of container used after re/deallocation}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void deref_after_std_data() {
|
||||||
|
const char *c;
|
||||||
|
std::string s;
|
||||||
|
c = std::data(s); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
|
||||||
|
s.push_back('c'); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'push_back'}}
|
||||||
|
consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}}
|
||||||
|
// expected-note@-1 {{Inner pointer of container used after re/deallocation}}
|
||||||
|
}
|
||||||
|
|
||||||
struct S {
|
struct S {
|
||||||
std::string s;
|
std::string s;
|
||||||
const char *name() {
|
const char *name() {
|
||||||
|
@ -361,8 +375,24 @@ void func_default_arg() {
|
||||||
// expected-note@-1 {{Inner pointer of container used after re/deallocation}}
|
// expected-note@-1 {{Inner pointer of container used after re/deallocation}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void func_addressof() {
|
||||||
|
const char *c;
|
||||||
|
std::string s;
|
||||||
|
c = s.c_str();
|
||||||
|
addressof(s);
|
||||||
|
consume(c); // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
void func_std_data() {
|
||||||
|
const char *c;
|
||||||
|
std::string s;
|
||||||
|
c = std::data(s);
|
||||||
|
consume(c); // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
struct T {
|
struct T {
|
||||||
std::string to_string() { return s; }
|
std::string to_string() { return s; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string s;
|
std::string s;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue