Matthew Malcomson <[email protected]> writes:
> +/* Emit gimple statements into &stmts that take the size given in `len` and
> + generate a size that is guaranteed to be rounded upwards to `align`.
> +
> + This is a helper function for both handle_builtin_alloca and
> + asan_expand_mark_ifn when using HWASAN.
> +
> + Return the tree node representing this size, it is of TREE_TYPE
> + size_type_node. */
> +
> +static tree
> +hwasan_emit_round_up (gimple_seq *seq, location_t loc, tree old_size,
> + uint8_t align)
> +{
> + uint8_t tg_mask = align - 1;
> + /* tree new_size = (old_size + tg_mask) & ~tg_mask; */
> + tree tree_mask = build_int_cst (size_type_node, tg_mask);
> + tree oversize = gimple_build (seq, loc, PLUS_EXPR, size_type_node,
> old_size,
> + tree_mask);
> +
> + tree mask = build_int_cst (size_type_node, -align);
> + return gimple_build (seq, loc, BIT_AND_EXPR, size_type_node, oversize,
> mask);
> +}
> +
There's nothing really hwasan-specific about this, apart from the choice
“uint8_t” for the alignment and mask. So I think we should:
- chnage “align” and “tg_mask” to “unsigned HOST_WIDE_INT”
- change the name to “gimple_build_round_up”
- take the type as a parameter, in the same position as other
gimple_build_* type parameters
- move the function to gimple-fold.c, exported via gimple-fold.h
- drop:
This is a helper function for both handle_builtin_alloca and
asan_expand_mark_ifn when using HWASAN.
> […]
> @@ -690,6 +757,71 @@ handle_builtin_alloca (gcall *call, gimple_stmt_iterator
> *iter)
> = DECL_FUNCTION_CODE (callee) == BUILT_IN_ALLOCA
> ? 0 : tree_to_uhwi (gimple_call_arg (call, 1));
>
> + if (hwasan_sanitize_allocas_p ())
> + {
> + gimple_seq stmts = NULL;
> + location_t loc = gimple_location (gsi_stmt (*iter));
> + /*
> + HWASAN needs a different expansion.
> +
> + addr = __builtin_alloca (size, align);
> +
> + should be replaced by
> +
> + new_size = size rounded up to HWASAN_TAG_GRANULE_SIZE byte alignment;
> + untagged_addr = __builtin_alloca (new_size, align);
> + tag = __hwasan_choose_alloca_tag ();
> + addr = ifn_HWASAN_SET_TAG (untagged_addr, tag);
> + __hwasan_tag_memory (untagged_addr, tag, new_size);
> + */
> + /* Ensure alignment at least HWASAN_TAG_GRANULE_SIZE bytes so we start
> on
> + a tag granule. */
> + align = align > HWASAN_TAG_GRANULE_SIZE ? align :
> HWASAN_TAG_GRANULE_SIZE;
> +
> + tree old_size = gimple_call_arg (call, 0);
> + tree new_size = hwasan_emit_round_up (&stmts, loc, old_size,
> + HWASAN_TAG_GRANULE_SIZE);
> +
> + /* Make the alloca call */
> + tree untagged_addr
> + = gimple_build (&stmts, loc,
> + as_combined_fn (BUILT_IN_ALLOCA_WITH_ALIGN), ptr_type,
> + new_size, build_int_cst (size_type_node, align));
> +
> + /* Choose the tag.
> + Here we use an internal function so we can choose the tag at expand
> + time. We need the decision to be made after stack variables have been
> + assigned their tag (i.e. once the hwasan_frame_tag_offset variable has
> + been set to one after the last stack variables tag). */
> + gcall *stmt = gimple_build_call_internal (IFN_HWASAN_CHOOSE_TAG, 0);
> + tree tag = make_ssa_name (unsigned_char_type_node);
> + gimple_call_set_lhs (stmt, tag);
> + gimple_set_location (stmt, loc);
> + gimple_seq_add_stmt_without_update (&stmts, stmt);
Even though there are currently no folds defined for argumentless
functions, I think it would be worth adding a gimple_build overload
for this instead of writing it out by hand. I.e. have a zero-argument
version of:
tree
gimple_build (gimple_seq *seq, location_t loc, combined_fn fn,
tree type, tree arg0)
{
tree res = gimple_simplify (fn, type, arg0, seq, gimple_build_valueize);
if (!res)
{
gcall *stmt;
if (internal_fn_p (fn))
stmt = gimple_build_call_internal (as_internal_fn (fn), 1, arg0);
else
{
tree decl = builtin_decl_implicit (as_builtin_fn (fn));
stmt = gimple_build_call (decl, 1, arg0);
}
if (!VOID_TYPE_P (type))
{
res = create_tmp_reg_or_ssa_name (type);
gimple_call_set_lhs (stmt, res);
}
gimple_set_location (stmt, loc);
gimple_seq_add_stmt_without_update (seq, stmt);
}
return res;
}
without the gimple_simplify call.
> +
> + /* Add tag to pointer. */
> + tree addr
> + = gimple_build (&stmts, loc, as_combined_fn (IFN_HWASAN_SET_TAG),
This is CFN_HWASAN_SET_TAG.
> + ptr_type, untagged_addr, tag);
> +
> + /* Tag shadow memory.
> + NOTE: require using `untagged_addr` here for libhwasan API. */
> + gimple_build (&stmts, loc, as_combined_fn (BUILT_IN_HWASAN_TAG_MEM),
> + void_type_node, untagged_addr, tag, new_size);
> +
> + /* Insert the built up code sequence into the original instruction
> stream
> + the iterator points to. */
> + gsi_insert_seq_before (iter, stmts, GSI_SAME_STMT);
> +
> + /* Finally, replace old alloca ptr with NEW_ALLOCA. */
> + replace_call_with_value (iter, addr);
> + return;
> + }
> +
> + tree last_alloca = get_last_alloca_addr ();
> + const HOST_WIDE_INT redzone_mask = ASAN_RED_ZONE_SIZE - 1;
> +
> +
Nit: excess blank line.
> /* If ALIGN > ASAN_RED_ZONE_SIZE, we embed left redzone into first ALIGN
> bytes of allocated space. Otherwise, align alloca to ASAN_RED_ZONE_SIZE
> manually. */
> […]
> @@ -2351,7 +2686,8 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
> {
> if (DECL_THREAD_LOCAL_P (inner))
> return;
> - if (!param_asan_globals && is_global_var (inner))
> + if ((hwasan_sanitize_p () || !param_asan_globals)
> + && is_global_var (inner))
> return;
> if (!TREE_STATIC (inner))
> {
As mentioned in the previous round, I think it would be good to have
some commentary explaining this.
> […]
> + /* If the length is a simple SHWI, then we can calculate the rounded up
> + length directly. Otherwise we need to emit gimple to do this
> + rounding at runtime. */
> + if (tree_fits_shwi_p (len))
> + {
> + unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
> + uint8_t tg_mask = HWASAN_TAG_GRANULE_SIZE - 1;
> + size_in_bytes = (size_in_bytes + tg_mask) & ~tg_mask;
> + new_len = build_int_cst (pointer_sized_int_node, size_in_bytes);
> + }
> + else
> + new_len = hwasan_emit_round_up (&stmts, loc, len,
> + HWASAN_TAG_GRANULE_SIZE);
I'm not sure the special case is worth it. We might as well call
(hwasan_emit/gimple_build)_round_up unconditionally, which would also
have the nice effect of testing it more.
> + gimple_build (&stmts, loc, as_combined_fn (IFN_HWASAN_MARK),
CFN_HWASAN_MARK
> + void_type_node, gimple_call_arg (g, 0),
> + base, new_len);
> + gsi_replace_with_seq (iter, stmts, true);
> + return false;
> + }
> +
> if (is_poison)
> {
> if (asan_handled_variables == NULL)
> […]
> +/* For hwasan stack tagging:
> + Tag a region of space in the shadow stack according to the base pointer of
> + an object on the stack. N.b. the length provided in the internal call is
> + required to be aligned to HWASAN_TAG_GRANULE_SIZE. */
> +static void
> +expand_HWASAN_MARK (internal_fn, gcall *gc)
> +{
> + HOST_WIDE_INT flag = tree_to_shwi (gimple_call_arg (gc, 0));
> + bool is_poison = ((asan_mark_flags)flag) == ASAN_MARK_POISON;
> +
> + tree base = gimple_call_arg (gc, 1);
> + gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
> + /* base is a pointer argument, hence in ptr_mode.
> + We convert to Pmode for use in the targetm.memtag.extract_tag and
> + targetm.memtag.untagged_pointer hooks.
> + We then convert the result to ptr_mode for the emit_library_call.
> +
> + This conversion is not for functionality so much as for the code to be
> + correctly formed. If ptr_mode is smaller than Pmode then the top byte
> of
> + a Pmode value will be truncated in C code losing the tag values, and
> + HWASAN will not work. */
> + rtx base_rtx = convert_memory_address (Pmode, expand_normal (base));
> +
> + rtx tag = is_poison ? HWASAN_STACK_BACKGROUND
> + : targetm.memtag.extract_tag (base_rtx, NULL_RTX);
> + rtx address = targetm.memtag.untagged_pointer (base_rtx, NULL_RTX);
> + address = convert_memory_address (ptr_mode, address);
> +
> + tree len = gimple_call_arg (gc, 2);
> + rtx r_len = expand_normal (len);
> +
> + rtx func = init_one_libfunc ("__hwasan_tag_memory");
> + emit_library_call (func, LCT_NORMAL, VOIDmode, address, ptr_mode,
> + tag, QImode, r_len, ptr_mode);
> +}
> +
> +/* For hwasan stack tagging:
> + Store a tag into a pointer. */
> +static void
> +expand_HWASAN_SET_TAG (internal_fn, gcall *gc)
> +{
> + tree g_target = gimple_call_lhs (gc);
> + tree g_ptr = gimple_call_arg (gc, 0);
> + tree g_tag = gimple_call_arg (gc, 1);
> +
> + rtx ptr = convert_memory_address (Pmode, expand_normal (g_ptr));
> + rtx tag = expand_expr (g_tag, NULL_RTX, QImode, EXPAND_NORMAL);
> + rtx target = expand_normal (g_target);
> + machine_mode mode = GET_MODE (target);
> +
> + rtx untagged = targetm.memtag.untagged_pointer (ptr, target);
> + rtx tagged_value = targetm.memtag.set_tag (untagged, tag, target);
> + /* Really need to put the return value into the `target` RTX, since that's
> + the return value of the function.
> + `target` will be in ptr_mode, while `tagged_value` will be in Pmode.
> + These can be different. When they are different we try to truncate the
> + Pmode value into ptr_mode. This will usually lose the tag, but since
> such
> + a difference between ptr_mode and Pmode will already cause problems
> + wherever the HWASAN library returns a pointer losing the tag here does
> not
> + introduce new incompatibilities.
> +
> + We do this mainly so that compiling for such a target with ptr_mode and
> + Pmode sizes being different doesn't ICE, even if the resulting binary is
> + not usable. */
I'm not sure that's the right trade-off. Producing wrong code is a more
serious class of bug than an ICE.
Could the code in principle be reached on ILP32 targets? If so,
that seems like something we should fix. If not, then I think we
should just do everything in Pmode, perhaps with an assertion that
Pmode == ptr_mode to make it clear that this is a deliberate choice.
I see what you mean about trying to make sure that types “agree”
at the appropriate interface. But in a sense we're already failing
to do that because we assume Pmode is correct for all addresses,
without taking address spaces into account. Assuming Pmode == ptr_mode
doesn't seem worse than assuming a unified address space.
LGTM otherwise though.
Thanks,
Richard