Discussion:
[PATCH, Pointer Bounds Checker 14/x] Passes [6/n] Instrument calls and returns
Ilya Enkovich
2014-10-08 19:04:40 UTC
Permalink
Hi,

This patch adds intrumentation of calls and returns into instrumentation pass.

Thanks,
Ilya
--
2014-10-08 Ilya Enkovich <***@intel.com>

* tree-chkp.c (chkp_add_bounds_to_ret_stmt): New.
(chkp_replace_address_check_builtin): New.
(chkp_replace_extract_builtin): New.
(chkp_find_bounds_for_elem): New.
(chkp_add_bounds_to_call_stmt): New.
(chkp_instrument_function): Instrument rets and calls.


diff --git a/gcc/tree-chkp.c b/gcc/tree-chkp.c
index bbb6459..6bbceb0 100644
--- a/gcc/tree-chkp.c
+++ b/gcc/tree-chkp.c
@@ -1047,6 +1047,29 @@ chkp_get_registered_bounds (tree ptr)
return slot ? *slot : NULL_TREE;
}

+/* Add bound retvals to return statement pointed by GSI. */
+
+static void
+chkp_add_bounds_to_ret_stmt (gimple_stmt_iterator *gsi)
+{
+ gimple ret = gsi_stmt (*gsi);
+ tree retval = gimple_return_retval (ret);
+ tree ret_decl = DECL_RESULT (cfun->decl);
+ tree bounds;
+
+ if (!retval)
+ return;
+
+ if (BOUNDED_P (ret_decl))
+ {
+ bounds = chkp_find_bounds (retval, gsi);
+ bounds = chkp_maybe_copy_and_register_bounds (ret_decl, bounds);
+ gimple_return_set_retbnd (ret, bounds);
+ }
+
+ update_stmt (ret);
+}
+
/* Force OP to be suitable for using as an argument for call.
New statements (if any) go to SEQ. */
static tree
@@ -1169,6 +1192,64 @@ chkp_check_mem_access (tree first, tree last, tree bounds,
chkp_check_upper (last, bounds, iter, location, dirflag);
}

+/* Replace call to _bnd_chk_* pointed by GSI with
+ bndcu and bndcl calls. DIRFLAG determines whether
+ check is for read or write. */
+
+void
+chkp_replace_address_check_builtin (gimple_stmt_iterator *gsi,
+ tree dirflag)
+{
+ gimple_stmt_iterator call_iter = *gsi;
+ gimple call = gsi_stmt (*gsi);
+ tree fndecl = gimple_call_fndecl (call);
+ tree addr = gimple_call_arg (call, 0);
+ tree bounds = chkp_find_bounds (addr, gsi);
+
+ if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_LBOUNDS
+ || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_BOUNDS)
+ chkp_check_lower (addr, bounds, *gsi, gimple_location (call), dirflag);
+
+ if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_UBOUNDS)
+ chkp_check_upper (addr, bounds, *gsi, gimple_location (call), dirflag);
+
+ if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_BOUNDS)
+ {
+ tree size = gimple_call_arg (call, 1);
+ addr = fold_build_pointer_plus (addr, size);
+ addr = fold_build_pointer_plus_hwi (addr, -1);
+ chkp_check_upper (addr, bounds, *gsi, gimple_location (call), dirflag);
+ }
+
+ gsi_remove (&call_iter, true);
+}
+
+/* Replace call to _bnd_get_ptr_* pointed by GSI with
+ corresponding bounds extract call. */
+
+void
+chkp_replace_extract_builtin (gimple_stmt_iterator *gsi)
+{
+ gimple call = gsi_stmt (*gsi);
+ tree fndecl = gimple_call_fndecl (call);
+ tree addr = gimple_call_arg (call, 0);
+ tree bounds = chkp_find_bounds (addr, gsi);
+ gimple extract;
+
+ if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_LBOUND)
+ fndecl = chkp_extract_lower_fndecl;
+ else if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_UBOUND)
+ fndecl = chkp_extract_upper_fndecl;
+ else
+ gcc_unreachable ();
+
+ extract = gimple_build_call (fndecl, 1, bounds);
+ gimple_call_set_lhs (extract, gimple_call_lhs (call));
+ chkp_mark_stmt (extract);
+
+ gsi_replace (gsi, extract, false);
+}
+
/* Return COMPONENT_REF accessing FIELD in OBJ. */
static tree
chkp_build_component_ref (tree obj, tree field)
@@ -1247,6 +1328,82 @@ chkp_can_be_shared (tree t)
return false;
}

+/* Helper function for chkp_add_bounds_to_call_stmt.
+ Fill ALL_BOUNDS output array with created bounds.
+
+ OFFS is used for recursive calls and holds basic
+ offset of TYPE in outer structure in bits.
+
+ ITER points a position where bounds are searched.
+
+ ALL_BOUNDS[i] is filled with elem bounds if there
+ is a field in TYPE which has pointer type and offset
+ equal to i * POINTER_SIZE in bits. */
+static void
+chkp_find_bounds_for_elem (tree elem, tree *all_bounds,
+ HOST_WIDE_INT offs,
+ gimple_stmt_iterator *iter)
+{
+ tree type = TREE_TYPE (elem);
+
+ if (BOUNDED_TYPE_P (type))
+ {
+ if (!all_bounds[offs / POINTER_SIZE])
+ {
+ tree temp = make_temp_ssa_name (type, gimple_build_nop (), "");
+ gimple assign = gimple_build_assign (temp, elem);
+ gimple_stmt_iterator gsi;
+
+ gsi_insert_before (iter, assign, GSI_SAME_STMT);
+ gsi = gsi_for_stmt (assign);
+
+ all_bounds[offs / POINTER_SIZE] = chkp_find_bounds (temp, &gsi);
+ }
+ }
+ else if (RECORD_OR_UNION_TYPE_P (type))
+ {
+ tree field;
+
+ for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+ if (TREE_CODE (field) == FIELD_DECL)
+ {
+ tree base = chkp_can_be_shared (elem)
+ ? elem
+ : unshare_expr (elem);
+ tree field_ref = chkp_build_component_ref (base, field);
+ HOST_WIDE_INT field_offs
+ = TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field));
+ if (DECL_FIELD_OFFSET (field))
+ field_offs += TREE_INT_CST_LOW (DECL_FIELD_OFFSET (field)) * 8;
+
+ chkp_find_bounds_for_elem (field_ref, all_bounds,
+ offs + field_offs, iter);
+ }
+ }
+ else if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ tree maxval = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
+ tree etype = TREE_TYPE (type);
+ HOST_WIDE_INT esize = TREE_INT_CST_LOW (TYPE_SIZE (etype));
+ unsigned HOST_WIDE_INT cur;
+
+ if (!maxval || integer_minus_onep (maxval))
+ return;
+
+ for (cur = 0; cur <= TREE_INT_CST_LOW (maxval); cur++)
+ {
+ tree base = chkp_can_be_shared (elem)
+ ? elem
+ : unshare_expr (elem);
+ tree arr_elem = chkp_build_array_ref (base, etype,
+ TYPE_SIZE (etype),
+ cur);
+ chkp_find_bounds_for_elem (arr_elem, all_bounds, offs + cur * esize,
+ iter);
+ }
+ }
+}
+
/* Fill HAVE_BOUND output bitmap with information about
bounds requred for object of type TYPE.

@@ -1306,6 +1463,223 @@ chkp_find_bound_slots (const_tree type)
return res;
}

+/* Add bound arguments to call statement pointed by GSI.
+ Also performs a replacement of user checker builtins calls
+ with internal ones. */
+
+static void
+chkp_add_bounds_to_call_stmt (gimple_stmt_iterator *gsi)
+{
+ gimple call = gsi_stmt (*gsi);
+ unsigned arg_no = 0;
+ tree fndecl = gimple_call_fndecl (call);
+ tree fntype;
+ tree first_formal_arg;
+ tree arg;
+ bool use_fntype = false;
+ tree op;
+ ssa_op_iter iter;
+ gimple new_call;
+
+ /* Do nothing for internal functions. */
+ if (gimple_call_internal_p (call))
+ return;
+
+ fntype = TREE_TYPE (TREE_TYPE (gimple_call_fn (call)));
+
+ /* Do nothing if back-end builtin is called. */
+ if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
+ return;
+
+ /* Do nothing for some middle-end builtins. */
+ if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+ && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_OBJECT_SIZE)
+ return;
+
+ /* Donothing for calls to legacy functions. */
+ if (fndecl
+ && lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (fndecl)))
+ return;
+
+ /* Ignore CHKP_INIT_PTR_BOUNDS, CHKP_NULL_PTR_BOUNDS
+ and CHKP_COPY_PTR_BOUNDS. */
+ if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+ && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_INIT_PTR_BOUNDS
+ || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_NULL_PTR_BOUNDS
+ || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_COPY_PTR_BOUNDS
+ || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_SET_PTR_BOUNDS))
+ return;
+
+ /* Check user builtins are replaced with checks. */
+ if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+ && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_LBOUNDS
+ || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_UBOUNDS
+ || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_BOUNDS))
+ {
+ chkp_replace_address_check_builtin (gsi, integer_minus_one_node);
+ return;
+ }
+
+ /* Check user builtins are replaced with bound extract. */
+ if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+ && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_LBOUND
+ || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_UBOUND))
+ {
+ chkp_replace_extract_builtin (gsi);
+ return;
+ }
+
+ /* BUILT_IN_CHKP_NARROW_PTR_BOUNDS call is replaced with
+ target narrow bounds call. */
+ if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+ && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_NARROW_PTR_BOUNDS)
+ {
+ tree arg = gimple_call_arg (call, 1);
+ tree bounds = chkp_find_bounds (arg, gsi);
+
+ gimple_call_set_fndecl (call, chkp_narrow_bounds_fndecl);
+ gimple_call_set_arg (call, 1, bounds);
+ update_stmt (call);
+
+ return;
+ }
+
+ /* BUILT_IN_CHKP_STORE_PTR_BOUNDS call is replaced with
+ bndstx call. */
+ if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+ && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_STORE_PTR_BOUNDS)
+ {
+ tree addr = gimple_call_arg (call, 0);
+ tree ptr = gimple_call_arg (call, 1);
+ tree bounds = chkp_find_bounds (ptr, gsi);
+ gimple_stmt_iterator iter = gsi_for_stmt (call);
+
+ chkp_build_bndstx (addr, ptr, bounds, gsi);
+ gsi_remove (&iter, true);
+
+ return;
+ }
+
+ if (!flag_chkp_instrument_calls)
+ return;
+
+ /* Avoid instrumented builtin functions for now. Due to IPA
+ it also means we have to avoid instrumentation of indirect
+ calls. */
+ if (fndecl && DECL_BUILT_IN_CLASS (fndecl) != NOT_BUILT_IN)
+ return;
+
+ /* If function decl is available then use it for
+ formal arguments list. Otherwise use function type. */
+ if (fndecl && DECL_ARGUMENTS (fndecl))
+ first_formal_arg = DECL_ARGUMENTS (fndecl);
+ else
+ {
+ first_formal_arg = TYPE_ARG_TYPES (fntype);
+ use_fntype = true;
+ }
+
+ /* Fill vector of new call args. */
+ vec<tree> new_args = vNULL;
+ new_args.create (gimple_call_num_args (call));
+ arg = first_formal_arg;
+ for (arg_no = 0; arg_no < gimple_call_num_args (call); arg_no++)
+ {
+ tree call_arg = gimple_call_arg (call, arg_no);
+ tree type;
+
+ /* Get arg type using formal argument description
+ or actual argument type. */
+ if (arg)
+ if (use_fntype)
+ if (TREE_VALUE (arg) != void_type_node)
+ {
+ type = TREE_VALUE (arg);
+ arg = TREE_CHAIN (arg);
+ }
+ else
+ type = TREE_TYPE (call_arg);
+ else
+ {
+ type = TREE_TYPE (arg);
+ arg = TREE_CHAIN (arg);
+ }
+ else
+ type = TREE_TYPE (call_arg);
+
+ new_args.safe_push (call_arg);
+
+ if (BOUNDED_TYPE_P (type)
+ || pass_by_reference (NULL, TYPE_MODE (type), type, true))
+ new_args.safe_push (chkp_find_bounds (call_arg, gsi));
+ else if (chkp_type_has_pointer (type))
+ {
+ HOST_WIDE_INT max_bounds
+ = TREE_INT_CST_LOW (TYPE_SIZE (type)) / POINTER_SIZE;
+ tree *all_bounds = (tree *)xmalloc (sizeof (tree) * max_bounds);
+ HOST_WIDE_INT bnd_no;
+
+ memset (all_bounds, 0, sizeof (tree) * max_bounds);
+
+ chkp_find_bounds_for_elem (call_arg, all_bounds, 0, gsi);
+
+ for (bnd_no = 0; bnd_no < max_bounds; bnd_no++)
+ if (all_bounds[bnd_no])
+ new_args.safe_push (all_bounds[bnd_no]);
+
+ free (all_bounds);
+ }
+ }
+
+ if (new_args.length () == gimple_call_num_args (call))
+ new_call = call;
+ else
+ {
+ new_call = gimple_build_call_vec (gimple_op (call, 1), new_args);
+ gimple_call_set_lhs (new_call, gimple_call_lhs (call));
+ gimple_call_copy_flags (new_call, call);
+ }
+ new_args.release ();
+
+ /* If we call built-in function and pass no bounds then
+ we do not need to change anything. */
+ if (new_call == call
+ && fndecl
+ && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+ && fndecl == builtin_decl_explicit (DECL_FUNCTION_CODE (fndecl)))
+ return;
+
+ /* For direct calls fndecl is replaced with instrumented version. */
+ if (fndecl)
+ {
+ tree new_decl = chkp_maybe_create_clone (fndecl)->decl;
+ gimple_call_set_fndecl (new_call, new_decl);
+ gimple_call_set_fntype (new_call, TREE_TYPE (new_decl));
+ }
+ /* For indirect call we should fix function pointer type if
+ pass some bounds. */
+ else if (new_call != call)
+ {
+ tree type = gimple_call_fntype (call);
+ type = chkp_copy_function_type_adding_bounds (type);
+ gimple_call_set_fntype (new_call, type);
+ }
+
+ /* replace old call statement with the new one. */
+ if (call != new_call)
+ {
+ FOR_EACH_SSA_TREE_OPERAND (op, call, iter, SSA_OP_ALL_DEFS)
+ {
+ SSA_NAME_DEF_STMT (op) = new_call;
+ }
+ gsi_replace (gsi, new_call, true);
+ }
+ else
+ update_stmt (new_call);
+
+ gimple_call_set_with_bounds (new_call, true);
+}
+
/* Return constant static bounds var with specified LB and UB
if such var exists in varpool. Return NULL otherwise. */
static tree
@@ -3314,7 +3688,8 @@ chkp_replace_function_pointers (gimple_stmt_iterator *gsi)
walk_gimple_stmt (gsi, NULL, chkp_replace_function_pointer, NULL);
}

-/* This function instruments all statements working with memory. */
+/* This function instruments all statements working with memory,
+ calls and rets. */
static void
chkp_instrument_function (void)
{
@@ -3358,12 +3733,22 @@ chkp_instrument_function (void)

case GIMPLE_RETURN:
if (gimple_return_retval (s) != NULL_TREE)
- chkp_process_stmt (&i, gimple_return_retval (s),
- gimple_location (s),
- integer_zero_node,
- NULL_TREE, NULL_TREE, safe);
+ {
+ chkp_process_stmt (&i, gimple_return_retval (s),
+ gimple_location (s),
+ integer_zero_node,
+ NULL_TREE, NULL_TREE, safe);
+
+ /* Additionall we need to add bounds
+ to return statement. */
+ chkp_add_bounds_to_ret_stmt (&i);
+ }
break;

+ case GIMPLE_CALL:
+ chkp_add_bounds_to_call_stmt (&i);
+ break;
+
default:
;
}

Loading...