diff options
author | David Malcolm <dmalcolm@redhat.com> | 2024-01-30 08:17:47 -0500 |
---|---|---|
committer | David Malcolm <dmalcolm@redhat.com> | 2024-01-30 08:17:47 -0500 |
commit | 9f382376660069e49290fdb51861abdec63519c7 (patch) | |
tree | 8155436542d5b19bd871e5e7e63d5868661e349f /gcc | |
parent | 181f753dc4afa9866d6125904cf050d5eb437a86 (diff) | |
download | gcc-9f382376660069e49290fdb51861abdec63519c7.zip gcc-9f382376660069e49290fdb51861abdec63519c7.tar.gz gcc-9f382376660069e49290fdb51861abdec63519c7.tar.bz2 |
analyzer: fix -Wanalyzer-allocation-size false +ve on Linux kernel's round_up macro [PR113654]
gcc/analyzer/ChangeLog:
PR analyzer/113654
* region-model.cc (is_round_up): New.
(is_multiple_p): New.
(is_dubious_capacity): New.
(region_model::check_region_size): Move usage of size_visitor into
is_dubious_capacity.
gcc/testsuite/ChangeLog:
PR analyzer/113654
* c-c++-common/analyzer/allocation-size-pr113654-1.c: New test.
Signed-off-by: David Malcolm <dmalcolm@redhat.com>
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/analyzer/region-model.cc | 75 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/analyzer/allocation-size-pr113654-1.c | 52 |
2 files changed, 125 insertions, 2 deletions
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index ba82f46..082972f 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -3349,6 +3349,76 @@ private: svalue_set result_set; /* Used as a mapping of svalue*->bool. */ }; +/* Return true if SIZE_CST is a power of 2, and we have + CAPACITY_SVAL == ((X | (Y - 1) ) + 1), since it is then a multiple + of SIZE_CST, as used by Linux kernel's round_up macro. */ + +static bool +is_round_up (tree size_cst, + const svalue *capacity_sval) +{ + if (!integer_pow2p (size_cst)) + return false; + const binop_svalue *binop_sval = capacity_sval->dyn_cast_binop_svalue (); + if (!binop_sval) + return false; + if (binop_sval->get_op () != PLUS_EXPR) + return false; + tree rhs_cst = binop_sval->get_arg1 ()->maybe_get_constant (); + if (!rhs_cst) + return false; + if (!integer_onep (rhs_cst)) + return false; + + /* We have CAPACITY_SVAL == (LHS + 1) for some LHS expression. */ + + const binop_svalue *lhs_binop_sval + = binop_sval->get_arg0 ()->dyn_cast_binop_svalue (); + if (!lhs_binop_sval) + return false; + if (lhs_binop_sval->get_op () != BIT_IOR_EXPR) + return false; + + tree inner_rhs_cst = lhs_binop_sval->get_arg1 ()->maybe_get_constant (); + if (!inner_rhs_cst) + return false; + + if (wi::to_widest (inner_rhs_cst) + 1 != wi::to_widest (size_cst)) + return false; + return true; +} + +/* Return true if CAPACITY_SVAL is known to be a multiple of SIZE_CST. */ + +static bool +is_multiple_p (tree size_cst, + const svalue *capacity_sval) +{ + if (const svalue *sval = capacity_sval->maybe_undo_cast ()) + return is_multiple_p (size_cst, sval); + + if (is_round_up (size_cst, capacity_sval)) + return true; + + return false; +} + +/* Return true if we should emit a dubious_allocation_size warning + on assigning a region of capacity CAPACITY_SVAL bytes to a pointer + of type with size SIZE_CST, where CM expresses known constraints. */ + +static bool +is_dubious_capacity (tree size_cst, + const svalue *capacity_sval, + constraint_manager *cm) +{ + if (is_multiple_p (size_cst, capacity_sval)) + return false; + size_visitor v (size_cst, capacity_sval, cm); + return v.is_dubious_capacity (); +} + + /* Return true if a struct or union either uses the inheritance pattern, where the first field is a base struct, or the flexible array member pattern, where the last field is an array without a specified size. */ @@ -3456,8 +3526,9 @@ region_model::check_region_size (const region *lhs_reg, const svalue *rhs_sval, { if (!is_struct) { - size_visitor v (pointee_size_tree, capacity, m_constraints); - if (v.is_dubious_capacity ()) + if (is_dubious_capacity (pointee_size_tree, + capacity, + m_constraints)) { tree expr = get_representative_tree (capacity); ctxt->warn (make_unique <dubious_allocation_size> (lhs_reg, diff --git a/gcc/testsuite/c-c++-common/analyzer/allocation-size-pr113654-1.c b/gcc/testsuite/c-c++-common/analyzer/allocation-size-pr113654-1.c new file mode 100644 index 0000000..b7bfc5f --- /dev/null +++ b/gcc/testsuite/c-c++-common/analyzer/allocation-size-pr113654-1.c @@ -0,0 +1,52 @@ +/* Adapted from include/linux/math.h */ +#define __round_mask(x, y) ((__typeof__(x))((y)-1)) +#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) + +/* Reduced from Linux kernel's drivers/gpu/drm/i915/display/intel_bios.c */ +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long __kernel_size_t; +typedef __kernel_size_t size_t; + +extern __attribute__((__alloc_size__(1))) __attribute__((__malloc__)) +void* kzalloc(size_t size); + +typedef struct +{ + u32 reg; +} i915_reg_t; +struct intel_uncore; +struct intel_uncore_funcs +{ + u32 (*mmio_readl)(struct intel_uncore* uncore, i915_reg_t r); +}; +struct intel_uncore +{ + void* regs; + struct intel_uncore_funcs funcs; +}; +static inline __attribute__((__gnu_inline__)) __attribute__((__unused__)) +__attribute__((no_instrument_function)) u32 +intel_uncore_read(struct intel_uncore* uncore, i915_reg_t reg) +{ + return uncore->funcs.mmio_readl(uncore, reg); +} +struct drm_i915_private +{ + struct intel_uncore uncore; +}; +struct vbt_header* +spi_oprom_get_vbt(struct drm_i915_private* i915) +{ + u16 vbt_size; + u32* vbt; + vbt_size = + intel_uncore_read(&i915->uncore, ((const i915_reg_t){ .reg = (0x102040) })); + vbt_size &= 0xffff; + vbt = (u32*)kzalloc(round_up (vbt_size, 4)); /* { dg-bogus "allocated buffer size is not a multiple of the pointee's size" "PR analyzer/113654" } */ + if (!vbt) + goto err_not_found; + return (struct vbt_header*)vbt; +err_not_found: + return ((struct vbt_header*)0); +} |