[analyzer] do not crash on cases where an array subscript is an rvalue

Array subscript is almost always an lvalue, except for a few cases where
it is not, such as a subscript into an Objective-C property, or a
return from the function.
This commit prevents crashing in such cases.

Fixes rdar://34829842

Differential Revision: https://reviews.llvm.org/D40584

llvm-svn: 319834
This commit is contained in:
George Karpenkov 2017-12-05 21:19:59 +00:00
parent d495301414
commit 8d345cb8a5
3 changed files with 60 additions and 14 deletions

View File

@ -332,9 +332,9 @@ public:
void Visit(const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet &Dst);
/// VisitArraySubscriptExpr - Transfer function for array accesses.
void VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *Ex,
ExplodedNode *Pred,
ExplodedNodeSet &Dst);
void VisitArraySubscriptExpr(const ArraySubscriptExpr *Ex,
ExplodedNode *Pred,
ExplodedNodeSet &Dst);
/// VisitGCCAsmStmt - Transfer function logic for inline asm.
void VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred,

View File

@ -1150,7 +1150,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::ArraySubscriptExprClass:
Bldr.takeNodes(Pred);
VisitLvalArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Pred, Dst);
VisitArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Pred, Dst);
Bldr.addNodes(Dst);
break;
@ -2126,10 +2126,9 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
}
/// VisitArraySubscriptExpr - Transfer function for array accesses
void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A,
void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A,
ExplodedNode *Pred,
ExplodedNodeSet &Dst){
const Expr *Base = A->getBase()->IgnoreParens();
const Expr *Idx = A->getIdx()->IgnoreParens();
@ -2138,18 +2137,32 @@ void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A,
ExplodedNodeSet EvalSet;
StmtNodeBuilder Bldr(CheckerPreStmt, EvalSet, *currBldrCtx);
assert(A->isGLValue() ||
(!AMgr.getLangOpts().CPlusPlus &&
A->getType().isCForbiddenLValueType()));
bool IsVectorType = A->getBase()->getType()->isVectorType();
// The "like" case is for situations where C standard prohibits the type to
// be an lvalue, e.g. taking the address of a subscript of an expression of
// type "void *".
bool IsGLValueLike = A->isGLValue() ||
(A->getType().isCForbiddenLValueType() && !AMgr.getLangOpts().CPlusPlus);
for (auto *Node : CheckerPreStmt) {
const LocationContext *LCtx = Node->getLocationContext();
ProgramStateRef state = Node->getState();
SVal V = state->getLValue(A->getType(),
state->getSVal(Idx, LCtx),
state->getSVal(Base, LCtx));
Bldr.generateNode(A, Node, state->BindExpr(A, LCtx, V), nullptr,
ProgramPoint::PostLValueKind);
if (IsGLValueLike) {
SVal V = state->getLValue(A->getType(),
state->getSVal(Idx, LCtx),
state->getSVal(Base, LCtx));
Bldr.generateNode(A, Node, state->BindExpr(A, LCtx, V), nullptr,
ProgramPoint::PostLValueKind);
} else if (IsVectorType) {
// FIXME: non-glvalue vector reads are not modelled.
Bldr.generateNode(A, Node, state, nullptr);
} else {
llvm_unreachable("Array subscript should be an lValue when not \
a vector and not a forbidden lvalue type");
}
}
getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, A, *this);

View File

@ -2,6 +2,7 @@
typedef int __attribute__((ext_vector_type(2))) V;
void clang_analyzer_warnIfReached();
void clang_analyzer_numTimesReached();
void clang_analyzer_eval(int);
@ -26,3 +27,35 @@ V dont_crash_and_dont_split_state(V x, V y) {
clang_analyzer_numTimesReached(); // expected-warning{{2}}
return z;
}
void test_read() {
V x;
x[0] = 0;
x[1] = 1;
clang_analyzer_eval(x[0] == 0); // expected-warning{{TRUE}}
}
V return_vector() {
V z;
z[0] = 0;
z[1] = 0;
return z;
}
int test_vector_access() {
return return_vector()[0]; // no-crash no-warning
}
@interface I
@property V v;
@end
// Do not crash on subscript operations into ObjC properties.
int myfunc(I *i2) {
int out = i2.v[0]; // no-crash no-warning
// Check that the analysis continues.
clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
return out;
}