r201583 - [analyzer] Teach CastSizeChecker about flexible array members.
Jordan Rose
jordan_rose at apple.com
Tue Feb 18 09:06:31 PST 2014
Author: jrose
Date: Tue Feb 18 11:06:30 2014
New Revision: 201583
URL: http://llvm.org/viewvc/llvm-project?rev=201583&view=rev
Log:
[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!
Modified:
cfe/trunk/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
cfe/trunk/test/Analysis/malloc.c
cfe/trunk/test/Analysis/no-outofbounds.c
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp?rev=201583&r1=201582&r2=201583&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp Tue Feb 18 11:06:30 2014
@@ -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 {
const Expr *E = CE->getSubExpr();
ASTContext &Ctx = C.getASTContext();
@@ -66,18 +124,20 @@ void CastSizeChecker::checkPreStmt(const
if (typeSize.isZero())
return;
- if (regionSize % typeSize != 0) {
- if (ExplodedNode *errorNode = C.generateSink()) {
- if (!BT)
- BT.reset(
- new BuiltinBug(this, "Cast region with wrong size.",
- "Cast a region whose size is not a multiple of the"
- " destination type size."));
- BugReport *R = new BugReport(*BT, BT->getDescription(),
- errorNode);
- R->addRange(CE->getSourceRange());
- C.emitReport(R);
- }
+ if (regionSize % typeSize == 0)
+ return;
+
+ if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy))
+ return;
+
+ if (ExplodedNode *errorNode = C.generateSink()) {
+ if (!BT)
+ BT.reset(new BuiltinBug(this, "Cast region with wrong size.",
+ "Cast a region whose size is not a multiple"
+ " of the destination type size."));
+ BugReport *R = new BugReport(*BT, BT->getDescription(), errorNode);
+ R->addRange(CE->getSourceRange());
+ C.emitReport(R);
}
}
Modified: cfe/trunk/test/Analysis/malloc.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/malloc.c?rev=201583&r1=201582&r2=201583&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/malloc.c (original)
+++ cfe/trunk/test/Analysis/malloc.c Tue Feb 18 11:06:30 2014
@@ -270,6 +270,222 @@ void PR7217() {
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 *p = malloc(2);
const void *cp = p; // not crash
Modified: cfe/trunk/test/Analysis/no-outofbounds.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/no-outofbounds.c?rev=201583&r1=201582&r2=201583&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/no-outofbounds.c (original)
+++ cfe/trunk/test/Analysis/no-outofbounds.c Tue Feb 18 11:06:30 2014
@@ -1,4 +1,5 @@
// 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.
@@ -24,8 +25,7 @@ void free(void *);
void field() {
struct vec { size_t len; int data[0]; };
- // FIXME: Not warn for this.
- struct vec *a = malloc(sizeof(struct vec) + 10); // expected-warning {{Cast a region whose size is not a multiple of the destination type size}}
+ struct vec *a = malloc(sizeof(struct vec) + 10*sizeof(int));
a->len = 10;
a->data[1] = 5; // no-warning
free(a);
More information about the cfe-commits
mailing list