Hi Jordan,

On Thu, 2014-02-13 at 09:18 -0800, Jordan Rose wrote:
> Nice to see this improvement! Some comments, but mostly just style
> things:

> +/// 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.
> 
> Since these are Doxygen comments, please surround code blocks with
> \code...\endcode.


> +static bool evenFlexibleArraySize(ASTContext &Ctx, CharUnits
> regionSize,
> +                                  CharUnits typeSize, QualType
> ToPointeeTy) {

> LLVM naming conventions require all variables and parameters to start
> with a capital letter. (This occurs throughout the function.)

Should I fix the rest of the file as well or should that be done in a
separate commit (if it should be done at all)?


> +  const RecordType *RT =
> ToPointeeTy.getTypePtr()->getAs<RecordType>();

> QualType has an overloaded operator->, so you can just say
> "ToPointeeTy->getAs<RecordType>()".


> +  FieldDecl *last = 0;

> Within the analyzer, we try to make all AST class pointers const. (I
> actually try to do that everywhere I can. Const-correctness is a nice
> thing.)

> 
> +  for (; iter != end; ++iter)
> +    last = *iter;

> What if the struct is empty?

Then the size of the struct will be 0 or 1, depending on C or C++. Types
with these sizes will be handled in checkPreStmt and never reach
evenFlexibleArraySize. But I agree it is far from obvious and have added
a check to make sure last has been set.
> 
> 
> +  const Type *elemType =
> last->getType()->getArrayElementTypeNoTypeQual();

> It looks like you could move this (and the flexSize calculation) to
> after you've already checked that this record has a flexible or
> zero-length array.

With the added support for last element of size 1 it needs to stay where
it is.
> 
> 
> +  if (const ConstantArrayType *ArrayTy =
> +      Ctx.getAsConstantArrayType(last->getType())) {

> I don't think there's a rule about this, but I think a wrapped
> initialization or assignment should have the next line indented.
> "Whatever clang-format does" is also almost always an acceptable
> answer.
> 
> 

> +    if (ArrayTy->getSize() != 0)

> People sometimes write this pattern with a last element of 1 instead
> of 0. Since the analyzer tries to avoid false positives, we should
> probably detect that as a similar pattern and perform the same check.
> 

I have added support for that as well. However, that forced me to keep
the flexSize calculation at its original place.

> 
> +      BT.reset(new BuiltinBug("Cast region with wrong size.",
> +                          "Cast a region whose size is not a multiple
> of the"
> +                          " destination type size."));
> 
> 
> While you're here, please re-align the string literals so all the
> arguments to BuiltinBug() line up. (Also, after Alex's recent patch
> you'll need to pass the current checker to BuiltinBug as well.
> 
> 
> 
> 
> Index: test/Analysis/malloc-annotations.c
> ===================================================================
> --- test/Analysis/malloc-annotations.c (revision 201043)
> +++ test/Analysis/malloc-annotations.c (working copy)
> 
> 
> Don't worry about adding test cases to malloc-annotations.c. That's
> only supposed to test custom malloc and free wrappers. On the other
> hand, please include some test cases with flexible-array structs where
> the allocation size is too small to begin with, and some where the
> allocation size has zero additional capacity over the sizeof of the
> struct.

Ok, added.
> 
> Thanks for working on this!
> Jordan
> 
Thanks for your feedback, attached is a new version of the patch.

Cheers,
Daniel Fahlgren
Index: lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp	(revision 201356)
+++ lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp	(working copy)
@@ -29,6 +29,66 @@
 };
 }
 
+/// 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;
+
+  if (!Last)
+    return false;
+
+  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 +126,21 @@
   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);
   }
 }
 
Index: test/Analysis/no-outofbounds.c
===================================================================
--- test/Analysis/no-outofbounds.c	(revision 201356)
+++ 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 201356)
+++ test/Analysis/malloc.c	(working copy)
@@ -270,6 +270,222 @@
   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
_______________________________________________
cfe-commits mailing list
cfe-commits@cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to