Hi,

I got a false positive at work and this patch fixes it. CastSizeChecker
will now handle structs ending with a flexible array. It will no longer
warn when you allocate sizeof the struct + a multiple of the array's
element size.

Best regards,
Daniel Fahlgren
Index: lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp	(revision 201043)
+++ lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp	(working copy)
@@ -29,6 +29,61 @@
 };
 }
 
+/// Check if we are casting to a struct with a flexible array at the end.
+///
+/// struct foo {
+///   size_t len;
+///   struct bar data[];
+/// };
+///
+/// or
+///
+/// struct foo {
+///   size_t len;
+///   struct bar data[0];
+/// }
+///
+/// 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) {
+  CharUnits left = regionSize - typeSize;
+  if (!left.isPositive())
+    return false;
+
+  const RecordType *RT = ToPointeeTy.getTypePtr()->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());
+  FieldDecl *last = 0;
+  for (; iter != end; ++iter)
+    last = *iter;
+
+  const Type *elemType = last->getType()->getArrayElementTypeNoTypeQual();
+  CharUnits flexSize;
+  if (const ConstantArrayType *ArrayTy =
+      Ctx.getAsConstantArrayType(last->getType())) {
+    if (ArrayTy->getSize() != 0)
+      return false;
+    flexSize = Ctx.getTypeSizeInChars(elemType);
+  } else if (RD->hasFlexibleArrayMember()) {
+    flexSize = Ctx.getTypeSizeInChars(elemType);
+  } else {
+    return false;
+  }
+
+  if (flexSize.isZero())
+    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,17 +121,21 @@
   if (typeSize.isZero())
     return;
 
-  if (regionSize % typeSize != 0) {
-    if (ExplodedNode *errorNode = C.generateSink()) {
-      if (!BT)
-        BT.reset(new BuiltinBug("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("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);
   }
 }
 
Index: test/Analysis/no-outofbounds.c
===================================================================
--- test/Analysis/no-outofbounds.c	(revision 201043)
+++ test/Analysis/no-outofbounds.c	(working copy)
@@ -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 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);
Index: test/Analysis/malloc.c
===================================================================
--- test/Analysis/malloc.c	(revision 201043)
+++ test/Analysis/malloc.c	(working copy)
@@ -270,6 +270,118 @@
   buf[1] = 'c'; // not crash
 }
 
+void cast_struct() {
+  struct st {
+    int i[100];
+    char j[2];
+  };
+
+  struct st *s = malloc(sizeof(struct st)); // no-warning
+  free(s);
+}
+
+void cast_struct_warn() {
+  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_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 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_4() {
+  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_5() {
+  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_6() {
+  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_1() {
+  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_2() {
+  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 mallocCastToVoid() {
   void *p = malloc(2);
   const void *cp = p; // not crash
Index: test/Analysis/malloc-annotations.c
===================================================================
--- test/Analysis/malloc-annotations.c	(revision 201043)
+++ test/Analysis/malloc-annotations.c	(working copy)
@@ -216,6 +216,118 @@
   buf[1] = 'c'; // not crash
 }
 
+void cast_struct() {
+  struct st {
+    int i[100];
+    char j[2];
+  };
+
+  struct st *s = malloc(sizeof(struct st)); // no-warning
+  free(s);
+}
+
+void cast_struct_warn() {
+  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_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 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_4() {
+  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_5() {
+  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_6() {
+  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_1() {
+  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_2() {
+  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 mallocCastToVoid() {
   void *p = malloc(2);
   const void *cp = p; // not crash
_______________________________________________
cfe-commits mailing list
cfe-commits@cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to