On Fri, 18 Feb 2022, Jakub Jelinek wrote:
> Hi!
>
> We ICE on the following testcase, because the asan1 pass decides to
> instrument
> <retval>.x = 0;
> and does that by
> _13 = &<retval>.x;
> .ASAN_CHECK (7, _13, 4, 4);
> <retval>.x = 0;
> and later sanopt pass turns that into:
> _39 = (unsigned long) &<retval>.x;
> _40 = _39 >> 3;
> _41 = _40 + 2147450880;
> _42 = (signed char *) _41;
> _43 = *_42;
> _44 = _43 != 0;
> _45 = _39 & 7;
> _46 = (signed char) _45;
> _47 = _46 + 3;
> _48 = _47 >= _43;
> _49 = _44 & _48;
> if (_49 != 0)
> goto <bb 10>; [0.05%]
> else
> goto <bb 9>; [99.95%]
>
> <bb 10> [local count: 536864]:
> __builtin___asan_report_store4 (_39);
>
> <bb 9> [local count: 1073741824]:
> <retval>.x = 0;
> The problem is during expansion, <retval> isn't marked TREE_ADDRESSABLE,
> even when we take its address in (unsigned long) &<retval>.x.
>
> Now, instrument_derefs has code to avoid the instrumentation altogether
> if we can prove the access is within bounds of an automatic variable in the
> current function and the var isn't TREE_ADDRESSABLE (or we don't instrument
> use after scope), but we do it solely for VAR_DECLs.
>
> I think we should treat RESULT_DECLs exactly like that too, which is what
> the following patch does. I must say I'm unsure about PARM_DECLs, those can
> have different cases, either they are fully or partially passed in
> registers, then if we take parameter's address, they are in a local copy
> inside of a function and so work like those automatic vars. But if they
> are fully passed in memory, we typically just take address of the slot
> and in that case they live in the caller's frame. It is true we don't
> (can't) put any asan padding in between the arguments, so all asan could
> detect in that case is if caller passes fewer on stack arguments or smaller
> arguments than callee accepts. Anyway, as I'm unsure, I haven't added
> PARM_DECLs to that case.
>
> And another thing is, when we actually build_fold_addr_expr, we need to
> mark_addressable the inner if it isn't addressable already.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
>
> 2022-02-18 Jakub Jelinek <[email protected]>
>
> PR sanitizer/102656
> * asan.cc (instrument_derefs): If inner is a RESULT_DECL and access is
> known to be within bounds, treat it like automatic variables.
> If instrumenting access and inner is {VAR,PARM,RESULT}_DECL from
> current function and !TREE_STATIC which is not TREE_ADDRESSABLE, mark
> it addressable.
>
> * g++.dg/asan/pr102656.C: New test.
>
> --- gcc/asan.cc.jj 2022-02-12 19:14:45.915168286 +0100
> +++ gcc/asan.cc 2022-02-17 17:13:09.346987047 +0100
> @@ -2688,13 +2688,13 @@ instrument_derefs (gimple_stmt_iterator
> return;
>
> poly_int64 decl_size;
> - if (VAR_P (inner)
> + if ((VAR_P (inner) || TREE_CODE (inner) == RESULT_DECL)
> && offset == NULL_TREE
> && DECL_SIZE (inner)
> && poly_int_tree_p (DECL_SIZE (inner), &decl_size)
> && known_subrange_p (bitpos, bitsize, 0, decl_size))
> {
> - if (DECL_THREAD_LOCAL_P (inner))
> + if (VAR_P (inner) && DECL_THREAD_LOCAL_P (inner))
> return;
> /* If we're not sanitizing globals and we can tell statically that this
> access is inside a global variable, then there's no point adding
> @@ -2724,6 +2726,14 @@ instrument_derefs (gimple_stmt_iterator
> }
> }
>
> + if ((VAR_P (inner)
> + || TREE_CODE (inner) == PARM_DECL
> + || TREE_CODE (inner) == RESULT_DECL)
> + && !TREE_STATIC (inner)
> + && decl_function_context (inner) == current_function_decl
> + && !TREE_ADDRESSABLE (inner))
There's no good reason to exclute TREE_STATIC or global vars. If
we take the address of something we should mark it addressable.
I think you can just use DECL_P (inner) as well here for simplicity.
OK with that change.
Thanks,
Richard.
> + mark_addressable (inner);
> +
> base = build_fold_addr_expr (t);
> if (!has_mem_ref_been_instrumented (base, size_in_bytes))
> {
> --- gcc/testsuite/g++.dg/asan/pr102656.C.jj 2022-02-17 17:09:42.758866731
> +0100
> +++ gcc/testsuite/g++.dg/asan/pr102656.C 2022-02-17 17:10:46.212981870
> +0100
> @@ -0,0 +1,27 @@
> +// PR sanitizer/102656
> +// { dg-do compile }
> +// { dg-options "-std=c++20 -fsanitize=address" }
> +
> +#include <coroutine>
> +
> +class promise;
> +
> +struct future {
> + using promise_type = promise;
> + future() = default;
> + int x = 0;
> +};
> +
> +struct promise {
> + future get_return_object() noexcept { return {}; }
> + auto initial_suspend() noexcept { return std::suspend_never{}; }
> + auto final_suspend() noexcept { return std::suspend_never{}; }
> + void return_void() noexcept {}
> + void unhandled_exception() {}
> +};
> +
> +future
> +func ()
> +{
> + co_return;
> +}
>
> Jakub
>
>
--
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg,
Germany; GF: Ivo Totev; HRB 36809 (AG Nuernberg)