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