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