Skip to content
Snippets Groups Projects
Commit e1bb9b2c authored by Jordan Rose's avatar Jordan Rose
Browse files

[analyzer] Teach CastSizeChecker about flexible array members.

...as well as fake flexible array members: structs that end in arrays with
length 0 or 1.

Patch by Daniel Fahlgren!

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@201583 91177308-0d34-0410-b5e6-96231b3b80d8
parent 90801c62
No related branches found
No related tags found
No related merge requests found
...@@ -29,6 +29,64 @@ public: ...@@ -29,6 +29,64 @@ public:
}; };
} }
/// Check if we are casting to a struct with a flexible array at the end.
/// \code
/// struct foo {
/// size_t len;
/// struct bar data[];
/// };
/// \endcode
/// or
/// \code
/// struct foo {
/// size_t len;
/// struct bar data[0];
/// }
/// \endcode
/// In these cases it is also valid to allocate size of struct foo + a multiple
/// of struct bar.
static bool evenFlexibleArraySize(ASTContext &Ctx, CharUnits RegionSize,
CharUnits TypeSize, QualType ToPointeeTy) {
const RecordType *RT = ToPointeeTy->getAs<RecordType>();
if (!RT)
return false;
const RecordDecl *RD = RT->getDecl();
RecordDecl::field_iterator Iter(RD->field_begin());
RecordDecl::field_iterator End(RD->field_end());
const FieldDecl *Last = 0;
for (; Iter != End; ++Iter)
Last = *Iter;
assert(Last && "empty structs should already be handled");
const Type *ElemType = Last->getType()->getArrayElementTypeNoTypeQual();
CharUnits FlexSize;
if (const ConstantArrayType *ArrayTy =
Ctx.getAsConstantArrayType(Last->getType())) {
FlexSize = Ctx.getTypeSizeInChars(ElemType);
if (ArrayTy->getSize() == 1 && TypeSize > FlexSize)
TypeSize -= FlexSize;
else if (ArrayTy->getSize() != 0)
return false;
} else if (RD->hasFlexibleArrayMember()) {
FlexSize = Ctx.getTypeSizeInChars(ElemType);
} else {
return false;
}
if (FlexSize.isZero())
return false;
CharUnits Left = RegionSize - TypeSize;
if (Left.isNegative())
return false;
if (Left % FlexSize == 0)
return true;
return false;
}
void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
const Expr *E = CE->getSubExpr(); const Expr *E = CE->getSubExpr();
ASTContext &Ctx = C.getASTContext(); ASTContext &Ctx = C.getASTContext();
...@@ -66,18 +124,20 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { ...@@ -66,18 +124,20 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
if (typeSize.isZero()) if (typeSize.isZero())
return; return;
if (regionSize % typeSize != 0) { if (regionSize % typeSize == 0)
if (ExplodedNode *errorNode = C.generateSink()) { return;
if (!BT)
BT.reset( if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy))
new BuiltinBug(this, "Cast region with wrong size.", return;
"Cast a region whose size is not a multiple of the"
" destination type size.")); if (ExplodedNode *errorNode = C.generateSink()) {
BugReport *R = new BugReport(*BT, BT->getDescription(), if (!BT)
errorNode); BT.reset(new BuiltinBug(this, "Cast region with wrong size.",
R->addRange(CE->getSourceRange()); "Cast a region whose size is not a multiple"
C.emitReport(R); " of the destination type size."));
} BugReport *R = new BugReport(*BT, BT->getDescription(), errorNode);
R->addRange(CE->getSourceRange());
C.emitReport(R);
} }
} }
......
...@@ -270,6 +270,222 @@ void PR7217() { ...@@ -270,6 +270,222 @@ void PR7217() {
buf[1] = 'c'; // not crash buf[1] = 'c'; // not crash
} }
void cast_emtpy_struct() {
struct st {
};
struct st *s = malloc(sizeof(struct st)); // no-warning
free(s);
}
void cast_struct_1() {
struct st {
int i[100];
char j[];
};
struct st *s = malloc(sizeof(struct st)); // no-warning
free(s);
}
void cast_struct_2() {
struct st {
int i[100];
char j[0];
};
struct st *s = malloc(sizeof(struct st)); // no-warning
free(s);
}
void cast_struct_3() {
struct st {
int i[100];
char j[1];
};
struct st *s = malloc(sizeof(struct st)); // no-warning
free(s);
}
void cast_struct_4() {
struct st {
int i[100];
char j[2];
};
struct st *s = malloc(sizeof(struct st)); // no-warning
free(s);
}
void cast_struct_5() {
struct st {
char i[200];
char j[1];
};
struct st *s = malloc(sizeof(struct st) - sizeof(char)); // no-warning
free(s);
}
void cast_struct_warn_1() {
struct st {
int i[100];
char j[2];
};
struct st *s = malloc(sizeof(struct st) + 2); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
free(s);
}
void cast_struct_warn_2() {
struct st {
int i[100];
char j[2];
};
struct st *s = malloc(2); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
free(s);
}
void cast_struct_flex_array_1() {
struct st {
int i[100];
char j[];
};
struct st *s = malloc(sizeof(struct st) + 3); // no-warning
free(s);
}
void cast_struct_flex_array_2() {
struct st {
int i[100];
char j[0];
};
struct st *s = malloc(sizeof(struct st) + 3); // no-warning
free(s);
}
void cast_struct_flex_array_3() {
struct st {
int i[100];
char j[1];
};
struct st *s = malloc(sizeof(struct st) + 3); // no-warning
free(s);
}
void cast_struct_flex_array_4() {
struct foo {
char f[32];
};
struct st {
char i[100];
struct foo data[];
};
struct st *s = malloc(sizeof(struct st) + 3 * sizeof(struct foo)); // no-warning
free(s);
}
void cast_struct_flex_array_5() {
struct foo {
char f[32];
};
struct st {
char i[100];
struct foo data[0];
};
struct st *s = malloc(sizeof(struct st) + 3 * sizeof(struct foo)); // no-warning
free(s);
}
void cast_struct_flex_array_6() {
struct foo {
char f[32];
};
struct st {
char i[100];
struct foo data[1];
};
struct st *s = malloc(sizeof(struct st) + 3 * sizeof(struct foo)); // no-warning
free(s);
}
void cast_struct_flex_array_warn_1() {
struct foo {
char f[32];
};
struct st {
char i[100];
struct foo data[];
};
struct st *s = malloc(3 * sizeof(struct st) + 3 * sizeof(struct foo)); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
free(s);
}
void cast_struct_flex_array_warn_2() {
struct foo {
char f[32];
};
struct st {
char i[100];
struct foo data[0];
};
struct st *s = malloc(3 * sizeof(struct st) + 3 * sizeof(struct foo)); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
free(s);
}
void cast_struct_flex_array_warn_3() {
struct foo {
char f[32];
};
struct st {
char i[100];
struct foo data[1];
};
struct st *s = malloc(3 * sizeof(struct st) + 3 * sizeof(struct foo)); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
free(s);
}
void cast_struct_flex_array_warn_4() {
struct st {
int i[100];
int j[];
};
struct st *s = malloc(sizeof(struct st) + 3); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
free(s);
}
void cast_struct_flex_array_warn_5() {
struct st {
int i[100];
int j[0];
};
struct st *s = malloc(sizeof(struct st) + 3); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
free(s);
}
void cast_struct_flex_array_warn_6() {
struct st {
int i[100];
int j[1];
};
struct st *s = malloc(sizeof(struct st) + 3); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}}
free(s);
}
void mallocCastToVoid() { void mallocCastToVoid() {
void *p = malloc(2); void *p = malloc(2);
const void *cp = p; // not crash const void *cp = p; // not crash
......
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core,alpha.unix,alpha.security.ArrayBound -analyzer-store=region -verify %s // RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core,alpha.unix,alpha.security.ArrayBound -analyzer-store=region -verify %s
// expected-no-diagnostics
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// This file tests cases where we should not flag out-of-bounds warnings. // This file tests cases where we should not flag out-of-bounds warnings.
...@@ -24,8 +25,7 @@ void free(void *); ...@@ -24,8 +25,7 @@ void free(void *);
void field() { void field() {
struct vec { size_t len; int data[0]; }; struct vec { size_t len; int data[0]; };
// FIXME: Not warn for this. struct vec *a = malloc(sizeof(struct vec) + 10*sizeof(int));
struct vec *a = malloc(sizeof(struct vec) + 10); // expected-warning {{Cast a region whose size is not a multiple of the destination type size}}
a->len = 10; a->len = 10;
a->data[1] = 5; // no-warning a->data[1] = 5; // no-warning
free(a); free(a);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment