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);
+}