Hi!

If we have
  .ASAN_MARK (UNPOISON, &a, 24);
  .ASAN_MARK (UNPOISON, &b, 24);
  p_7 = &a;
  q_8 = &b;
  .ASAN_MARK (POISON, &a, 24);
  .ASAN_MARK (POISON, &b, 24);
  _1 = *p_7;
  _2 = *q_8;
  _3 = _1 + _2;
  <retval> = _3;
  return <retval>;
or
  .ASAN_MARK (UNPOISON, &a, 24);
  .ASAN_MARK (UNPOISON, &b, 24);
  p_4 = &a;
  q_5 = &b;
  .ASAN_MARK (POISON, &a, 24);
  .ASAN_MARK (POISON, &b, 24);
  *p_4 = 1;
  *q_5 = 2;
  return;
which represent load or store uses after scope and decide not to make the
vars addressable anymore, we turn that into
  a_10 = .ASAN_POISON ();
  b_11 = .ASAN_POISON ();
  _1 = a_10 + b_11;
  <retval> = _1;
  return <retval>;
or
  a_8 = .ASAN_POISON ();
  b_9 = .ASAN_POISON ();
  .ASAN_POISON_USE (a_8);
  .ASAN_POISON_USE (b_9);
  return;
These 2 internal fns are something that is normally lowere during
sanopt.  Now, if the involved vars are large/huge _BitInt, the bitintlower
pass doesn't handle them and we end up with invalid IL (we try to change
the lhs of .ASAN_POISON from SSA_NAME to a var etc. which violates what
sanopt expects and get an extra .ASAN_POISON_USE while doing that etc.

I thought what would be the best way to deal with these, e.g. try to replace
them with something tracking just one limb in those (although it is nicer to
report the proper sizes in asan rather than just small part of it), but
the .ASAN_POISON () uses can be also PHI args and some PHI args could be
.ASAN_POISON () uses while others could be unrelated SSA_NAMEs, so we'd
need to change those uses to be extensions from the .ASAN_POISONed limb
into full size on all edges and what to do with abnormal edges etc.

So, in the end I've decided instead to just perform what sanopt pass does
for these 2 ifns if large/huge _BitInt is involved at the start of the
bitintlower pass (similarly how we lower switches there).

The asan.cc changes are needed so that we can properly report 24 bytes
or 568 bytes etc. READs or WRITEs after scope.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2026-07-14  Jakub Jelinek  <[email protected]>

        PR middle-end/126084
        * gimple-lower-bitint.cc: Include "attribs.h" and "asan.h".
        (gimple_lower_bitint): Use asan_expand_poison_ifn to lower
        .ASAN_POISON calls with large/huge _BitInt lhs.
        * asan.cc (report_error_func): Set *nargs and use _n builtin
        even if size is not a power of two or larger than 16.
        (asan_expand_poison_ifn): Handle nargs == 2.

        * gcc.dg/asan/bitint-1.c: New test.
        * gcc.dg/asan/bitint-2.c: New test.

--- gcc/gimple-lower-bitint.cc.jj       2026-07-08 11:09:58.434724804 +0200
+++ gcc/gimple-lower-bitint.cc  2026-07-13 15:43:50.414954519 +0200
@@ -56,6 +56,8 @@ along with GCC; see the file COPYING3.
 #include "ubsan.h"
 #include "stor-layout.h"
 #include "gimple-lower-bitint.h"
+#include "attribs.h"
+#include "asan.h"
 
 /* Split BITINT_TYPE precisions in 4 categories.  Small _BitInt, where
    target hook says it is a single limb, middle _BitInt which per ABI
@@ -7319,6 +7321,40 @@ gimple_lower_bitint (void)
          free_dominance_info (CDI_DOMINATORS);
          free_dominance_info (CDI_POST_DOMINATORS);
          mark_virtual_operands_for_renaming (cfun);
+         cleanup_tree_cfg (TODO_update_ssa);
+       }
+    }
+
+  if (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_HWADDRESS))
+    {
+      hash_map<tree, tree> shadow_vars_mapping;
+      bool need_commit_edge_insert = false;
+      bool any_asan_poison = false;
+      for (unsigned j = 0; j < num_ssa_names; ++j)
+       {
+         tree s = ssa_name (j);
+         if (s == NULL)
+           continue;
+         tree type = TREE_TYPE (s);
+         if (BITINT_TYPE_P (type)
+             && gimple_call_internal_p (SSA_NAME_DEF_STMT (s),
+                                        IFN_ASAN_POISON)
+             && bitint_precision_kind (type) >= bitint_prec_large)
+           {
+             any_asan_poison = true;
+             gimple_stmt_iterator gsi = gsi_for_stmt (SSA_NAME_DEF_STMT (s));
+             asan_expand_poison_ifn (&gsi, &need_commit_edge_insert,
+                                     shadow_vars_mapping);
+           }
+       }
+      if (any_asan_poison)
+       {
+         if (need_commit_edge_insert)
+           gsi_commit_edge_inserts ();
+         i = 0;
+         free_dominance_info (CDI_DOMINATORS);
+         free_dominance_info (CDI_POST_DOMINATORS);
+         mark_virtual_operands_for_renaming (cfun);
          cleanup_tree_cfg (TODO_update_ssa);
        }
     }
--- gcc/asan.cc.jj      2026-05-30 17:45:09.339110356 +0200
+++ gcc/asan.cc 2026-07-13 16:17:42.135726685 +0200
@@ -2536,8 +2536,13 @@ report_error_func (bool is_store, bool r
       *nargs = 2;
       return builtin_decl_implicit (report[recover_p][is_store][5]);
     }
-  *nargs = 1;
   int size_log2 = exact_log2 (size_in_bytes);
+  if (size_log2 == -1 || size_log2 >= 5)
+    {
+      *nargs = 2;
+      return builtin_decl_implicit (report[recover_p][is_store][5]);
+    }
+  *nargs = 1;
   return builtin_decl_implicit (report[recover_p][is_store][size_log2]);
 }
 
@@ -4380,8 +4385,12 @@ asan_expand_poison_ifn (gimple_stmt_iter
        {
          tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
                                        &nargs);
-         call = gimple_build_call (fun, 1,
-                                   build_fold_addr_expr (shadow_var));
+         call = gimple_build_call (fun, nargs,
+                                   build_fold_addr_expr (shadow_var),
+                                   nargs == 2
+                                   ? fold_convert (pointer_sized_int_node,
+                                                   size)
+                                   : NULL_TREE);
        }
       gimple_set_location (call, gimple_location (use));
       gimple *call_to_insert = call;
--- gcc/testsuite/gcc.dg/asan/bitint-1.c.jj     2026-07-13 16:25:06.148278451 
+0200
+++ gcc/testsuite/gcc.dg/asan/bitint-1.c        2026-07-13 16:29:19.034189583 
+0200
@@ -0,0 +1,26 @@
+/* PR middle-end/126084 */
+/* { dg-do run { target bitint575 } } */
+/* { dg-shouldfail "asan" } */
+/* { dg-skip-if "" { *-*-* }  { "*" } { "-O2" } } */
+
+_BitInt(135)
+foo ()
+{
+  _BitInt(135) *p, *q;
+  {
+    _BitInt(135) a, b;
+    p = &a;
+    q = &b;
+  }
+  return *p + *q;
+}
+
+int
+main ()
+{
+  volatile _BitInt(135) x = foo ();
+}
+
+/* { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on 
address.*(\n|\r\n|\r)" } */
+/* { dg-output "READ of size .*" } */
+/* { dg-output ".*'a' \\(line 11\\) <== Memory access at offset \[0-9\]* is 
inside this variable.*" } */
--- gcc/testsuite/gcc.dg/asan/bitint-2.c.jj     2026-07-13 16:25:09.077242677 
+0200
+++ gcc/testsuite/gcc.dg/asan/bitint-2.c        2026-07-13 16:29:50.433806054 
+0200
@@ -0,0 +1,27 @@
+/* PR middle-end/126084 */
+/* { dg-do run { target bitint575 } } */
+/* { dg-shouldfail "asan" } */
+/* { dg-skip-if "" { *-*-* }  { "*" } { "-O2" } } */
+
+void
+foo ()
+{
+  _BitInt(135) *p, *q;
+  {
+    _BitInt(135) a, b;
+    p = &a;
+    q = &b;
+  }
+  *p = 1;
+  *q = 2;
+}
+
+int
+main ()
+{
+  foo ();
+}
+
+/* { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on 
address.*(\n|\r\n|\r)" } */
+/* { dg-output "WRITE of size .*" } */
+/* { dg-output ".*'a' \\(line 11\\) <== Memory access at offset \[0-9\]* is 
inside this variable.*" } */

        Jakub

Reply via email to