[pushed] analyzer: fix -Wanalyzer-allocation-size false +ve on Linux kernel's round_up macro [PR113654]

Message ID 20240130133123.1875809-1-dmalcolm@redhat.com
State Committed
Commit 9f382376660069e49290fdb51861abdec63519c7
Headers
Series [pushed] analyzer: fix -Wanalyzer-allocation-size false +ve on Linux kernel's round_up macro [PR113654] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm warning Patch is already merged
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 warning Patch is already merged

Commit Message

David Malcolm Jan. 30, 2024, 1:31 p.m. UTC
  Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Successful run of analyzer integration tests on x86_64-pc-linux-gnu.
Pushed to trunk as r14-8627-g9f382376660069.

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>
---
 gcc/analyzer/region-model.cc                  | 75 ++++++++++++++++++-
 .../analyzer/allocation-size-pr113654-1.c     | 52 +++++++++++++
 2 files changed, 125 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/analyzer/allocation-size-pr113654-1.c
  

Patch

diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index ba82f46c1887..082972f9d294 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 000000000000..b7bfc5fec72a
--- /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);
+}