This patch fixes the ICE reported in PR106181 by Arseny Solokha. With
this patch, the allocation size checker tries to handle floating-point
operands of allocation size arguments. Constant sizes get implicitly
converted and symbolic sizes are handled as long as the floating-point
operand could also be represented as a positive integer. In all other
cases and on unhandled constants, the checker falls back to not
emitting a warning.
Also, I unified the logic on zero byte allocations.

Regression-tested on x86_64 linux.

2022-07-05  Tim Lange  <m...@tim-lange.me>

gcc/analyzer/ChangeLog:

        PR analyzer/106181
        * region-model.cc (capacity_compatible_with_type):
        Can handle non-integer constants now.
        (region_model::check_region_size): Adapted to the new signature of
        capacity_compatible_with_type.

gcc/testsuite/ChangeLog:

        PR analyzer/106181
        * gcc.dg/analyzer/allocation-size-1.c: New tests.
        * gcc.dg/analyzer/allocation-size-2.c: New tests.
        * gcc.dg/analyzer/pr106181.c: New test.

---
 gcc/analyzer/region-model.cc                  | 44 ++++++++++++++++---
 .../gcc.dg/analyzer/allocation-size-1.c       | 29 +++++++++++-
 .../gcc.dg/analyzer/allocation-size-2.c       | 22 ++++++++++
 gcc/testsuite/gcc.dg/analyzer/pr106181.c      |  7 +++
 4 files changed, 95 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/pr106181.c

diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 5d939327e01..e097ecb3c07 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -2904,13 +2904,45 @@ private:
 
 static bool
 capacity_compatible_with_type (tree cst, tree pointee_size_tree,
-                              bool is_struct)
+                              bool is_struct, bool floor_real)
 {
-  gcc_assert (TREE_CODE (cst) == INTEGER_CST);
   gcc_assert (TREE_CODE (pointee_size_tree) == INTEGER_CST);
-
   unsigned HOST_WIDE_INT pointee_size = TREE_INT_CST_LOW (pointee_size_tree);
-  unsigned HOST_WIDE_INT alloc_size = TREE_INT_CST_LOW (cst);
+
+  unsigned HOST_WIDE_INT alloc_size;
+  switch (TREE_CODE (cst))
+    {
+    default:
+      /* Assume all unhandled operands are compatible.  */
+      return true;
+    case INTEGER_CST:
+      alloc_size = TREE_INT_CST_LOW (cst);
+      break;
+    case REAL_CST:
+      {
+       const REAL_VALUE_TYPE *rv = TREE_REAL_CST_PTR (cst);
+       if (floor_real)
+         {
+           /* If the size is constant real at compile-time,
+              we can model the conversion.  */
+           alloc_size = real_to_integer (rv);
+         }
+       else
+         {
+           /* On expressions where the value of one operator isn't
+              representable as an integer or is negative, we give up and
+              just assume that the programmer knows what they are doing.  */
+           HOST_WIDE_INT i;
+           if (real_isneg (rv) || !real_isinteger (rv, &i))
+             return true;
+           alloc_size = i;
+         }
+      }
+      break;
+    }
+
+  if (alloc_size == 0)
+    return true;
 
   if (is_struct)
     return alloc_size >= pointee_size;
@@ -2920,7 +2952,7 @@ capacity_compatible_with_type (tree cst, tree 
pointee_size_tree,
 static bool
 capacity_compatible_with_type (tree cst, tree pointee_size_tree)
 {
-  return capacity_compatible_with_type (cst, pointee_size_tree, false);
+  return capacity_compatible_with_type (cst, pointee_size_tree, false, false);
 }
 
 /* Checks whether SVAL could be a multiple of SIZE_CST.
@@ -3145,7 +3177,7 @@ region_model::check_region_size (const region *lhs_reg, 
const svalue *rhs_sval,
                = as_a <const constant_svalue *> (capacity);
        tree cst_cap = cst_cap_sval->get_constant ();
        if (!capacity_compatible_with_type (cst_cap, pointee_size_tree,
-                                           is_struct))
+                                           is_struct, true))
          ctxt->warn (new dubious_allocation_size (lhs_reg, rhs_reg,
                                                   cst_cap));
       }
diff --git a/gcc/testsuite/gcc.dg/analyzer/allocation-size-1.c 
b/gcc/testsuite/gcc.dg/analyzer/allocation-size-1.c
index 4a78a81d054..1a1c8e75c98 100644
--- a/gcc/testsuite/gcc.dg/analyzer/allocation-size-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/allocation-size-1.c
@@ -114,4 +114,31 @@ void test_10 (int32_t n)
 {
   char *ptr = malloc (7 * n);
   free (ptr);
-}
\ No newline at end of file
+}
+
+void test_11 ()
+{
+  int32_t *ptr = malloc (3.0); /* { dg-line malloc11 } */
+  free (ptr);  
+  /* { dg-warning "allocated buffer size is not a multiple of the pointee's 
size \\\[CWE-131\\\]" "warning" { target *-*-* } malloc11 } */
+  /* { dg-message "'int32_t \\*' (\\\{aka '(long )?int \\*'\\\})? here; 
'sizeof \\(int32_t (\\\{aka (long )?int\\\})?\\)' is '4'" "note" { target *-*-* 
} malloc11 } */
+}
+
+void test_12 ()
+{
+  int32_t *ptr = malloc (4.0);
+  free (ptr);
+}
+
+void test_13 ()
+{
+  int32_t *ptr = malloc (4.7);
+  free (ptr);
+}
+
+void test_14 ()
+{
+  /* Test round towards zero.  */
+  int32_t *ptr = malloc (-0.9);
+  free (ptr);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/allocation-size-2.c 
b/gcc/testsuite/gcc.dg/analyzer/allocation-size-2.c
index d541d5ef8db..babf9ae668d 100644
--- a/gcc/testsuite/gcc.dg/analyzer/allocation-size-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/allocation-size-2.c
@@ -154,3 +154,25 @@ void test_13 (void)
   else
     free (ptr);
 }
+
+void test_14 (int32_t n)
+{
+  int32_t *ptr = malloc (n * 3.0); /* { dg-line malloc14 } */
+  free (ptr);  
+  /* { dg-warning "allocated buffer size is not a multiple of the pointee's 
size \\\[CWE-131\\\]" "warning" { target *-*-* } malloc14 } */
+  /* { dg-message "'int32_t \\*' (\\\{aka '(long )?int \\*'\\\})? here; 
'sizeof \\(int32_t (\\\{aka (long )?int\\\})?\\)' is '4'" "note" { target *-*-* 
} malloc14 } */
+}
+
+void test_15 (int32_t n)
+{
+  int32_t *ptr = malloc (n * 4.0);
+  free (ptr);
+}
+
+void test_16 (int32_t n)
+{
+  /* Should not emit a warning because we can not reason whether the result
+     of the floating-point arithmetic actually is a valid size or not.  */
+  int32_t *ptr = malloc (n * 3.1);
+  free (ptr);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr106181.c 
b/gcc/testsuite/gcc.dg/analyzer/pr106181.c
new file mode 100644
index 00000000000..6df4e4538c0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr106181.c
@@ -0,0 +1,7 @@
+#include <stdint.h>
+
+int32_t *
+foo (int32_t x)
+{
+  return __builtin_calloc (x * 1.1, 1);
+}
-- 
2.36.1

Reply via email to