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
