From f80b5ea1605c9f9408c5aa386ba71c16d918ebbf Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Wed, 22 Apr 2015 10:21:45 +0000 Subject: Imported from /home/lorry/working-area/delta_gcc-tarball/gcc-5.1.0.tar.bz2. --- gcc/tree-chkp.c | 4386 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4386 insertions(+) create mode 100644 gcc/tree-chkp.c (limited to 'gcc/tree-chkp.c') diff --git a/gcc/tree-chkp.c b/gcc/tree-chkp.c new file mode 100644 index 0000000000..8c5a628a9a --- /dev/null +++ b/gcc/tree-chkp.c @@ -0,0 +1,4386 @@ +/* Pointer Bounds Checker insrumentation pass. + Copyright (C) 2014-2015 Free Software Foundation, Inc. + Contributed by Ilya Enkovich (ilya.enkovich@intel.com) + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "hash-set.h" +#include "machmode.h" +#include "vec.h" +#include "double-int.h" +#include "input.h" +#include "alias.h" +#include "symtab.h" +#include "options.h" +#include "wide-int.h" +#include "inchash.h" +#include "tree.h" +#include "fold-const.h" +#include "stor-layout.h" +#include "varasm.h" +#include "target.h" +#include "tree-iterator.h" +#include "tree-cfg.h" +#include "langhooks.h" +#include "tree-pass.h" +#include "diagnostic.h" +#include "ggc.h" +#include "is-a.h" +#include "cfgloop.h" +#include "stringpool.h" +#include "tree-ssa-alias.h" +#include "tree-ssanames.h" +#include "tree-ssa-operands.h" +#include "tree-ssa-address.h" +#include "tree-ssa.h" +#include "predict.h" +#include "dominance.h" +#include "cfg.h" +#include "basic-block.h" +#include "tree-ssa-loop-niter.h" +#include "gimple-expr.h" +#include "gimple.h" +#include "tree-phinodes.h" +#include "gimple-ssa.h" +#include "ssa-iterators.h" +#include "gimple-pretty-print.h" +#include "gimple-iterator.h" +#include "gimplify.h" +#include "gimplify-me.h" +#include "print-tree.h" +#include "hashtab.h" +#include "tm.h" +#include "hard-reg-set.h" +#include "function.h" +#include "rtl.h" +#include "flags.h" +#include "statistics.h" +#include "real.h" +#include "fixed-value.h" +#include "insn-config.h" +#include "expmed.h" +#include "dojump.h" +#include "explow.h" +#include "calls.h" +#include "emit-rtl.h" +#include "stmt.h" +#include "expr.h" +#include "tree-ssa-propagate.h" +#include "gimple-fold.h" +#include "tree-chkp.h" +#include "gimple-walk.h" +#include "rtl.h" /* For MEM_P, assign_temp. */ +#include "tree-dfa.h" +#include "ipa-ref.h" +#include "lto-streamer.h" +#include "cgraph.h" +#include "ipa-chkp.h" +#include "params.h" + +/* Pointer Bounds Checker instruments code with memory checks to find + out-of-bounds memory accesses. Checks are performed by computing + bounds for each pointer and then comparing address of accessed + memory before pointer dereferencing. + + 1. Function clones. + + See ipa-chkp.c. + + 2. Instrumentation. + + There are few things to instrument: + + a) Memory accesses - add checker calls to check address of accessed memory + against bounds of dereferenced pointer. Obviously safe memory + accesses like static variable access does not have to be instrumented + with checks. + + Example: + + val_2 = *p_1; + + with 4 bytes access is transformed into: + + __builtin___chkp_bndcl (__bound_tmp.1_3, p_1); + D.1_4 = p_1 + 3; + __builtin___chkp_bndcu (__bound_tmp.1_3, D.1_4); + val_2 = *p_1; + + where __bound_tmp.1_3 are bounds computed for pointer p_1, + __builtin___chkp_bndcl is a lower bound check and + __builtin___chkp_bndcu is an upper bound check. + + b) Pointer stores. + + When pointer is stored in memory we need to store its bounds. To + achieve compatibility of instrumented code with regular codes + we have to keep data layout and store bounds in special bound tables + via special checker call. Implementation of bounds table may vary for + different platforms. It has to associate pointer value and its + location (it is required because we may have two equal pointers + with different bounds stored in different places) with bounds. + Another checker builtin allows to get bounds for specified pointer + loaded from specified location. + + Example: + + buf1[i_1] = &buf2; + + is transformed into: + + buf1[i_1] = &buf2; + D.1_2 = &buf1[i_1]; + __builtin___chkp_bndstx (D.1_2, &buf2, __bound_tmp.1_2); + + where __bound_tmp.1_2 are bounds of &buf2. + + c) Static initialization. + + The special case of pointer store is static pointer initialization. + Bounds initialization is performed in a few steps: + - register all static initializations in front-end using + chkp_register_var_initializer + - when file compilation finishes we create functions with special + attribute 'chkp ctor' and put explicit initialization code + (assignments) for all statically initialized pointers. + - when checker constructor is compiled checker pass adds required + bounds initialization for all statically initialized pointers + - since we do not actually need excess pointers initialization + in checker constructor we remove such assignments from them + + d) Calls. + + For each call in the code we add additional arguments to pass + bounds for pointer arguments. We determine type of call arguments + using arguments list from function declaration; if function + declaration is not available we use function type; otherwise + (e.g. for unnamed arguments) we use type of passed value. Function + declaration/type is replaced with the instrumented one. + + Example: + + val_1 = foo (&buf1, &buf2, &buf1, 0); + + is translated into: + + val_1 = foo.chkp (&buf1, __bound_tmp.1_2, &buf2, __bound_tmp.1_3, + &buf1, __bound_tmp.1_2, 0); + + e) Returns. + + If function returns a pointer value we have to return bounds also. + A new operand was added for return statement to hold returned bounds. + + Example: + + return &_buf1; + + is transformed into + + return &_buf1, __bound_tmp.1_1; + + 3. Bounds computation. + + Compiler is fully responsible for computing bounds to be used for each + memory access. The first step for bounds computation is to find the + origin of pointer dereferenced for memory access. Basing on pointer + origin we define a way to compute its bounds. There are just few + possible cases: + + a) Pointer is returned by call. + + In this case we use corresponding checker builtin method to obtain returned + bounds. + + Example: + + buf_1 = malloc (size_2); + foo (buf_1); + + is translated into: + + buf_1 = malloc (size_2); + __bound_tmp.1_3 = __builtin___chkp_bndret (buf_1); + foo (buf_1, __bound_tmp.1_3); + + b) Pointer is an address of an object. + + In this case compiler tries to compute objects size and create corresponding + bounds. If object has incomplete type then special checker builtin is used to + obtain its size at runtime. + + Example: + + foo () + { + __bound_tmp.3; + static int buf[100]; + + : + __bound_tmp.3_2 = __builtin___chkp_bndmk (&buf, 400); + + : + return &buf, __bound_tmp.3_2; + } + + Example: + + Address of an object 'extern int buf[]' with incomplete type is + returned. + + foo () + { + __bound_tmp.4; + long unsigned int __size_tmp.3; + + : + __size_tmp.3_4 = __builtin_ia32_sizeof (buf); + __bound_tmp.4_3 = __builtin_ia32_bndmk (&buf, __size_tmp.3_4); + + : + return &buf, __bound_tmp.4_3; + } + + c) Pointer is the result of object narrowing. + + It happens when we use pointer to an object to compute pointer to a part + of an object. E.g. we take pointer to a field of a structure. In this + case we perform bounds intersection using bounds of original object and + bounds of object's part (which are computed basing on its type). + + There may be some debatable questions about when narrowing should occur + and when it should not. To avoid false bound violations in correct + programs we do not perform narrowing when address of an array element is + obtained (it has address of the whole array) and when address of the first + structure field is obtained (because it is guaranteed to be equal to + address of the whole structure and it is legal to cast it back to structure). + + Default narrowing behavior may be changed using compiler flags. + + Example: + + In this example address of the second structure field is returned. + + foo (struct A * p, __bounds_type __bounds_of_p) + { + __bound_tmp.3; + int * _2; + int * _5; + + : + _5 = &p_1(D)->second_field; + __bound_tmp.3_6 = __builtin___chkp_bndmk (_5, 4); + __bound_tmp.3_8 = __builtin___chkp_intersect (__bound_tmp.3_6, + __bounds_of_p_3(D)); + _2 = &p_1(D)->second_field; + return _2, __bound_tmp.3_8; + } + + Example: + + In this example address of the first field of array element is returned. + + foo (struct A * p, __bounds_type __bounds_of_p, int i) + { + long unsigned int _3; + long unsigned int _4; + struct A * _6; + int * _7; + + : + _3 = (long unsigned int) i_1(D); + _4 = _3 * 8; + _6 = p_5(D) + _4; + _7 = &_6->first_field; + return _7, __bounds_of_p_2(D); + } + + + d) Pointer is the result of pointer arithmetic or type cast. + + In this case bounds of the base pointer are used. In case of binary + operation producing a pointer we are analyzing data flow further + looking for operand's bounds. One operand is considered as a base + if it has some valid bounds. If we fall into a case when none of + operands (or both of them) has valid bounds, a default bounds value + is used. + + Trying to find out bounds for binary operations we may fall into + cyclic dependencies for pointers. To avoid infinite recursion all + walked phi nodes instantly obtain corresponding bounds but created + bounds are marked as incomplete. It helps us to stop DF walk during + bounds search. + + When we reach pointer source, some args of incomplete bounds phi obtain + valid bounds and those values are propagated further through phi nodes. + If no valid bounds were found for phi node then we mark its result as + invalid bounds. Process stops when all incomplete bounds become either + valid or invalid and we are able to choose a pointer base. + + e) Pointer is loaded from the memory. + + In this case we just need to load bounds from the bounds table. + + Example: + + foo () + { + __bound_tmp.3; + static int * buf; + int * _2; + + : + _2 = buf; + __bound_tmp.3_4 = __builtin___chkp_bndldx (&buf, _2); + return _2, __bound_tmp.3_4; + } + +*/ + +typedef void (*assign_handler)(tree, tree, void *); + +static tree chkp_get_zero_bounds (); +static tree chkp_find_bounds (tree ptr, gimple_stmt_iterator *iter); +static tree chkp_find_bounds_loaded (tree ptr, tree ptr_src, + gimple_stmt_iterator *iter); +static void chkp_parse_array_and_component_ref (tree node, tree *ptr, + tree *elt, bool *safe, + bool *bitfield, + tree *bounds, + gimple_stmt_iterator *iter, + bool innermost_bounds); + +#define chkp_bndldx_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDLDX)) +#define chkp_bndstx_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDSTX)) +#define chkp_checkl_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDCL)) +#define chkp_checku_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDCU)) +#define chkp_bndmk_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDMK)) +#define chkp_ret_bnd_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDRET)) +#define chkp_intersect_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_INTERSECT)) +#define chkp_narrow_bounds_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_NARROW)) +#define chkp_sizeof_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_SIZEOF)) +#define chkp_extract_lower_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_EXTRACT_LOWER)) +#define chkp_extract_upper_fndecl \ + (targetm.builtin_chkp_function (BUILT_IN_CHKP_EXTRACT_UPPER)) + +static GTY (()) tree chkp_uintptr_type; + +static GTY (()) tree chkp_zero_bounds_var; +static GTY (()) tree chkp_none_bounds_var; + +static GTY (()) basic_block entry_block; +static GTY (()) tree zero_bounds; +static GTY (()) tree none_bounds; +static GTY (()) tree incomplete_bounds; +static GTY (()) tree tmp_var; +static GTY (()) tree size_tmp_var; +static GTY (()) bitmap chkp_abnormal_copies; + +struct hash_set *chkp_invalid_bounds; +struct hash_set *chkp_completed_bounds_set; +struct hash_map *chkp_reg_bounds; +struct hash_map *chkp_bound_vars; +struct hash_map *chkp_reg_addr_bounds; +struct hash_map *chkp_incomplete_bounds_map; +struct hash_map *chkp_bounds_map; +struct hash_map *chkp_static_var_bounds; + +static bool in_chkp_pass; + +#define CHKP_BOUND_TMP_NAME "__bound_tmp" +#define CHKP_SIZE_TMP_NAME "__size_tmp" +#define CHKP_BOUNDS_OF_SYMBOL_PREFIX "__chkp_bounds_of_" +#define CHKP_STRING_BOUNDS_PREFIX "__chkp_string_bounds_" +#define CHKP_VAR_BOUNDS_PREFIX "__chkp_var_bounds_" +#define CHKP_ZERO_BOUNDS_VAR_NAME "__chkp_zero_bounds" +#define CHKP_NONE_BOUNDS_VAR_NAME "__chkp_none_bounds" + +/* Static checker constructors may become very large and their + compilation with optimization may take too much time. + Therefore we put a limit to number of statements in one + constructor. Tests with 100 000 statically initialized + pointers showed following compilation times on Sandy Bridge + server (used -O2): + limit 100 => ~18 sec. + limit 300 => ~22 sec. + limit 1000 => ~30 sec. + limit 3000 => ~49 sec. + limit 5000 => ~55 sec. + limit 10000 => ~76 sec. + limit 100000 => ~532 sec. */ +#define MAX_STMTS_IN_STATIC_CHKP_CTOR (PARAM_VALUE (PARAM_CHKP_MAX_CTOR_SIZE)) + +struct chkp_ctor_stmt_list +{ + tree stmts; + int avail; +}; + +/* Return 1 if function FNDECL is instrumented by Pointer + Bounds Checker. */ +bool +chkp_function_instrumented_p (tree fndecl) +{ + return fndecl + && lookup_attribute ("chkp instrumented", DECL_ATTRIBUTES (fndecl)); +} + +/* Mark function FNDECL as instrumented. */ +void +chkp_function_mark_instrumented (tree fndecl) +{ + if (chkp_function_instrumented_p (fndecl)) + return; + + DECL_ATTRIBUTES (fndecl) + = tree_cons (get_identifier ("chkp instrumented"), NULL, + DECL_ATTRIBUTES (fndecl)); +} + +/* Return true when STMT is builtin call to instrumentation function + corresponding to CODE. */ + +bool +chkp_gimple_call_builtin_p (gimple call, + enum built_in_function code) +{ + tree fndecl; + if (is_gimple_call (call) + && (fndecl = targetm.builtin_chkp_function (code)) + && gimple_call_fndecl (call) == fndecl) + return true; + return false; +} + +/* Emit code to store zero bounds for PTR located at MEM. */ +void +chkp_expand_bounds_reset_for_mem (tree mem, tree ptr) +{ + tree zero_bnd, bnd, addr, bndstx; + + if (flag_chkp_use_static_const_bounds) + zero_bnd = chkp_get_zero_bounds_var (); + else + zero_bnd = chkp_build_make_bounds_call (integer_zero_node, + integer_zero_node); + bnd = make_tree (pointer_bounds_type_node, + assign_temp (pointer_bounds_type_node, 0, 1)); + addr = build1 (ADDR_EXPR, + build_pointer_type (TREE_TYPE (mem)), mem); + bndstx = chkp_build_bndstx_call (addr, ptr, bnd); + + expand_assignment (bnd, zero_bnd, false); + expand_normal (bndstx); +} + +/* Build retbnd call for returned value RETVAL. + + If BNDVAL is not NULL then result is stored + in it. Otherwise a temporary is created to + hold returned value. + + GSI points to a position for a retbnd call + and is set to created stmt. + + Cgraph edge is created for a new call if + UPDATE_EDGE is 1. + + Obtained bounds are returned. */ +tree +chkp_insert_retbnd_call (tree bndval, tree retval, + gimple_stmt_iterator *gsi) +{ + gimple call; + + if (!bndval) + bndval = create_tmp_reg (pointer_bounds_type_node, "retbnd"); + + call = gimple_build_call (chkp_ret_bnd_fndecl, 1, retval); + gimple_call_set_lhs (call, bndval); + gsi_insert_after (gsi, call, GSI_CONTINUE_LINKING); + + return bndval; +} + +/* Mark statement S to not be instrumented. */ +static void +chkp_mark_stmt (gimple s) +{ + gimple_set_plf (s, GF_PLF_1, true); +} + +/* Mark statement S to be instrumented. */ +static void +chkp_unmark_stmt (gimple s) +{ + gimple_set_plf (s, GF_PLF_1, false); +} + +/* Return 1 if statement S should not be instrumented. */ +static bool +chkp_marked_stmt_p (gimple s) +{ + return gimple_plf (s, GF_PLF_1); +} + +/* Get var to be used for bound temps. */ +static tree +chkp_get_tmp_var (void) +{ + if (!tmp_var) + tmp_var = create_tmp_reg (pointer_bounds_type_node, CHKP_BOUND_TMP_NAME); + + return tmp_var; +} + +/* Get SSA_NAME to be used as temp. */ +static tree +chkp_get_tmp_reg (gimple stmt) +{ + if (in_chkp_pass) + return make_ssa_name (chkp_get_tmp_var (), stmt); + + return make_temp_ssa_name (pointer_bounds_type_node, stmt, + CHKP_BOUND_TMP_NAME); +} + +/* Get var to be used for size temps. */ +static tree +chkp_get_size_tmp_var (void) +{ + if (!size_tmp_var) + size_tmp_var = create_tmp_reg (chkp_uintptr_type, CHKP_SIZE_TMP_NAME); + + return size_tmp_var; +} + +/* Register bounds BND for address of OBJ. */ +static void +chkp_register_addr_bounds (tree obj, tree bnd) +{ + if (bnd == incomplete_bounds) + return; + + chkp_reg_addr_bounds->put (obj, bnd); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Regsitered bound "); + print_generic_expr (dump_file, bnd, 0); + fprintf (dump_file, " for address of "); + print_generic_expr (dump_file, obj, 0); + fprintf (dump_file, "\n"); + } +} + +/* Return bounds registered for address of OBJ. */ +static tree +chkp_get_registered_addr_bounds (tree obj) +{ + tree *slot = chkp_reg_addr_bounds->get (obj); + return slot ? *slot : NULL_TREE; +} + +/* Mark BOUNDS as completed. */ +static void +chkp_mark_completed_bounds (tree bounds) +{ + chkp_completed_bounds_set->add (bounds); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Marked bounds "); + print_generic_expr (dump_file, bounds, 0); + fprintf (dump_file, " as completed\n"); + } +} + +/* Return 1 if BOUNDS were marked as completed and 0 otherwise. */ +static bool +chkp_completed_bounds (tree bounds) +{ + return chkp_completed_bounds_set->contains (bounds); +} + +/* Clear comleted bound marks. */ +static void +chkp_erase_completed_bounds (void) +{ + delete chkp_completed_bounds_set; + chkp_completed_bounds_set = new hash_set; +} + +/* Mark BOUNDS associated with PTR as incomplete. */ +static void +chkp_register_incomplete_bounds (tree bounds, tree ptr) +{ + chkp_incomplete_bounds_map->put (bounds, ptr); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Regsitered incomplete bounds "); + print_generic_expr (dump_file, bounds, 0); + fprintf (dump_file, " for "); + print_generic_expr (dump_file, ptr, 0); + fprintf (dump_file, "\n"); + } +} + +/* Return 1 if BOUNDS are incomplete and 0 otherwise. */ +static bool +chkp_incomplete_bounds (tree bounds) +{ + if (bounds == incomplete_bounds) + return true; + + if (chkp_completed_bounds (bounds)) + return false; + + return chkp_incomplete_bounds_map->get (bounds) != NULL; +} + +/* Clear incomleted bound marks. */ +static void +chkp_erase_incomplete_bounds (void) +{ + delete chkp_incomplete_bounds_map; + chkp_incomplete_bounds_map = new hash_map; +} + +/* Build and return bndmk call which creates bounds for structure + pointed by PTR. Structure should have complete type. */ +tree +chkp_make_bounds_for_struct_addr (tree ptr) +{ + tree type = TREE_TYPE (ptr); + tree size; + + gcc_assert (POINTER_TYPE_P (type)); + + size = TYPE_SIZE (TREE_TYPE (type)); + + gcc_assert (size); + + return build_call_nary (pointer_bounds_type_node, + build_fold_addr_expr (chkp_bndmk_fndecl), + 2, ptr, size); +} + +/* Traversal function for chkp_may_finish_incomplete_bounds. + Set RES to 0 if at least one argument of phi statement + defining bounds (passed in KEY arg) is unknown. + Traversal stops when first unknown phi argument is found. */ +bool +chkp_may_complete_phi_bounds (tree const &bounds, tree *slot ATTRIBUTE_UNUSED, + bool *res) +{ + gimple phi; + unsigned i; + + gcc_assert (TREE_CODE (bounds) == SSA_NAME); + + phi = SSA_NAME_DEF_STMT (bounds); + + gcc_assert (phi && gimple_code (phi) == GIMPLE_PHI); + + for (i = 0; i < gimple_phi_num_args (phi); i++) + { + tree phi_arg = gimple_phi_arg_def (phi, i); + if (!phi_arg) + { + *res = false; + /* Do not need to traverse further. */ + return false; + } + } + + return true; +} + +/* Return 1 if all phi nodes created for bounds have their + arguments computed. */ +static bool +chkp_may_finish_incomplete_bounds (void) +{ + bool res = true; + + chkp_incomplete_bounds_map + ->traverse (&res); + + return res; +} + +/* Helper function for chkp_finish_incomplete_bounds. + Recompute args for bounds phi node. */ +bool +chkp_recompute_phi_bounds (tree const &bounds, tree *slot, + void *res ATTRIBUTE_UNUSED) +{ + tree ptr = *slot; + gphi *bounds_phi; + gphi *ptr_phi; + unsigned i; + + gcc_assert (TREE_CODE (bounds) == SSA_NAME); + gcc_assert (TREE_CODE (ptr) == SSA_NAME); + + bounds_phi = as_a (SSA_NAME_DEF_STMT (bounds)); + ptr_phi = as_a (SSA_NAME_DEF_STMT (ptr)); + + for (i = 0; i < gimple_phi_num_args (bounds_phi); i++) + { + tree ptr_arg = gimple_phi_arg_def (ptr_phi, i); + tree bound_arg = chkp_find_bounds (ptr_arg, NULL); + + add_phi_arg (bounds_phi, bound_arg, + gimple_phi_arg_edge (ptr_phi, i), + UNKNOWN_LOCATION); + } + + return true; +} + +/* Mark BOUNDS as invalid. */ +static void +chkp_mark_invalid_bounds (tree bounds) +{ + chkp_invalid_bounds->add (bounds); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Marked bounds "); + print_generic_expr (dump_file, bounds, 0); + fprintf (dump_file, " as invalid\n"); + } +} + +/* Return 1 if BOUNDS were marked as invalid and 0 otherwise. */ +static bool +chkp_valid_bounds (tree bounds) +{ + if (bounds == zero_bounds || bounds == none_bounds) + return false; + + return !chkp_invalid_bounds->contains (bounds); +} + +/* Helper function for chkp_finish_incomplete_bounds. + Check all arguments of phi nodes trying to find + valid completed bounds. If there is at least one + such arg then bounds produced by phi node are marked + as valid completed bounds and all phi args are + recomputed. */ +bool +chkp_find_valid_phi_bounds (tree const &bounds, tree *slot, bool *res) +{ + gimple phi; + unsigned i; + + gcc_assert (TREE_CODE (bounds) == SSA_NAME); + + if (chkp_completed_bounds (bounds)) + return true; + + phi = SSA_NAME_DEF_STMT (bounds); + + gcc_assert (phi && gimple_code (phi) == GIMPLE_PHI); + + for (i = 0; i < gimple_phi_num_args (phi); i++) + { + tree phi_arg = gimple_phi_arg_def (phi, i); + + gcc_assert (phi_arg); + + if (chkp_valid_bounds (phi_arg) && !chkp_incomplete_bounds (phi_arg)) + { + *res = true; + chkp_mark_completed_bounds (bounds); + chkp_recompute_phi_bounds (bounds, slot, NULL); + return true; + } + } + + return true; +} + +/* Helper function for chkp_finish_incomplete_bounds. + Marks all incompleted bounds as invalid. */ +bool +chkp_mark_invalid_bounds_walker (tree const &bounds, + tree *slot ATTRIBUTE_UNUSED, + void *res ATTRIBUTE_UNUSED) +{ + if (!chkp_completed_bounds (bounds)) + { + chkp_mark_invalid_bounds (bounds); + chkp_mark_completed_bounds (bounds); + } + return true; +} + +/* When all bound phi nodes have all their args computed + we have enough info to find valid bounds. We iterate + through all incompleted bounds searching for valid + bounds. Found valid bounds are marked as completed + and all remaining incompleted bounds are recomputed. + Process continues until no new valid bounds may be + found. All remained incompleted bounds are marked as + invalid (i.e. have no valid source of bounds). */ +static void +chkp_finish_incomplete_bounds (void) +{ + bool found_valid; + + while (found_valid) + { + found_valid = false; + + chkp_incomplete_bounds_map-> + traverse (&found_valid); + + if (found_valid) + chkp_incomplete_bounds_map-> + traverse (NULL); + } + + chkp_incomplete_bounds_map-> + traverse (NULL); + chkp_incomplete_bounds_map-> + traverse (NULL); + + chkp_erase_completed_bounds (); + chkp_erase_incomplete_bounds (); +} + +/* Return 1 if type TYPE is a pointer type or a + structure having a pointer type as one of its fields. + Otherwise return 0. */ +bool +chkp_type_has_pointer (const_tree type) +{ + bool res = false; + + if (BOUNDED_TYPE_P (type)) + res = true; + 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) + res = res || chkp_type_has_pointer (TREE_TYPE (field)); + } + else if (TREE_CODE (type) == ARRAY_TYPE) + res = chkp_type_has_pointer (TREE_TYPE (type)); + + return res; +} + +unsigned +chkp_type_bounds_count (const_tree type) +{ + unsigned res = 0; + + if (!type) + res = 0; + else if (BOUNDED_TYPE_P (type)) + res = 1; + else if (RECORD_OR_UNION_TYPE_P (type)) + { + bitmap have_bound; + + bitmap_obstack_initialize (NULL); + have_bound = BITMAP_ALLOC (NULL); + chkp_find_bound_slots (type, have_bound); + res = bitmap_count_bits (have_bound); + BITMAP_FREE (have_bound); + bitmap_obstack_release (NULL); + } + + return res; +} + +/* Get bounds associated with NODE via + chkp_set_bounds call. */ +tree +chkp_get_bounds (tree node) +{ + tree *slot; + + if (!chkp_bounds_map) + return NULL_TREE; + + slot = chkp_bounds_map->get (node); + return slot ? *slot : NULL_TREE; +} + +/* Associate bounds VAL with NODE. */ +void +chkp_set_bounds (tree node, tree val) +{ + if (!chkp_bounds_map) + chkp_bounds_map = new hash_map; + + chkp_bounds_map->put (node, val); +} + +/* Check if statically initialized variable VAR require + static bounds initialization. If VAR is added into + bounds initlization list then 1 is returned. Otherwise + return 0. */ +extern bool +chkp_register_var_initializer (tree var) +{ + if (!flag_check_pointer_bounds + || DECL_INITIAL (var) == error_mark_node) + return false; + + gcc_assert (TREE_CODE (var) == VAR_DECL); + gcc_assert (DECL_INITIAL (var)); + + if (TREE_STATIC (var) + && chkp_type_has_pointer (TREE_TYPE (var))) + { + varpool_node::get_create (var)->need_bounds_init = 1; + return true; + } + + return false; +} + +/* Helper function for chkp_finish_file. + + Add new modification statement (RHS is assigned to LHS) + into list of static initializer statementes (passed in ARG). + If statements list becomes too big, emit checker constructor + and start the new one. */ +static void +chkp_add_modification_to_stmt_list (tree lhs, + tree rhs, + void *arg) +{ + struct chkp_ctor_stmt_list *stmts = (struct chkp_ctor_stmt_list *)arg; + tree modify; + + if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs))) + rhs = build1 (CONVERT_EXPR, TREE_TYPE (lhs), rhs); + + modify = build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, rhs); + append_to_statement_list (modify, &stmts->stmts); + + stmts->avail--; +} + +/* Build and return ADDR_EXPR for specified object OBJ. */ +static tree +chkp_build_addr_expr (tree obj) +{ + return TREE_CODE (obj) == TARGET_MEM_REF + ? tree_mem_ref_addr (ptr_type_node, obj) + : build_fold_addr_expr (obj); +} + +/* Helper function for chkp_finish_file. + Initialize bound variable BND_VAR with bounds of variable + VAR to statements list STMTS. If statements list becomes + too big, emit checker constructor and start the new one. */ +static void +chkp_output_static_bounds (tree bnd_var, tree var, + struct chkp_ctor_stmt_list *stmts) +{ + tree lb, ub, size; + + if (TREE_CODE (var) == STRING_CST) + { + lb = build1 (CONVERT_EXPR, size_type_node, chkp_build_addr_expr (var)); + size = build_int_cst (size_type_node, TREE_STRING_LENGTH (var) - 1); + } + else if (DECL_SIZE (var) + && !chkp_variable_size_type (TREE_TYPE (var))) + { + /* Compute bounds using statically known size. */ + lb = build1 (CONVERT_EXPR, size_type_node, chkp_build_addr_expr (var)); + size = size_binop (MINUS_EXPR, DECL_SIZE_UNIT (var), size_one_node); + } + else + { + /* Compute bounds using dynamic size. */ + tree call; + + lb = build1 (CONVERT_EXPR, size_type_node, chkp_build_addr_expr (var)); + call = build1 (ADDR_EXPR, + build_pointer_type (TREE_TYPE (chkp_sizeof_fndecl)), + chkp_sizeof_fndecl); + size = build_call_nary (TREE_TYPE (TREE_TYPE (chkp_sizeof_fndecl)), + call, 1, var); + + if (flag_chkp_zero_dynamic_size_as_infinite) + { + tree max_size, cond; + + max_size = build2 (MINUS_EXPR, size_type_node, size_zero_node, lb); + cond = build2 (NE_EXPR, boolean_type_node, size, size_zero_node); + size = build3 (COND_EXPR, size_type_node, cond, size, max_size); + } + + size = size_binop (MINUS_EXPR, size, size_one_node); + } + + ub = size_binop (PLUS_EXPR, lb, size); + stmts->avail -= targetm.chkp_initialize_bounds (bnd_var, lb, ub, + &stmts->stmts); + if (stmts->avail <= 0) + { + cgraph_build_static_cdtor ('B', stmts->stmts, + MAX_RESERVED_INIT_PRIORITY + 2); + stmts->avail = MAX_STMTS_IN_STATIC_CHKP_CTOR; + stmts->stmts = NULL; + } +} + +/* Return entry block to be used for checker initilization code. + Create new block if required. */ +static basic_block +chkp_get_entry_block (void) +{ + if (!entry_block) + entry_block = split_block (ENTRY_BLOCK_PTR_FOR_FN (cfun), NULL)->dest; + + return entry_block; +} + +/* Return a bounds var to be used for pointer var PTR_VAR. */ +static tree +chkp_get_bounds_var (tree ptr_var) +{ + tree bnd_var; + tree *slot; + + slot = chkp_bound_vars->get (ptr_var); + if (slot) + bnd_var = *slot; + else + { + bnd_var = create_tmp_reg (pointer_bounds_type_node, + CHKP_BOUND_TMP_NAME); + chkp_bound_vars->put (ptr_var, bnd_var); + } + + return bnd_var; +} + + + +/* Register bounds BND for object PTR in global bounds table. + A copy of bounds may be created for abnormal ssa names. + Returns bounds to use for PTR. */ +static tree +chkp_maybe_copy_and_register_bounds (tree ptr, tree bnd) +{ + bool abnormal_ptr; + + if (!chkp_reg_bounds) + return bnd; + + /* Do nothing if bounds are incomplete_bounds + because it means bounds will be recomputed. */ + if (bnd == incomplete_bounds) + return bnd; + + abnormal_ptr = (TREE_CODE (ptr) == SSA_NAME + && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ptr) + && gimple_code (SSA_NAME_DEF_STMT (ptr)) != GIMPLE_PHI); + + /* A single bounds value may be reused multiple times for + different pointer values. It may cause coalescing issues + for abnormal SSA names. To avoid it we create a bounds + copy in case it is computed for abnormal SSA name. + + We also cannot reuse such created copies for other pointers */ + if (abnormal_ptr + || bitmap_bit_p (chkp_abnormal_copies, SSA_NAME_VERSION (bnd))) + { + tree bnd_var = NULL_TREE; + + if (abnormal_ptr) + { + if (SSA_NAME_VAR (ptr)) + bnd_var = chkp_get_bounds_var (SSA_NAME_VAR (ptr)); + } + else + bnd_var = chkp_get_tmp_var (); + + /* For abnormal copies we may just find original + bounds and use them. */ + if (!abnormal_ptr && !SSA_NAME_IS_DEFAULT_DEF (bnd)) + { + gimple bnd_def = SSA_NAME_DEF_STMT (bnd); + gcc_checking_assert (gimple_code (bnd_def) == GIMPLE_ASSIGN); + bnd = gimple_assign_rhs1 (bnd_def); + } + /* For undefined values we usually use none bounds + value but in case of abnormal edge it may cause + coalescing failures. Use default definition of + bounds variable instead to avoid it. */ + else if (SSA_NAME_IS_DEFAULT_DEF (ptr) + && TREE_CODE (SSA_NAME_VAR (ptr)) != PARM_DECL) + { + bnd = get_or_create_ssa_default_def (cfun, bnd_var); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Using default def bounds "); + print_generic_expr (dump_file, bnd, 0); + fprintf (dump_file, " for abnormal default def SSA name "); + print_generic_expr (dump_file, ptr, 0); + fprintf (dump_file, "\n"); + } + } + else + { + tree copy; + gimple def = SSA_NAME_DEF_STMT (ptr); + gimple assign; + gimple_stmt_iterator gsi; + + if (bnd_var) + copy = make_ssa_name (bnd_var, gimple_build_nop ()); + else + copy = make_temp_ssa_name (pointer_bounds_type_node, + gimple_build_nop (), + CHKP_BOUND_TMP_NAME); + assign = gimple_build_assign (copy, bnd); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Creating a copy of bounds "); + print_generic_expr (dump_file, bnd, 0); + fprintf (dump_file, " for abnormal SSA name "); + print_generic_expr (dump_file, ptr, 0); + fprintf (dump_file, "\n"); + } + + if (gimple_code (def) == GIMPLE_NOP) + { + gsi = gsi_last_bb (chkp_get_entry_block ()); + if (!gsi_end_p (gsi) && is_ctrl_stmt (gsi_stmt (gsi))) + gsi_insert_before (&gsi, assign, GSI_CONTINUE_LINKING); + else + gsi_insert_after (&gsi, assign, GSI_CONTINUE_LINKING); + } + else + { + gimple bnd_def = SSA_NAME_DEF_STMT (bnd); + /* Sometimes (e.g. when we load a pointer from a + memory) bounds are produced later than a pointer. + We need to insert bounds copy appropriately. */ + if (gimple_code (bnd_def) != GIMPLE_NOP + && stmt_dominates_stmt_p (def, bnd_def)) + gsi = gsi_for_stmt (bnd_def); + else + gsi = gsi_for_stmt (def); + gsi_insert_after (&gsi, assign, GSI_CONTINUE_LINKING); + } + + bnd = copy; + } + + if (abnormal_ptr) + bitmap_set_bit (chkp_abnormal_copies, SSA_NAME_VERSION (bnd)); + } + + chkp_reg_bounds->put (ptr, bnd); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Regsitered bound "); + print_generic_expr (dump_file, bnd, 0); + fprintf (dump_file, " for pointer "); + print_generic_expr (dump_file, ptr, 0); + fprintf (dump_file, "\n"); + } + + return bnd; +} + +/* Get bounds registered for object PTR in global bounds table. */ +static tree +chkp_get_registered_bounds (tree ptr) +{ + tree *slot; + + if (!chkp_reg_bounds) + return NULL_TREE; + + slot = chkp_reg_bounds->get (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) +{ + greturn *ret = as_a (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 +chkp_force_gimple_call_op (tree op, gimple_seq *seq) +{ + gimple_seq stmts; + gimple_stmt_iterator si; + + op = force_gimple_operand (unshare_expr (op), &stmts, true, NULL_TREE); + + for (si = gsi_start (stmts); !gsi_end_p (si); gsi_next (&si)) + chkp_mark_stmt (gsi_stmt (si)); + + gimple_seq_add_seq (seq, stmts); + + return op; +} + +/* Generate lower bound check for memory access by ADDR. + Check is inserted before the position pointed by ITER. + DIRFLAG indicates whether memory access is load or store. */ +static void +chkp_check_lower (tree addr, tree bounds, + gimple_stmt_iterator iter, + location_t location, + tree dirflag) +{ + gimple_seq seq; + gimple check; + tree node; + + if (!chkp_function_instrumented_p (current_function_decl) + && bounds == chkp_get_zero_bounds ()) + return; + + if (dirflag == integer_zero_node + && !flag_chkp_check_read) + return; + + if (dirflag == integer_one_node + && !flag_chkp_check_write) + return; + + seq = NULL; + + node = chkp_force_gimple_call_op (addr, &seq); + + check = gimple_build_call (chkp_checkl_fndecl, 2, node, bounds); + chkp_mark_stmt (check); + gimple_call_set_with_bounds (check, true); + gimple_set_location (check, location); + gimple_seq_add_stmt (&seq, check); + + gsi_insert_seq_before (&iter, seq, GSI_SAME_STMT); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + gimple before = gsi_stmt (iter); + fprintf (dump_file, "Generated lower bound check for statement "); + print_gimple_stmt (dump_file, before, 0, TDF_VOPS|TDF_MEMSYMS); + fprintf (dump_file, " "); + print_gimple_stmt (dump_file, check, 0, TDF_VOPS|TDF_MEMSYMS); + } +} + +/* Generate upper bound check for memory access by ADDR. + Check is inserted before the position pointed by ITER. + DIRFLAG indicates whether memory access is load or store. */ +static void +chkp_check_upper (tree addr, tree bounds, + gimple_stmt_iterator iter, + location_t location, + tree dirflag) +{ + gimple_seq seq; + gimple check; + tree node; + + if (!chkp_function_instrumented_p (current_function_decl) + && bounds == chkp_get_zero_bounds ()) + return; + + if (dirflag == integer_zero_node + && !flag_chkp_check_read) + return; + + if (dirflag == integer_one_node + && !flag_chkp_check_write) + return; + + seq = NULL; + + node = chkp_force_gimple_call_op (addr, &seq); + + check = gimple_build_call (chkp_checku_fndecl, 2, node, bounds); + chkp_mark_stmt (check); + gimple_call_set_with_bounds (check, true); + gimple_set_location (check, location); + gimple_seq_add_stmt (&seq, check); + + gsi_insert_seq_before (&iter, seq, GSI_SAME_STMT); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + gimple before = gsi_stmt (iter); + fprintf (dump_file, "Generated upper bound check for statement "); + print_gimple_stmt (dump_file, before, 0, TDF_VOPS|TDF_MEMSYMS); + fprintf (dump_file, " "); + print_gimple_stmt (dump_file, check, 0, TDF_VOPS|TDF_MEMSYMS); + } +} + +/* Generate lower and upper bound checks for memory access + to memory slot [FIRST, LAST] againsr BOUNDS. Checks + are inserted before the position pointed by ITER. + DIRFLAG indicates whether memory access is load or store. */ +void +chkp_check_mem_access (tree first, tree last, tree bounds, + gimple_stmt_iterator iter, + location_t location, + tree dirflag) +{ + chkp_check_lower (first, bounds, iter, location, dirflag); + 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) +{ + tree res; + + /* If object is TMR then we do not use component_ref but + add offset instead. We need it to be able to get addr + of the reasult later. */ + if (TREE_CODE (obj) == TARGET_MEM_REF) + { + tree offs = TMR_OFFSET (obj); + offs = fold_binary_to_constant (PLUS_EXPR, TREE_TYPE (offs), + offs, DECL_FIELD_OFFSET (field)); + + gcc_assert (offs); + + res = copy_node (obj); + TREE_TYPE (res) = TREE_TYPE (field); + TMR_OFFSET (res) = offs; + } + else + res = build3 (COMPONENT_REF, TREE_TYPE (field), obj, field, NULL_TREE); + + return res; +} + +/* Return ARRAY_REF for array ARR and index IDX with + specified element type ETYPE and element size ESIZE. */ +static tree +chkp_build_array_ref (tree arr, tree etype, tree esize, + unsigned HOST_WIDE_INT idx) +{ + tree index = build_int_cst (size_type_node, idx); + tree res; + + /* If object is TMR then we do not use array_ref but + add offset instead. We need it to be able to get addr + of the reasult later. */ + if (TREE_CODE (arr) == TARGET_MEM_REF) + { + tree offs = TMR_OFFSET (arr); + + esize = fold_binary_to_constant (MULT_EXPR, TREE_TYPE (esize), + esize, index); + gcc_assert(esize); + + offs = fold_binary_to_constant (PLUS_EXPR, TREE_TYPE (offs), + offs, esize); + gcc_assert (offs); + + res = copy_node (arr); + TREE_TYPE (res) = etype; + TMR_OFFSET (res) = offs; + } + else + res = build4 (ARRAY_REF, etype, arr, index, NULL_TREE, NULL_TREE); + + return res; +} + +/* 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 = 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 = 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. + + OFFS is used for recursive calls and holds basic + offset of TYPE in outer structure in bits. + + HAVE_BOUND[i] is set to 1 if there is a field + in TYPE which has pointer type and offset + equal to i * POINTER_SIZE - OFFS in bits. */ +void +chkp_find_bound_slots_1 (const_tree type, bitmap have_bound, + HOST_WIDE_INT offs) +{ + if (BOUNDED_TYPE_P (type)) + bitmap_set_bit (have_bound, offs / POINTER_SIZE); + 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) + { + 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_bound_slots_1 (TREE_TYPE (field), have_bound, + offs + field_offs); + } + } + 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 + || TREE_CODE (maxval) != INTEGER_CST + || integer_minus_onep (maxval)) + return; + + for (cur = 0; cur <= TREE_INT_CST_LOW (maxval); cur++) + chkp_find_bound_slots_1 (etype, have_bound, offs + cur * esize); + } +} + +/* Fill bitmap RES with information about bounds for + type TYPE. See chkp_find_bound_slots_1 for more + details. */ +void +chkp_find_bound_slots (const_tree type, bitmap res) +{ + bitmap_clear (res); + chkp_find_bound_slots_1 (type, res, 0); +} + +/* Return 1 if call to FNDECL should be instrumented + and 0 otherwise. */ + +static bool +chkp_instrument_normal_builtin (tree fndecl) +{ + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_STRLEN: + case BUILT_IN_STRCPY: + case BUILT_IN_STRNCPY: + case BUILT_IN_STPCPY: + case BUILT_IN_STPNCPY: + case BUILT_IN_STRCAT: + case BUILT_IN_STRNCAT: + case BUILT_IN_MEMCPY: + case BUILT_IN_MEMPCPY: + case BUILT_IN_MEMSET: + case BUILT_IN_MEMMOVE: + case BUILT_IN_BZERO: + case BUILT_IN_STRCMP: + case BUILT_IN_STRNCMP: + case BUILT_IN_BCMP: + case BUILT_IN_MEMCMP: + case BUILT_IN_MEMCPY_CHK: + case BUILT_IN_MEMPCPY_CHK: + case BUILT_IN_MEMMOVE_CHK: + case BUILT_IN_MEMSET_CHK: + case BUILT_IN_STRCPY_CHK: + case BUILT_IN_STRNCPY_CHK: + case BUILT_IN_STPCPY_CHK: + case BUILT_IN_STPNCPY_CHK: + case BUILT_IN_STRCAT_CHK: + case BUILT_IN_STRNCAT_CHK: + case BUILT_IN_MALLOC: + case BUILT_IN_CALLOC: + case BUILT_IN_REALLOC: + return 1; + + default: + return 0; + } +} + +/* 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) +{ + gcall *call = as_a (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; + gcall *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; + + /* Do nothing for calls to not instrumentable functions. */ + if (fndecl && !chkp_instrumentable_p (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; + + /* We instrument only some subset of builtins. We also instrument + builtin calls to be inlined. */ + if (fndecl + && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && !chkp_instrument_normal_builtin (fndecl)) + { + if (!lookup_attribute ("always_inline", DECL_ATTRIBUTES (fndecl))) + return; + + struct cgraph_node *clone = chkp_maybe_create_clone (fndecl); + if (!clone + || !gimple_has_body_p (clone->decl)) + 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 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); + gimple_call_set_chain (new_call, gimple_call_chain (call)); + } + new_args.release (); + + /* 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 bounds LB and UB. + If such var does not exists then new var is created with specified NAME. */ +static tree +chkp_make_static_const_bounds (HOST_WIDE_INT lb, + HOST_WIDE_INT ub, + const char *name) +{ + tree id = get_identifier (name); + tree var; + varpool_node *node; + symtab_node *snode; + + var = build_decl (UNKNOWN_LOCATION, VAR_DECL, id, + pointer_bounds_type_node); + TREE_STATIC (var) = 1; + TREE_PUBLIC (var) = 1; + + /* With LTO we may have constant bounds already in varpool. + Try to find it. */ + if ((snode = symtab_node::get_for_asmname (DECL_ASSEMBLER_NAME (var)))) + { + /* We don't allow this symbol usage for non bounds. */ + if (snode->type != SYMTAB_VARIABLE + || !POINTER_BOUNDS_P (snode->decl)) + sorry ("-fcheck-pointer-bounds requires '%s' " + "name for internal usage", + IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (var))); + + return snode->decl; + } + + TREE_USED (var) = 1; + TREE_READONLY (var) = 1; + TREE_ADDRESSABLE (var) = 0; + DECL_ARTIFICIAL (var) = 1; + DECL_READ_P (var) = 1; + DECL_INITIAL (var) = targetm.chkp_make_bounds_constant (lb, ub); + make_decl_one_only (var, DECL_ASSEMBLER_NAME (var)); + /* We may use this symbol during ctors generation in chkp_finish_file + when all symbols are emitted. Force output to avoid undefined + symbols in ctors. */ + node = varpool_node::get_create (var); + node->force_output = 1; + + varpool_node::finalize_decl (var); + + return var; +} + +/* Generate code to make bounds with specified lower bound LB and SIZE. + if AFTER is 1 then code is inserted after position pointed by ITER + otherwise code is inserted before position pointed by ITER. + If ITER is NULL then code is added to entry block. */ +static tree +chkp_make_bounds (tree lb, tree size, gimple_stmt_iterator *iter, bool after) +{ + gimple_seq seq; + gimple_stmt_iterator gsi; + gimple stmt; + tree bounds; + + if (iter) + gsi = *iter; + else + gsi = gsi_start_bb (chkp_get_entry_block ()); + + seq = NULL; + + lb = chkp_force_gimple_call_op (lb, &seq); + size = chkp_force_gimple_call_op (size, &seq); + + stmt = gimple_build_call (chkp_bndmk_fndecl, 2, lb, size); + chkp_mark_stmt (stmt); + + bounds = chkp_get_tmp_reg (stmt); + gimple_call_set_lhs (stmt, bounds); + + gimple_seq_add_stmt (&seq, stmt); + + if (iter && after) + gsi_insert_seq_after (&gsi, seq, GSI_SAME_STMT); + else + gsi_insert_seq_before (&gsi, seq, GSI_SAME_STMT); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Made bounds: "); + print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS); + if (iter) + { + fprintf (dump_file, " inserted before statement: "); + print_gimple_stmt (dump_file, gsi_stmt (*iter), 0, TDF_VOPS|TDF_MEMSYMS); + } + else + fprintf (dump_file, " at function entry\n"); + } + + /* update_stmt (stmt); */ + + return bounds; +} + +/* Return var holding zero bounds. */ +tree +chkp_get_zero_bounds_var (void) +{ + if (!chkp_zero_bounds_var) + chkp_zero_bounds_var + = chkp_make_static_const_bounds (0, -1, + CHKP_ZERO_BOUNDS_VAR_NAME); + return chkp_zero_bounds_var; +} + +/* Return var holding none bounds. */ +tree +chkp_get_none_bounds_var (void) +{ + if (!chkp_none_bounds_var) + chkp_none_bounds_var + = chkp_make_static_const_bounds (-1, 0, + CHKP_NONE_BOUNDS_VAR_NAME); + return chkp_none_bounds_var; +} + +/* Return SSA_NAME used to represent zero bounds. */ +static tree +chkp_get_zero_bounds (void) +{ + if (zero_bounds) + return zero_bounds; + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Creating zero bounds..."); + + if ((flag_chkp_use_static_bounds && flag_chkp_use_static_const_bounds) + || flag_chkp_use_static_const_bounds > 0) + { + gimple_stmt_iterator gsi = gsi_start_bb (chkp_get_entry_block ()); + gimple stmt; + + zero_bounds = chkp_get_tmp_reg (gimple_build_nop ()); + stmt = gimple_build_assign (zero_bounds, chkp_get_zero_bounds_var ()); + gsi_insert_before (&gsi, stmt, GSI_SAME_STMT); + } + else + zero_bounds = chkp_make_bounds (integer_zero_node, + integer_zero_node, + NULL, + false); + + return zero_bounds; +} + +/* Return SSA_NAME used to represent none bounds. */ +static tree +chkp_get_none_bounds (void) +{ + if (none_bounds) + return none_bounds; + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Creating none bounds..."); + + + if ((flag_chkp_use_static_bounds && flag_chkp_use_static_const_bounds) + || flag_chkp_use_static_const_bounds > 0) + { + gimple_stmt_iterator gsi = gsi_start_bb (chkp_get_entry_block ()); + gimple stmt; + + none_bounds = chkp_get_tmp_reg (gimple_build_nop ()); + stmt = gimple_build_assign (none_bounds, chkp_get_none_bounds_var ()); + gsi_insert_before (&gsi, stmt, GSI_SAME_STMT); + } + else + none_bounds = chkp_make_bounds (integer_minus_one_node, + build_int_cst (size_type_node, 2), + NULL, + false); + + return none_bounds; +} + +/* Return bounds to be used as a result of operation which + should not create poiunter (e.g. MULT_EXPR). */ +static tree +chkp_get_invalid_op_bounds (void) +{ + return chkp_get_zero_bounds (); +} + +/* Return bounds to be used for loads of non-pointer values. */ +static tree +chkp_get_nonpointer_load_bounds (void) +{ + return chkp_get_zero_bounds (); +} + +/* Return 1 if may use bndret call to get bounds for pointer + returned by CALL. */ +static bool +chkp_call_returns_bounds_p (gcall *call) +{ + if (gimple_call_internal_p (call)) + return false; + + if (gimple_call_builtin_p (call, BUILT_IN_CHKP_NARROW_PTR_BOUNDS) + || chkp_gimple_call_builtin_p (call, BUILT_IN_CHKP_NARROW)) + return true; + + if (gimple_call_with_bounds_p (call)) + return true; + + tree fndecl = gimple_call_fndecl (call); + + if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) + return false; + + if (fndecl && !chkp_instrumentable_p (fndecl)) + return false; + + if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) + { + if (chkp_instrument_normal_builtin (fndecl)) + return true; + + if (!lookup_attribute ("always_inline", DECL_ATTRIBUTES (fndecl))) + return false; + + struct cgraph_node *clone = chkp_maybe_create_clone (fndecl); + return (clone && gimple_has_body_p (clone->decl)); + } + + return true; +} + +/* Build bounds returned by CALL. */ +static tree +chkp_build_returned_bound (gcall *call) +{ + gimple_stmt_iterator gsi; + tree bounds; + gimple stmt; + tree fndecl = gimple_call_fndecl (call); + unsigned int retflags; + + /* To avoid fixing alloca expands in targets we handle + it separately. */ + if (fndecl + && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA + || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA_WITH_ALIGN)) + { + tree size = gimple_call_arg (call, 0); + tree lb = gimple_call_lhs (call); + gimple_stmt_iterator iter = gsi_for_stmt (call); + bounds = chkp_make_bounds (lb, size, &iter, true); + } + /* We know bounds returned by set_bounds builtin call. */ + else if (fndecl + && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_SET_PTR_BOUNDS) + { + tree lb = gimple_call_arg (call, 0); + tree size = gimple_call_arg (call, 1); + gimple_stmt_iterator iter = gsi_for_stmt (call); + bounds = chkp_make_bounds (lb, size, &iter, true); + } + /* Detect bounds initialization calls. */ + else if (fndecl + && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_INIT_PTR_BOUNDS) + bounds = chkp_get_zero_bounds (); + /* Detect bounds nullification calls. */ + else if (fndecl + && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_NULL_PTR_BOUNDS) + bounds = chkp_get_none_bounds (); + /* Detect bounds copy calls. */ + else if (fndecl + && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_COPY_PTR_BOUNDS) + { + gimple_stmt_iterator iter = gsi_for_stmt (call); + bounds = chkp_find_bounds (gimple_call_arg (call, 1), &iter); + } + /* Do not use retbnd when returned bounds are equal to some + of passed bounds. */ + else if (((retflags = gimple_call_return_flags (call)) & ERF_RETURNS_ARG) + && (retflags & ERF_RETURN_ARG_MASK) < gimple_call_num_args (call)) + { + gimple_stmt_iterator iter = gsi_for_stmt (call); + unsigned int retarg = retflags & ERF_RETURN_ARG_MASK, argno; + if (gimple_call_with_bounds_p (call)) + { + for (argno = 0; argno < gimple_call_num_args (call); argno++) + if (!POINTER_BOUNDS_P (gimple_call_arg (call, argno))) + { + if (retarg) + retarg--; + else + break; + } + } + else + argno = retarg; + + bounds = chkp_find_bounds (gimple_call_arg (call, argno), &iter); + } + else if (chkp_call_returns_bounds_p (call)) + { + gcc_assert (TREE_CODE (gimple_call_lhs (call)) == SSA_NAME); + + /* In general case build checker builtin call to + obtain returned bounds. */ + stmt = gimple_build_call (chkp_ret_bnd_fndecl, 1, + gimple_call_lhs (call)); + chkp_mark_stmt (stmt); + + gsi = gsi_for_stmt (call); + gsi_insert_after (&gsi, stmt, GSI_SAME_STMT); + + bounds = chkp_get_tmp_reg (stmt); + gimple_call_set_lhs (stmt, bounds); + + update_stmt (stmt); + } + else + bounds = chkp_get_zero_bounds (); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Built returned bounds ("); + print_generic_expr (dump_file, bounds, 0); + fprintf (dump_file, ") for call: "); + print_gimple_stmt (dump_file, call, 0, TDF_VOPS|TDF_MEMSYMS); + } + + bounds = chkp_maybe_copy_and_register_bounds (gimple_call_lhs (call), bounds); + + return bounds; +} + +/* Return bounds used as returned by call + which produced SSA name VAL. */ +gcall * +chkp_retbnd_call_by_val (tree val) +{ + if (TREE_CODE (val) != SSA_NAME) + return NULL; + + gcc_assert (gimple_code (SSA_NAME_DEF_STMT (val)) == GIMPLE_CALL); + + imm_use_iterator use_iter; + use_operand_p use_p; + FOR_EACH_IMM_USE_FAST (use_p, use_iter, val) + if (gimple_code (USE_STMT (use_p)) == GIMPLE_CALL + && gimple_call_fndecl (USE_STMT (use_p)) == chkp_ret_bnd_fndecl) + return as_a (USE_STMT (use_p)); + + return NULL; +} + +/* Check the next parameter for the given PARM is bounds + and return it's default SSA_NAME (create if required). */ +static tree +chkp_get_next_bounds_parm (tree parm) +{ + tree bounds = TREE_CHAIN (parm); + gcc_assert (POINTER_BOUNDS_P (bounds)); + bounds = ssa_default_def (cfun, bounds); + if (!bounds) + { + bounds = make_ssa_name (TREE_CHAIN (parm), gimple_build_nop ()); + set_ssa_default_def (cfun, TREE_CHAIN (parm), bounds); + } + return bounds; +} + +/* Return bounds to be used for input argument PARM. */ +static tree +chkp_get_bound_for_parm (tree parm) +{ + tree decl = SSA_NAME_VAR (parm); + tree bounds; + + gcc_assert (TREE_CODE (decl) == PARM_DECL); + + bounds = chkp_get_registered_bounds (parm); + + if (!bounds) + bounds = chkp_get_registered_bounds (decl); + + if (!bounds) + { + tree orig_decl = cgraph_node::get (cfun->decl)->orig_decl; + + /* For static chain param we return zero bounds + because currently we do not check dereferences + of this pointer. */ + if (cfun->static_chain_decl == decl) + bounds = chkp_get_zero_bounds (); + /* If non instrumented runtime is used then it may be useful + to use zero bounds for input arguments of main + function. */ + else if (flag_chkp_zero_input_bounds_for_main + && strcmp (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (orig_decl)), + "main") == 0) + bounds = chkp_get_zero_bounds (); + else if (BOUNDED_P (parm)) + { + bounds = chkp_get_next_bounds_parm (decl); + bounds = chkp_maybe_copy_and_register_bounds (decl, bounds); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Built arg bounds ("); + print_generic_expr (dump_file, bounds, 0); + fprintf (dump_file, ") for arg: "); + print_node (dump_file, "", decl, 0); + } + } + else + bounds = chkp_get_zero_bounds (); + } + + if (!chkp_get_registered_bounds (parm)) + bounds = chkp_maybe_copy_and_register_bounds (parm, bounds); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Using bounds "); + print_generic_expr (dump_file, bounds, 0); + fprintf (dump_file, " for parm "); + print_generic_expr (dump_file, parm, 0); + fprintf (dump_file, " of type "); + print_generic_expr (dump_file, TREE_TYPE (parm), 0); + fprintf (dump_file, ".\n"); + } + + return bounds; +} + +/* Build and return CALL_EXPR for bndstx builtin with specified + arguments. */ +tree +chkp_build_bndldx_call (tree addr, tree ptr) +{ + tree fn = build1 (ADDR_EXPR, + build_pointer_type (TREE_TYPE (chkp_bndldx_fndecl)), + chkp_bndldx_fndecl); + tree call = build_call_nary (TREE_TYPE (TREE_TYPE (chkp_bndldx_fndecl)), + fn, 2, addr, ptr); + CALL_WITH_BOUNDS_P (call) = true; + return call; +} + +/* Insert code to load bounds for PTR located by ADDR. + Code is inserted after position pointed by GSI. + Loaded bounds are returned. */ +static tree +chkp_build_bndldx (tree addr, tree ptr, gimple_stmt_iterator *gsi) +{ + gimple_seq seq; + gimple stmt; + tree bounds; + + seq = NULL; + + addr = chkp_force_gimple_call_op (addr, &seq); + ptr = chkp_force_gimple_call_op (ptr, &seq); + + stmt = gimple_build_call (chkp_bndldx_fndecl, 2, addr, ptr); + chkp_mark_stmt (stmt); + bounds = chkp_get_tmp_reg (stmt); + gimple_call_set_lhs (stmt, bounds); + + gimple_seq_add_stmt (&seq, stmt); + + gsi_insert_seq_after (gsi, seq, GSI_CONTINUE_LINKING); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Generated bndldx for pointer "); + print_generic_expr (dump_file, ptr, 0); + fprintf (dump_file, ": "); + print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS); + } + + return bounds; +} + +/* Build and return CALL_EXPR for bndstx builtin with specified + arguments. */ +tree +chkp_build_bndstx_call (tree addr, tree ptr, tree bounds) +{ + tree fn = build1 (ADDR_EXPR, + build_pointer_type (TREE_TYPE (chkp_bndstx_fndecl)), + chkp_bndstx_fndecl); + tree call = build_call_nary (TREE_TYPE (TREE_TYPE (chkp_bndstx_fndecl)), + fn, 3, ptr, bounds, addr); + CALL_WITH_BOUNDS_P (call) = true; + return call; +} + +/* Insert code to store BOUNDS for PTR stored by ADDR. + New statements are inserted after position pointed + by GSI. */ +void +chkp_build_bndstx (tree addr, tree ptr, tree bounds, + gimple_stmt_iterator *gsi) +{ + gimple_seq seq; + gimple stmt; + + seq = NULL; + + addr = chkp_force_gimple_call_op (addr, &seq); + ptr = chkp_force_gimple_call_op (ptr, &seq); + + stmt = gimple_build_call (chkp_bndstx_fndecl, 3, ptr, bounds, addr); + chkp_mark_stmt (stmt); + gimple_call_set_with_bounds (stmt, true); + + gimple_seq_add_stmt (&seq, stmt); + + gsi_insert_seq_after (gsi, seq, GSI_CONTINUE_LINKING); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Generated bndstx for pointer store "); + print_gimple_stmt (dump_file, gsi_stmt (*gsi), 0, TDF_VOPS|TDF_MEMSYMS); + print_gimple_stmt (dump_file, stmt, 2, TDF_VOPS|TDF_MEMSYMS); + } +} + +/* Compute bounds for pointer NODE which was assigned in + assignment statement ASSIGN. Return computed bounds. */ +static tree +chkp_compute_bounds_for_assignment (tree node, gimple assign) +{ + enum tree_code rhs_code = gimple_assign_rhs_code (assign); + tree rhs1 = gimple_assign_rhs1 (assign); + tree bounds = NULL_TREE; + gimple_stmt_iterator iter = gsi_for_stmt (assign); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Computing bounds for assignment: "); + print_gimple_stmt (dump_file, assign, 0, TDF_VOPS|TDF_MEMSYMS); + } + + switch (rhs_code) + { + case MEM_REF: + case TARGET_MEM_REF: + case COMPONENT_REF: + case ARRAY_REF: + /* We need to load bounds from the bounds table. */ + bounds = chkp_find_bounds_loaded (node, rhs1, &iter); + break; + + case VAR_DECL: + case SSA_NAME: + case ADDR_EXPR: + case POINTER_PLUS_EXPR: + case NOP_EXPR: + case CONVERT_EXPR: + case INTEGER_CST: + /* Bounds are just propagated from RHS. */ + bounds = chkp_find_bounds (rhs1, &iter); + break; + + case VIEW_CONVERT_EXPR: + /* Bounds are just propagated from RHS. */ + bounds = chkp_find_bounds (TREE_OPERAND (rhs1, 0), &iter); + break; + + case PARM_DECL: + if (BOUNDED_P (rhs1)) + { + /* We need to load bounds from the bounds table. */ + bounds = chkp_build_bndldx (chkp_build_addr_expr (rhs1), + node, &iter); + TREE_ADDRESSABLE (rhs1) = 1; + } + else + bounds = chkp_get_nonpointer_load_bounds (); + break; + + case MINUS_EXPR: + case PLUS_EXPR: + case BIT_AND_EXPR: + case BIT_IOR_EXPR: + case BIT_XOR_EXPR: + { + tree rhs2 = gimple_assign_rhs2 (assign); + tree bnd1 = chkp_find_bounds (rhs1, &iter); + tree bnd2 = chkp_find_bounds (rhs2, &iter); + + /* First we try to check types of operands. If it + does not help then look at bound values. + + If some bounds are incomplete and other are + not proven to be valid (i.e. also incomplete + or invalid because value is not pointer) then + resulting value is incomplete and will be + recomputed later in chkp_finish_incomplete_bounds. */ + if (BOUNDED_P (rhs1) + && !BOUNDED_P (rhs2)) + bounds = bnd1; + else if (BOUNDED_P (rhs2) + && !BOUNDED_P (rhs1) + && rhs_code != MINUS_EXPR) + bounds = bnd2; + else if (chkp_incomplete_bounds (bnd1)) + if (chkp_valid_bounds (bnd2) && rhs_code != MINUS_EXPR + && !chkp_incomplete_bounds (bnd2)) + bounds = bnd2; + else + bounds = incomplete_bounds; + else if (chkp_incomplete_bounds (bnd2)) + if (chkp_valid_bounds (bnd1) + && !chkp_incomplete_bounds (bnd1)) + bounds = bnd1; + else + bounds = incomplete_bounds; + else if (!chkp_valid_bounds (bnd1)) + if (chkp_valid_bounds (bnd2) && rhs_code != MINUS_EXPR) + bounds = bnd2; + else if (bnd2 == chkp_get_zero_bounds ()) + bounds = bnd2; + else + bounds = bnd1; + else if (!chkp_valid_bounds (bnd2)) + bounds = bnd1; + else + /* Seems both operands may have valid bounds + (e.g. pointer minus pointer). In such case + use default invalid op bounds. */ + bounds = chkp_get_invalid_op_bounds (); + } + break; + + case BIT_NOT_EXPR: + case NEGATE_EXPR: + case LSHIFT_EXPR: + case RSHIFT_EXPR: + case LROTATE_EXPR: + case RROTATE_EXPR: + case EQ_EXPR: + case NE_EXPR: + case LT_EXPR: + case LE_EXPR: + case GT_EXPR: + case GE_EXPR: + case MULT_EXPR: + case RDIV_EXPR: + case TRUNC_DIV_EXPR: + case FLOOR_DIV_EXPR: + case CEIL_DIV_EXPR: + case ROUND_DIV_EXPR: + case TRUNC_MOD_EXPR: + case FLOOR_MOD_EXPR: + case CEIL_MOD_EXPR: + case ROUND_MOD_EXPR: + case EXACT_DIV_EXPR: + case FIX_TRUNC_EXPR: + case FLOAT_EXPR: + case REALPART_EXPR: + case IMAGPART_EXPR: + /* No valid bounds may be produced by these exprs. */ + bounds = chkp_get_invalid_op_bounds (); + break; + + case COND_EXPR: + { + tree val1 = gimple_assign_rhs2 (assign); + tree val2 = gimple_assign_rhs3 (assign); + tree bnd1 = chkp_find_bounds (val1, &iter); + tree bnd2 = chkp_find_bounds (val2, &iter); + gimple stmt; + + if (chkp_incomplete_bounds (bnd1) || chkp_incomplete_bounds (bnd2)) + bounds = incomplete_bounds; + else if (bnd1 == bnd2) + bounds = bnd1; + else + { + rhs1 = unshare_expr (rhs1); + + bounds = chkp_get_tmp_reg (assign); + stmt = gimple_build_assign (bounds, COND_EXPR, rhs1, bnd1, bnd2); + gsi_insert_after (&iter, stmt, GSI_SAME_STMT); + + if (!chkp_valid_bounds (bnd1) && !chkp_valid_bounds (bnd2)) + chkp_mark_invalid_bounds (bounds); + } + } + break; + + case MAX_EXPR: + case MIN_EXPR: + { + tree rhs2 = gimple_assign_rhs2 (assign); + tree bnd1 = chkp_find_bounds (rhs1, &iter); + tree bnd2 = chkp_find_bounds (rhs2, &iter); + + if (chkp_incomplete_bounds (bnd1) || chkp_incomplete_bounds (bnd2)) + bounds = incomplete_bounds; + else if (bnd1 == bnd2) + bounds = bnd1; + else + { + gimple stmt; + tree cond = build2 (rhs_code == MAX_EXPR ? GT_EXPR : LT_EXPR, + boolean_type_node, rhs1, rhs2); + bounds = chkp_get_tmp_reg (assign); + stmt = gimple_build_assign (bounds, COND_EXPR, cond, bnd1, bnd2); + + gsi_insert_after (&iter, stmt, GSI_SAME_STMT); + + if (!chkp_valid_bounds (bnd1) && !chkp_valid_bounds (bnd2)) + chkp_mark_invalid_bounds (bounds); + } + } + break; + + default: + bounds = chkp_get_zero_bounds (); + warning (0, "pointer bounds were lost due to unexpected expression %s", + get_tree_code_name (rhs_code)); + } + + gcc_assert (bounds); + + if (node) + bounds = chkp_maybe_copy_and_register_bounds (node, bounds); + + return bounds; +} + +/* Compute bounds for ssa name NODE defined by DEF_STMT pointed by ITER. + + There are just few statement codes allowed: NOP (for default ssa names), + ASSIGN, CALL, PHI, ASM. + + Return computed bounds. */ +static tree +chkp_get_bounds_by_definition (tree node, gimple def_stmt, + gphi_iterator *iter) +{ + tree var, bounds; + enum gimple_code code = gimple_code (def_stmt); + gphi *stmt; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Searching for bounds for node: "); + print_generic_expr (dump_file, node, 0); + + fprintf (dump_file, " using its definition: "); + print_gimple_stmt (dump_file, def_stmt, 0, TDF_VOPS|TDF_MEMSYMS); + } + + switch (code) + { + case GIMPLE_NOP: + var = SSA_NAME_VAR (node); + switch (TREE_CODE (var)) + { + case PARM_DECL: + bounds = chkp_get_bound_for_parm (node); + break; + + case VAR_DECL: + /* For uninitialized pointers use none bounds. */ + bounds = chkp_get_none_bounds (); + bounds = chkp_maybe_copy_and_register_bounds (node, bounds); + break; + + case RESULT_DECL: + { + tree base_type; + + gcc_assert (TREE_CODE (TREE_TYPE (node)) == REFERENCE_TYPE); + + base_type = TREE_TYPE (TREE_TYPE (node)); + + gcc_assert (TYPE_SIZE (base_type) + && TREE_CODE (TYPE_SIZE (base_type)) == INTEGER_CST + && tree_to_uhwi (TYPE_SIZE (base_type)) != 0); + + bounds = chkp_make_bounds (node, TYPE_SIZE_UNIT (base_type), + NULL, false); + bounds = chkp_maybe_copy_and_register_bounds (node, bounds); + } + break; + + default: + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Unexpected var with no definition\n"); + print_generic_expr (dump_file, var, 0); + } + internal_error ("chkp_get_bounds_by_definition: Unexpected var of type %s", + get_tree_code_name (TREE_CODE (var))); + } + break; + + case GIMPLE_ASSIGN: + bounds = chkp_compute_bounds_for_assignment (node, def_stmt); + break; + + case GIMPLE_CALL: + bounds = chkp_build_returned_bound (as_a (def_stmt)); + break; + + case GIMPLE_PHI: + if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (node)) + if (SSA_NAME_VAR (node)) + var = chkp_get_bounds_var (SSA_NAME_VAR (node)); + else + var = make_temp_ssa_name (pointer_bounds_type_node, + gimple_build_nop (), + CHKP_BOUND_TMP_NAME); + else + var = chkp_get_tmp_var (); + stmt = create_phi_node (var, gimple_bb (def_stmt)); + bounds = gimple_phi_result (stmt); + *iter = gsi_for_phi (stmt); + + bounds = chkp_maybe_copy_and_register_bounds (node, bounds); + + /* Created bounds do not have all phi args computed and + therefore we do not know if there is a valid source + of bounds for that node. Therefore we mark bounds + as incomplete and then recompute them when all phi + args are computed. */ + chkp_register_incomplete_bounds (bounds, node); + break; + + case GIMPLE_ASM: + bounds = chkp_get_zero_bounds (); + bounds = chkp_maybe_copy_and_register_bounds (node, bounds); + break; + + default: + internal_error ("chkp_get_bounds_by_definition: Unexpected GIMPLE code %s", + gimple_code_name[code]); + } + + return bounds; +} + +/* Return CALL_EXPR for bndmk with specified LOWER_BOUND and SIZE. */ +tree +chkp_build_make_bounds_call (tree lower_bound, tree size) +{ + tree call = build1 (ADDR_EXPR, + build_pointer_type (TREE_TYPE (chkp_bndmk_fndecl)), + chkp_bndmk_fndecl); + return build_call_nary (TREE_TYPE (TREE_TYPE (chkp_bndmk_fndecl)), + call, 2, lower_bound, size); +} + +/* Create static bounds var of specfified OBJ which is + is either VAR_DECL or string constant. */ +static tree +chkp_make_static_bounds (tree obj) +{ + static int string_id = 1; + static int var_id = 1; + tree *slot; + const char *var_name; + char *bnd_var_name; + tree bnd_var; + + /* First check if we already have required var. */ + if (chkp_static_var_bounds) + { + /* For vars we use assembler name as a key in + chkp_static_var_bounds map. It allows to + avoid duplicating bound vars for decls + sharing assembler name. */ + if (TREE_CODE (obj) == VAR_DECL) + { + tree name = DECL_ASSEMBLER_NAME (obj); + slot = chkp_static_var_bounds->get (name); + if (slot) + return *slot; + } + else + { + slot = chkp_static_var_bounds->get (obj); + if (slot) + return *slot; + } + } + + /* Build decl for bounds var. */ + if (TREE_CODE (obj) == VAR_DECL) + { + if (DECL_IGNORED_P (obj)) + { + bnd_var_name = (char *) xmalloc (strlen (CHKP_VAR_BOUNDS_PREFIX) + 10); + sprintf (bnd_var_name, "%s%d", CHKP_VAR_BOUNDS_PREFIX, var_id++); + } + else + { + var_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (obj)); + + /* For hidden symbols we want to skip first '*' char. */ + if (*var_name == '*') + var_name++; + + bnd_var_name = (char *) xmalloc (strlen (var_name) + + strlen (CHKP_BOUNDS_OF_SYMBOL_PREFIX) + 1); + strcpy (bnd_var_name, CHKP_BOUNDS_OF_SYMBOL_PREFIX); + strcat (bnd_var_name, var_name); + } + + bnd_var = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier (bnd_var_name), + pointer_bounds_type_node); + + /* Address of the obj will be used as lower bound. */ + TREE_ADDRESSABLE (obj) = 1; + } + else + { + bnd_var_name = (char *) xmalloc (strlen (CHKP_STRING_BOUNDS_PREFIX) + 10); + sprintf (bnd_var_name, "%s%d", CHKP_STRING_BOUNDS_PREFIX, string_id++); + + bnd_var = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier (bnd_var_name), + pointer_bounds_type_node); + } + + TREE_PUBLIC (bnd_var) = 0; + TREE_USED (bnd_var) = 1; + TREE_READONLY (bnd_var) = 0; + TREE_STATIC (bnd_var) = 1; + TREE_ADDRESSABLE (bnd_var) = 0; + DECL_ARTIFICIAL (bnd_var) = 1; + DECL_COMMON (bnd_var) = 1; + DECL_COMDAT (bnd_var) = 1; + DECL_READ_P (bnd_var) = 1; + DECL_INITIAL (bnd_var) = chkp_build_addr_expr (obj); + /* Force output similar to constant bounds. + See chkp_make_static_const_bounds. */ + varpool_node::get_create (bnd_var)->force_output = 1; + /* Mark symbol as requiring bounds initialization. */ + varpool_node::get_create (bnd_var)->need_bounds_init = 1; + varpool_node::finalize_decl (bnd_var); + + /* Add created var to the map to use it for other references + to obj. */ + if (!chkp_static_var_bounds) + chkp_static_var_bounds = new hash_map; + + if (TREE_CODE (obj) == VAR_DECL) + { + tree name = DECL_ASSEMBLER_NAME (obj); + chkp_static_var_bounds->put (name, bnd_var); + } + else + chkp_static_var_bounds->put (obj, bnd_var); + + return bnd_var; +} + +/* When var has incomplete type we cannot get size to + compute its bounds. In such cases we use checker + builtin call which determines object size at runtime. */ +static tree +chkp_generate_extern_var_bounds (tree var) +{ + tree bounds, size_reloc, lb, size, max_size, cond; + gimple_stmt_iterator gsi; + gimple_seq seq = NULL; + gimple stmt; + + /* If instrumentation is not enabled for vars having + incomplete type then just return zero bounds to avoid + checks for this var. */ + if (!flag_chkp_incomplete_type) + return chkp_get_zero_bounds (); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Generating bounds for extern symbol '"); + print_generic_expr (dump_file, var, 0); + fprintf (dump_file, "'\n"); + } + + stmt = gimple_build_call (chkp_sizeof_fndecl, 1, var); + + size_reloc = create_tmp_reg (chkp_uintptr_type, CHKP_SIZE_TMP_NAME); + gimple_call_set_lhs (stmt, size_reloc); + + gimple_seq_add_stmt (&seq, stmt); + + lb = chkp_build_addr_expr (var); + size = make_ssa_name (chkp_get_size_tmp_var (), gimple_build_nop ()); + + if (flag_chkp_zero_dynamic_size_as_infinite) + { + /* We should check that size relocation was resolved. + If it was not then use maximum possible size for the var. */ + max_size = build2 (MINUS_EXPR, chkp_uintptr_type, integer_zero_node, + fold_convert (chkp_uintptr_type, lb)); + max_size = chkp_force_gimple_call_op (max_size, &seq); + + cond = build2 (NE_EXPR, boolean_type_node, + size_reloc, integer_zero_node); + stmt = gimple_build_assign (size, COND_EXPR, cond, size_reloc, max_size); + gimple_seq_add_stmt (&seq, stmt); + } + else + { + stmt = gimple_build_assign (size, size_reloc); + gimple_seq_add_stmt (&seq, stmt); + } + + gsi = gsi_start_bb (chkp_get_entry_block ()); + gsi_insert_seq_after (&gsi, seq, GSI_CONTINUE_LINKING); + + bounds = chkp_make_bounds (lb, size, &gsi, true); + + return bounds; +} + +/* Return 1 if TYPE has fields with zero size or fields + marked with chkp_variable_size attribute. */ +bool +chkp_variable_size_type (tree type) +{ + bool res = false; + tree field; + + if (RECORD_OR_UNION_TYPE_P (type)) + for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL) + res = res + || lookup_attribute ("bnd_variable_size", DECL_ATTRIBUTES (field)) + || chkp_variable_size_type (TREE_TYPE (field)); + } + else + res = !TYPE_SIZE (type) + || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST + || tree_to_uhwi (TYPE_SIZE (type)) == 0; + + return res; +} + +/* Compute and return bounds for address of DECL which is + one of VAR_DECL, PARM_DECL, RESULT_DECL. */ +static tree +chkp_get_bounds_for_decl_addr (tree decl) +{ + tree bounds; + + gcc_assert (TREE_CODE (decl) == VAR_DECL + || TREE_CODE (decl) == PARM_DECL + || TREE_CODE (decl) == RESULT_DECL); + + bounds = chkp_get_registered_addr_bounds (decl); + + if (bounds) + return bounds; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Building bounds for address of decl "); + print_generic_expr (dump_file, decl, 0); + fprintf (dump_file, "\n"); + } + + /* Use zero bounds if size is unknown and checks for + unknown sizes are restricted. */ + if ((!DECL_SIZE (decl) + || (chkp_variable_size_type (TREE_TYPE (decl)) + && (TREE_STATIC (decl) + || DECL_EXTERNAL (decl) + || TREE_PUBLIC (decl)))) + && !flag_chkp_incomplete_type) + return chkp_get_zero_bounds (); + + if (flag_chkp_use_static_bounds + && TREE_CODE (decl) == VAR_DECL + && (TREE_STATIC (decl) + || DECL_EXTERNAL (decl) + || TREE_PUBLIC (decl)) + && !DECL_THREAD_LOCAL_P (decl)) + { + tree bnd_var = chkp_make_static_bounds (decl); + gimple_stmt_iterator gsi = gsi_start_bb (chkp_get_entry_block ()); + gimple stmt; + + bounds = chkp_get_tmp_reg (gimple_build_nop ()); + stmt = gimple_build_assign (bounds, bnd_var); + gsi_insert_before (&gsi, stmt, GSI_SAME_STMT); + } + else if (!DECL_SIZE (decl) + || (chkp_variable_size_type (TREE_TYPE (decl)) + && (TREE_STATIC (decl) + || DECL_EXTERNAL (decl) + || TREE_PUBLIC (decl)))) + { + gcc_assert (TREE_CODE (decl) == VAR_DECL); + bounds = chkp_generate_extern_var_bounds (decl); + } + else + { + tree lb = chkp_build_addr_expr (decl); + bounds = chkp_make_bounds (lb, DECL_SIZE_UNIT (decl), NULL, false); + } + + return bounds; +} + +/* Compute and return bounds for constant string. */ +static tree +chkp_get_bounds_for_string_cst (tree cst) +{ + tree bounds; + tree lb; + tree size; + + gcc_assert (TREE_CODE (cst) == STRING_CST); + + bounds = chkp_get_registered_bounds (cst); + + if (bounds) + return bounds; + + if ((flag_chkp_use_static_bounds && flag_chkp_use_static_const_bounds) + || flag_chkp_use_static_const_bounds > 0) + { + tree bnd_var = chkp_make_static_bounds (cst); + gimple_stmt_iterator gsi = gsi_start_bb (chkp_get_entry_block ()); + gimple stmt; + + bounds = chkp_get_tmp_reg (gimple_build_nop ()); + stmt = gimple_build_assign (bounds, bnd_var); + gsi_insert_before (&gsi, stmt, GSI_SAME_STMT); + } + else + { + lb = chkp_build_addr_expr (cst); + size = build_int_cst (chkp_uintptr_type, TREE_STRING_LENGTH (cst)); + bounds = chkp_make_bounds (lb, size, NULL, false); + } + + bounds = chkp_maybe_copy_and_register_bounds (cst, bounds); + + return bounds; +} + +/* Generate code to instersect bounds BOUNDS1 and BOUNDS2 and + return the result. if ITER is not NULL then Code is inserted + before position pointed by ITER. Otherwise code is added to + entry block. */ +static tree +chkp_intersect_bounds (tree bounds1, tree bounds2, gimple_stmt_iterator *iter) +{ + if (!bounds1 || bounds1 == chkp_get_zero_bounds ()) + return bounds2 ? bounds2 : bounds1; + else if (!bounds2 || bounds2 == chkp_get_zero_bounds ()) + return bounds1; + else + { + gimple_seq seq; + gimple stmt; + tree bounds; + + seq = NULL; + + stmt = gimple_build_call (chkp_intersect_fndecl, 2, bounds1, bounds2); + chkp_mark_stmt (stmt); + + bounds = chkp_get_tmp_reg (stmt); + gimple_call_set_lhs (stmt, bounds); + + gimple_seq_add_stmt (&seq, stmt); + + /* We are probably doing narrowing for constant expression. + In such case iter may be undefined. */ + if (!iter) + { + gimple_stmt_iterator gsi = gsi_last_bb (chkp_get_entry_block ()); + iter = &gsi; + gsi_insert_seq_after (iter, seq, GSI_SAME_STMT); + } + else + gsi_insert_seq_before (iter, seq, GSI_SAME_STMT); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Bounds intersection: "); + print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS); + fprintf (dump_file, " inserted before statement: "); + print_gimple_stmt (dump_file, gsi_stmt (*iter), 0, + TDF_VOPS|TDF_MEMSYMS); + } + + return bounds; + } +} + +/* Return 1 if we are allowed to narrow bounds for addressed FIELD + and 0 othersize. */ +static bool +chkp_may_narrow_to_field (tree field) +{ + return DECL_SIZE (field) && TREE_CODE (DECL_SIZE (field)) == INTEGER_CST + && tree_to_uhwi (DECL_SIZE (field)) != 0 + && (!DECL_FIELD_OFFSET (field) + || TREE_CODE (DECL_FIELD_OFFSET (field)) == INTEGER_CST) + && (!DECL_FIELD_BIT_OFFSET (field) + || TREE_CODE (DECL_FIELD_BIT_OFFSET (field)) == INTEGER_CST) + && !lookup_attribute ("bnd_variable_size", DECL_ATTRIBUTES (field)) + && !chkp_variable_size_type (TREE_TYPE (field)); +} + +/* Return 1 if bounds for FIELD should be narrowed to + field's own size. */ +static bool +chkp_narrow_bounds_for_field (tree field) +{ + HOST_WIDE_INT offs; + HOST_WIDE_INT bit_offs; + + if (!chkp_may_narrow_to_field (field)) + return false; + + /* Accesse to compiler generated fields should not cause + bounds narrowing. */ + if (DECL_ARTIFICIAL (field)) + return false; + + offs = tree_to_uhwi (DECL_FIELD_OFFSET (field)); + bit_offs = tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field)); + + return (flag_chkp_narrow_bounds + && (flag_chkp_first_field_has_own_bounds + || offs + || bit_offs)); +} + +/* Perform narrowing for BOUNDS using bounds computed for field + access COMPONENT. ITER meaning is the same as for + chkp_intersect_bounds. */ +static tree +chkp_narrow_bounds_to_field (tree bounds, tree component, + gimple_stmt_iterator *iter) +{ + tree field = TREE_OPERAND (component, 1); + tree size = DECL_SIZE_UNIT (field); + tree field_ptr = chkp_build_addr_expr (component); + tree field_bounds; + + field_bounds = chkp_make_bounds (field_ptr, size, iter, false); + + return chkp_intersect_bounds (field_bounds, bounds, iter); +} + +/* Parse field or array access NODE. + + PTR ouput parameter holds a pointer to the outermost + object. + + BITFIELD output parameter is set to 1 if bitfield is + accessed and to 0 otherwise. If it is 1 then ELT holds + outer component for accessed bit field. + + SAFE outer parameter is set to 1 if access is safe and + checks are not required. + + BOUNDS outer parameter holds bounds to be used to check + access (may be NULL). + + If INNERMOST_BOUNDS is 1 then try to narrow bounds to the + innermost accessed component. */ +static void +chkp_parse_array_and_component_ref (tree node, tree *ptr, + tree *elt, bool *safe, + bool *bitfield, + tree *bounds, + gimple_stmt_iterator *iter, + bool innermost_bounds) +{ + tree comp_to_narrow = NULL_TREE; + tree last_comp = NULL_TREE; + bool array_ref_found = false; + tree *nodes; + tree var; + int len; + int i; + + /* Compute tree height for expression. */ + var = node; + len = 1; + while (TREE_CODE (var) == COMPONENT_REF + || TREE_CODE (var) == ARRAY_REF + || TREE_CODE (var) == VIEW_CONVERT_EXPR) + { + var = TREE_OPERAND (var, 0); + len++; + } + + gcc_assert (len > 1); + + /* It is more convenient for us to scan left-to-right, + so walk tree again and put all node to nodes vector + in reversed order. */ + nodes = XALLOCAVEC (tree, len); + nodes[len - 1] = node; + for (i = len - 2; i >= 0; i--) + nodes[i] = TREE_OPERAND (nodes[i + 1], 0); + + if (bounds) + *bounds = NULL; + *safe = true; + *bitfield = (TREE_CODE (node) == COMPONENT_REF + && DECL_BIT_FIELD_TYPE (TREE_OPERAND (node, 1))); + /* To get bitfield address we will need outer elemnt. */ + if (*bitfield) + *elt = nodes[len - 2]; + else + *elt = NULL_TREE; + + /* If we have indirection in expression then compute + outermost structure bounds. Computed bounds may be + narrowed later. */ + if (TREE_CODE (nodes[0]) == MEM_REF || INDIRECT_REF_P (nodes[0])) + { + *safe = false; + *ptr = TREE_OPERAND (nodes[0], 0); + if (bounds) + *bounds = chkp_find_bounds (*ptr, iter); + } + else + { + gcc_assert (TREE_CODE (var) == VAR_DECL + || TREE_CODE (var) == PARM_DECL + || TREE_CODE (var) == RESULT_DECL + || TREE_CODE (var) == STRING_CST + || TREE_CODE (var) == SSA_NAME); + + *ptr = chkp_build_addr_expr (var); + } + + /* In this loop we are trying to find a field access + requiring narrowing. There are two simple rules + for search: + 1. Leftmost array_ref is chosen if any. + 2. Rightmost suitable component_ref is chosen if innermost + bounds are required and no array_ref exists. */ + for (i = 1; i < len; i++) + { + var = nodes[i]; + + if (TREE_CODE (var) == ARRAY_REF) + { + *safe = false; + array_ref_found = true; + if (flag_chkp_narrow_bounds + && !flag_chkp_narrow_to_innermost_arrray + && (!last_comp + || chkp_may_narrow_to_field (TREE_OPERAND (last_comp, 1)))) + { + comp_to_narrow = last_comp; + break; + } + } + else if (TREE_CODE (var) == COMPONENT_REF) + { + tree field = TREE_OPERAND (var, 1); + + if (innermost_bounds + && !array_ref_found + && chkp_narrow_bounds_for_field (field)) + comp_to_narrow = var; + last_comp = var; + + if (flag_chkp_narrow_bounds + && flag_chkp_narrow_to_innermost_arrray + && TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE) + { + if (bounds) + *bounds = chkp_narrow_bounds_to_field (*bounds, var, iter); + comp_to_narrow = NULL; + } + } + else if (TREE_CODE (var) == VIEW_CONVERT_EXPR) + /* Nothing to do for it. */ + ; + else + gcc_unreachable (); + } + + if (comp_to_narrow && DECL_SIZE (TREE_OPERAND (comp_to_narrow, 1)) && bounds) + *bounds = chkp_narrow_bounds_to_field (*bounds, comp_to_narrow, iter); + + if (innermost_bounds && bounds && !*bounds) + *bounds = chkp_find_bounds (*ptr, iter); +} + +/* Compute and return bounds for address of OBJ. */ +static tree +chkp_make_addressed_object_bounds (tree obj, gimple_stmt_iterator *iter) +{ + tree bounds = chkp_get_registered_addr_bounds (obj); + + if (bounds) + return bounds; + + switch (TREE_CODE (obj)) + { + case VAR_DECL: + case PARM_DECL: + case RESULT_DECL: + bounds = chkp_get_bounds_for_decl_addr (obj); + break; + + case STRING_CST: + bounds = chkp_get_bounds_for_string_cst (obj); + break; + + case ARRAY_REF: + case COMPONENT_REF: + { + tree elt; + tree ptr; + bool safe; + bool bitfield; + + chkp_parse_array_and_component_ref (obj, &ptr, &elt, &safe, + &bitfield, &bounds, iter, true); + + gcc_assert (bounds); + } + break; + + case FUNCTION_DECL: + case LABEL_DECL: + bounds = chkp_get_zero_bounds (); + break; + + case MEM_REF: + bounds = chkp_find_bounds (TREE_OPERAND (obj, 0), iter); + break; + + case REALPART_EXPR: + case IMAGPART_EXPR: + bounds = chkp_make_addressed_object_bounds (TREE_OPERAND (obj, 0), iter); + break; + + default: + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "chkp_make_addressed_object_bounds: " + "unexpected object of type %s\n", + get_tree_code_name (TREE_CODE (obj))); + print_node (dump_file, "", obj, 0); + } + internal_error ("chkp_make_addressed_object_bounds: " + "Unexpected tree code %s", + get_tree_code_name (TREE_CODE (obj))); + } + + chkp_register_addr_bounds (obj, bounds); + + return bounds; +} + +/* Compute bounds for pointer PTR loaded from PTR_SRC. Generate statements + to compute bounds if required. Computed bounds should be available at + position pointed by ITER. + + If PTR_SRC is NULL_TREE then pointer definition is identified. + + If PTR_SRC is not NULL_TREE then ITER points to statements which loads + PTR. If PTR is a any memory reference then ITER points to a statement + after which bndldx will be inserterd. In both cases ITER will be updated + to point to the inserted bndldx statement. */ + +static tree +chkp_find_bounds_1 (tree ptr, tree ptr_src, gimple_stmt_iterator *iter) +{ + tree addr = NULL_TREE; + tree bounds = NULL_TREE; + + if (!ptr_src) + ptr_src = ptr; + + bounds = chkp_get_registered_bounds (ptr_src); + + if (bounds) + return bounds; + + switch (TREE_CODE (ptr_src)) + { + case MEM_REF: + case VAR_DECL: + if (BOUNDED_P (ptr_src)) + if (TREE_CODE (ptr) == VAR_DECL && DECL_REGISTER (ptr)) + bounds = chkp_get_zero_bounds (); + else + { + addr = chkp_build_addr_expr (ptr_src); + bounds = chkp_build_bndldx (addr, ptr, iter); + } + else + bounds = chkp_get_nonpointer_load_bounds (); + break; + + case ARRAY_REF: + case COMPONENT_REF: + addr = get_base_address (ptr_src); + if (DECL_P (addr) + || TREE_CODE (addr) == MEM_REF + || TREE_CODE (addr) == TARGET_MEM_REF) + { + if (BOUNDED_P (ptr_src)) + if (TREE_CODE (ptr) == VAR_DECL && DECL_REGISTER (ptr)) + bounds = chkp_get_zero_bounds (); + else + { + addr = chkp_build_addr_expr (ptr_src); + bounds = chkp_build_bndldx (addr, ptr, iter); + } + else + bounds = chkp_get_nonpointer_load_bounds (); + } + else + { + gcc_assert (TREE_CODE (addr) == SSA_NAME); + bounds = chkp_find_bounds (addr, iter); + } + break; + + case PARM_DECL: + gcc_unreachable (); + bounds = chkp_get_bound_for_parm (ptr_src); + break; + + case TARGET_MEM_REF: + addr = chkp_build_addr_expr (ptr_src); + bounds = chkp_build_bndldx (addr, ptr, iter); + break; + + case SSA_NAME: + bounds = chkp_get_registered_bounds (ptr_src); + if (!bounds) + { + gimple def_stmt = SSA_NAME_DEF_STMT (ptr_src); + gphi_iterator phi_iter; + + bounds = chkp_get_bounds_by_definition (ptr_src, def_stmt, &phi_iter); + + gcc_assert (bounds); + + if (gphi *def_phi = dyn_cast (def_stmt)) + { + unsigned i; + + for (i = 0; i < gimple_phi_num_args (def_phi); i++) + { + tree arg = gimple_phi_arg_def (def_phi, i); + tree arg_bnd; + gphi *phi_bnd; + + arg_bnd = chkp_find_bounds (arg, NULL); + + /* chkp_get_bounds_by_definition created new phi + statement and phi_iter points to it. + + Previous call to chkp_find_bounds could create + new basic block and therefore change phi statement + phi_iter points to. */ + phi_bnd = phi_iter.phi (); + + add_phi_arg (phi_bnd, arg_bnd, + gimple_phi_arg_edge (def_phi, i), + UNKNOWN_LOCATION); + } + + /* If all bound phi nodes have their arg computed + then we may finish its computation. See + chkp_finish_incomplete_bounds for more details. */ + if (chkp_may_finish_incomplete_bounds ()) + chkp_finish_incomplete_bounds (); + } + + gcc_assert (bounds == chkp_get_registered_bounds (ptr_src) + || chkp_incomplete_bounds (bounds)); + } + break; + + case ADDR_EXPR: + bounds = chkp_make_addressed_object_bounds (TREE_OPERAND (ptr_src, 0), iter); + break; + + case INTEGER_CST: + if (integer_zerop (ptr_src)) + bounds = chkp_get_none_bounds (); + else + bounds = chkp_get_invalid_op_bounds (); + break; + + default: + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "chkp_find_bounds: unexpected ptr of type %s\n", + get_tree_code_name (TREE_CODE (ptr_src))); + print_node (dump_file, "", ptr_src, 0); + } + internal_error ("chkp_find_bounds: Unexpected tree code %s", + get_tree_code_name (TREE_CODE (ptr_src))); + } + + if (!bounds) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (stderr, "chkp_find_bounds: cannot find bounds for pointer\n"); + print_node (dump_file, "", ptr_src, 0); + } + internal_error ("chkp_find_bounds: Cannot find bounds for pointer"); + } + + return bounds; +} + +/* Normal case for bounds search without forced narrowing. */ +static tree +chkp_find_bounds (tree ptr, gimple_stmt_iterator *iter) +{ + return chkp_find_bounds_1 (ptr, NULL_TREE, iter); +} + +/* Search bounds for pointer PTR loaded from PTR_SRC + by statement *ITER points to. */ +static tree +chkp_find_bounds_loaded (tree ptr, tree ptr_src, gimple_stmt_iterator *iter) +{ + return chkp_find_bounds_1 (ptr, ptr_src, iter); +} + +/* Helper function which checks type of RHS and finds all pointers in + it. For each found pointer we build it's accesses in LHS and RHS + objects and then call HANDLER for them. Function is used to copy + or initilize bounds for copied object. */ +static void +chkp_walk_pointer_assignments (tree lhs, tree rhs, void *arg, + assign_handler handler) +{ + tree type = TREE_TYPE (lhs); + + /* We have nothing to do with clobbers. */ + if (TREE_CLOBBER_P (rhs)) + return; + + if (BOUNDED_TYPE_P (type)) + handler (lhs, rhs, arg); + else if (RECORD_OR_UNION_TYPE_P (type)) + { + tree field; + + if (TREE_CODE (rhs) == CONSTRUCTOR) + { + unsigned HOST_WIDE_INT cnt; + tree val; + + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (rhs), cnt, field, val) + { + if (chkp_type_has_pointer (TREE_TYPE (field))) + { + tree lhs_field = chkp_build_component_ref (lhs, field); + chkp_walk_pointer_assignments (lhs_field, val, arg, handler); + } + } + } + else + for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && chkp_type_has_pointer (TREE_TYPE (field))) + { + tree rhs_field = chkp_build_component_ref (rhs, field); + tree lhs_field = chkp_build_component_ref (lhs, field); + chkp_walk_pointer_assignments (lhs_field, rhs_field, arg, handler); + } + } + else if (TREE_CODE (type) == ARRAY_TYPE) + { + unsigned HOST_WIDE_INT cur = 0; + tree maxval = TYPE_MAX_VALUE (TYPE_DOMAIN (type)); + tree etype = TREE_TYPE (type); + tree esize = TYPE_SIZE (etype); + + if (TREE_CODE (rhs) == CONSTRUCTOR) + { + unsigned HOST_WIDE_INT cnt; + tree purp, val, lhs_elem; + + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (rhs), cnt, purp, val) + { + if (purp && TREE_CODE (purp) == RANGE_EXPR) + { + tree lo_index = TREE_OPERAND (purp, 0); + tree hi_index = TREE_OPERAND (purp, 1); + + for (cur = (unsigned)tree_to_uhwi (lo_index); + cur <= (unsigned)tree_to_uhwi (hi_index); + cur++) + { + lhs_elem = chkp_build_array_ref (lhs, etype, esize, cur); + chkp_walk_pointer_assignments (lhs_elem, val, arg, handler); + } + } + else + { + if (purp) + { + gcc_assert (TREE_CODE (purp) == INTEGER_CST); + cur = tree_to_uhwi (purp); + } + + lhs_elem = chkp_build_array_ref (lhs, etype, esize, cur++); + + chkp_walk_pointer_assignments (lhs_elem, val, arg, handler); + } + } + } + /* Copy array only when size is known. */ + else if (maxval && !integer_minus_onep (maxval)) + for (cur = 0; cur <= TREE_INT_CST_LOW (maxval); cur++) + { + tree lhs_elem = chkp_build_array_ref (lhs, etype, esize, cur); + tree rhs_elem = chkp_build_array_ref (rhs, etype, esize, cur); + chkp_walk_pointer_assignments (lhs_elem, rhs_elem, arg, handler); + } + } + else + internal_error("chkp_walk_pointer_assignments: unexpected RHS type: %s", + get_tree_code_name (TREE_CODE (type))); +} + +/* Add code to copy bounds for assignment of RHS to LHS. + ARG is an iterator pointing ne code position. */ +static void +chkp_copy_bounds_for_elem (tree lhs, tree rhs, void *arg) +{ + gimple_stmt_iterator *iter = (gimple_stmt_iterator *)arg; + tree bounds = chkp_find_bounds (rhs, iter); + tree addr = chkp_build_addr_expr(lhs); + + chkp_build_bndstx (addr, rhs, bounds, iter); +} + +/* Emit static bound initilizers and size vars. */ +void +chkp_finish_file (void) +{ + struct varpool_node *node; + struct chkp_ctor_stmt_list stmts; + + if (seen_error ()) + return; + + /* Iterate through varpool and generate bounds initialization + constructors for all statically initialized pointers. */ + stmts.avail = MAX_STMTS_IN_STATIC_CHKP_CTOR; + stmts.stmts = NULL; + FOR_EACH_VARIABLE (node) + /* Check that var is actually emitted and we need and may initialize + its bounds. */ + if (node->need_bounds_init + && !POINTER_BOUNDS_P (node->decl) + && DECL_RTL (node->decl) + && MEM_P (DECL_RTL (node->decl)) + && TREE_ASM_WRITTEN (node->decl)) + { + chkp_walk_pointer_assignments (node->decl, + DECL_INITIAL (node->decl), + &stmts, + chkp_add_modification_to_stmt_list); + + if (stmts.avail <= 0) + { + cgraph_build_static_cdtor ('P', stmts.stmts, + MAX_RESERVED_INIT_PRIORITY + 3); + stmts.avail = MAX_STMTS_IN_STATIC_CHKP_CTOR; + stmts.stmts = NULL; + } + } + + if (stmts.stmts) + cgraph_build_static_cdtor ('P', stmts.stmts, + MAX_RESERVED_INIT_PRIORITY + 3); + + /* Iterate through varpool and generate bounds initialization + constructors for all static bounds vars. */ + stmts.avail = MAX_STMTS_IN_STATIC_CHKP_CTOR; + stmts.stmts = NULL; + FOR_EACH_VARIABLE (node) + if (node->need_bounds_init + && POINTER_BOUNDS_P (node->decl) + && TREE_ASM_WRITTEN (node->decl)) + { + tree bnd = node->decl; + tree var; + + gcc_assert (DECL_INITIAL (bnd) + && TREE_CODE (DECL_INITIAL (bnd)) == ADDR_EXPR); + + var = TREE_OPERAND (DECL_INITIAL (bnd), 0); + chkp_output_static_bounds (bnd, var, &stmts); + } + + if (stmts.stmts) + cgraph_build_static_cdtor ('B', stmts.stmts, + MAX_RESERVED_INIT_PRIORITY + 2); + + delete chkp_static_var_bounds; + delete chkp_bounds_map; +} + +/* An instrumentation function which is called for each statement + having memory access we want to instrument. It inserts check + code and bounds copy code. + + ITER points to statement to instrument. + + NODE holds memory access in statement to check. + + LOC holds the location information for statement. + + DIRFLAGS determines whether access is read or write. + + ACCESS_OFFS should be added to address used in NODE + before check. + + ACCESS_SIZE holds size of checked access. + + SAFE indicates if NODE access is safe and should not be + checked. */ +static void +chkp_process_stmt (gimple_stmt_iterator *iter, tree node, + location_t loc, tree dirflag, + tree access_offs, tree access_size, + bool safe) +{ + tree node_type = TREE_TYPE (node); + tree size = access_size ? access_size : TYPE_SIZE_UNIT (node_type); + tree addr_first = NULL_TREE; /* address of the first accessed byte */ + tree addr_last = NULL_TREE; /* address of the last accessed byte */ + tree ptr = NULL_TREE; /* a pointer used for dereference */ + tree bounds = NULL_TREE; + + /* We do not need instrumentation for clobbers. */ + if (dirflag == integer_one_node + && gimple_code (gsi_stmt (*iter)) == GIMPLE_ASSIGN + && TREE_CLOBBER_P (gimple_assign_rhs1 (gsi_stmt (*iter)))) + return; + + switch (TREE_CODE (node)) + { + case ARRAY_REF: + case COMPONENT_REF: + { + bool bitfield; + tree elt; + + if (safe) + { + /* We are not going to generate any checks, so do not + generate bounds as well. */ + addr_first = chkp_build_addr_expr (node); + break; + } + + chkp_parse_array_and_component_ref (node, &ptr, &elt, &safe, + &bitfield, &bounds, iter, false); + + /* Break if there is no dereference and operation is safe. */ + + if (bitfield) + { + tree field = TREE_OPERAND (node, 1); + + if (TREE_CODE (DECL_SIZE_UNIT (field)) == INTEGER_CST) + size = DECL_SIZE_UNIT (field); + + if (elt) + elt = chkp_build_addr_expr (elt); + addr_first = fold_convert_loc (loc, ptr_type_node, elt ? elt : ptr); + addr_first = fold_build_pointer_plus_loc (loc, + addr_first, + byte_position (field)); + } + else + addr_first = chkp_build_addr_expr (node); + } + break; + + case INDIRECT_REF: + ptr = TREE_OPERAND (node, 0); + addr_first = ptr; + break; + + case MEM_REF: + ptr = TREE_OPERAND (node, 0); + addr_first = chkp_build_addr_expr (node); + break; + + case TARGET_MEM_REF: + ptr = TMR_BASE (node); + addr_first = chkp_build_addr_expr (node); + break; + + case ARRAY_RANGE_REF: + printf("ARRAY_RANGE_REF\n"); + debug_gimple_stmt(gsi_stmt(*iter)); + debug_tree(node); + gcc_unreachable (); + break; + + case BIT_FIELD_REF: + { + tree offs, rem, bpu; + + gcc_assert (!access_offs); + gcc_assert (!access_size); + + bpu = fold_convert (size_type_node, bitsize_int (BITS_PER_UNIT)); + offs = fold_convert (size_type_node, TREE_OPERAND (node, 2)); + rem = size_binop_loc (loc, TRUNC_MOD_EXPR, offs, bpu); + offs = size_binop_loc (loc, TRUNC_DIV_EXPR, offs, bpu); + + size = fold_convert (size_type_node, TREE_OPERAND (node, 1)); + size = size_binop_loc (loc, PLUS_EXPR, size, rem); + size = size_binop_loc (loc, CEIL_DIV_EXPR, size, bpu); + size = fold_convert (size_type_node, size); + + chkp_process_stmt (iter, TREE_OPERAND (node, 0), loc, + dirflag, offs, size, safe); + return; + } + break; + + case VAR_DECL: + case RESULT_DECL: + case PARM_DECL: + if (dirflag != integer_one_node + || DECL_REGISTER (node)) + return; + + safe = true; + addr_first = chkp_build_addr_expr (node); + break; + + default: + return; + } + + /* If addr_last was not computed then use (addr_first + size - 1) + expression to compute it. */ + if (!addr_last) + { + addr_last = fold_build_pointer_plus_loc (loc, addr_first, size); + addr_last = fold_build_pointer_plus_hwi_loc (loc, addr_last, -1); + } + + /* Shift both first_addr and last_addr by access_offs if specified. */ + if (access_offs) + { + addr_first = fold_build_pointer_plus_loc (loc, addr_first, access_offs); + addr_last = fold_build_pointer_plus_loc (loc, addr_last, access_offs); + } + + /* Generate bndcl/bndcu checks if memory access is not safe. */ + if (!safe) + { + gimple_stmt_iterator stmt_iter = *iter; + + if (!bounds) + bounds = chkp_find_bounds (ptr, iter); + + chkp_check_mem_access (addr_first, addr_last, bounds, + stmt_iter, loc, dirflag); + } + + /* We need to store bounds in case pointer is stored. */ + if (dirflag == integer_one_node + && chkp_type_has_pointer (node_type) + && flag_chkp_store_bounds) + { + gimple stmt = gsi_stmt (*iter); + tree rhs1 = gimple_assign_rhs1 (stmt); + enum tree_code rhs_code = gimple_assign_rhs_code (stmt); + + if (get_gimple_rhs_class (rhs_code) == GIMPLE_SINGLE_RHS) + chkp_walk_pointer_assignments (node, rhs1, iter, + chkp_copy_bounds_for_elem); + else + { + bounds = chkp_compute_bounds_for_assignment (NULL_TREE, stmt); + chkp_build_bndstx (addr_first, rhs1, bounds, iter); + } + } +} + +/* Add code to copy bounds for all pointers copied + in ASSIGN created during inline of EDGE. */ +void +chkp_copy_bounds_for_assign (gimple assign, struct cgraph_edge *edge) +{ + tree lhs = gimple_assign_lhs (assign); + tree rhs = gimple_assign_rhs1 (assign); + gimple_stmt_iterator iter = gsi_for_stmt (assign); + + if (!flag_chkp_store_bounds) + return; + + chkp_walk_pointer_assignments (lhs, rhs, &iter, chkp_copy_bounds_for_elem); + + /* We should create edges for all created calls to bndldx and bndstx. */ + while (gsi_stmt (iter) != assign) + { + gimple stmt = gsi_stmt (iter); + if (gimple_code (stmt) == GIMPLE_CALL) + { + tree fndecl = gimple_call_fndecl (stmt); + struct cgraph_node *callee = cgraph_node::get_create (fndecl); + struct cgraph_edge *new_edge; + + gcc_assert (fndecl == chkp_bndstx_fndecl + || fndecl == chkp_bndldx_fndecl + || fndecl == chkp_ret_bnd_fndecl); + + new_edge = edge->caller->create_edge (callee, + as_a (stmt), + edge->count, + edge->frequency); + new_edge->frequency = compute_call_stmt_bb_frequency + (edge->caller->decl, gimple_bb (stmt)); + } + gsi_prev (&iter); + } +} + +/* Some code transformation made during instrumentation pass + may put code into inconsistent state. Here we find and fix + such flaws. */ +void +chkp_fix_cfg () +{ + basic_block bb; + gimple_stmt_iterator i; + + /* We could insert some code right after stmt which ends bb. + We wanted to put this code on fallthru edge but did not + add new edges from the beginning because it may cause new + phi node creation which may be incorrect due to incomplete + bound phi nodes. */ + FOR_ALL_BB_FN (bb, cfun) + for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i)) + { + gimple stmt = gsi_stmt (i); + gimple_stmt_iterator next = i; + + gsi_next (&next); + + if (stmt_ends_bb_p (stmt) + && !gsi_end_p (next)) + { + edge fall = find_fallthru_edge (bb->succs); + basic_block dest = NULL; + int flags = 0; + + gcc_assert (fall); + + /* We cannot split abnormal edge. Therefore we + store its params, make it regular and then + rebuild abnormal edge after split. */ + if (fall->flags & EDGE_ABNORMAL) + { + flags = fall->flags & ~EDGE_FALLTHRU; + dest = fall->dest; + + fall->flags &= ~EDGE_COMPLEX; + } + + while (!gsi_end_p (next)) + { + gimple next_stmt = gsi_stmt (next); + gsi_remove (&next, false); + gsi_insert_on_edge (fall, next_stmt); + } + + gsi_commit_edge_inserts (); + + /* Re-create abnormal edge. */ + if (dest) + make_edge (bb, dest, flags); + } + } +} + +/* Walker callback for chkp_replace_function_pointers. Replaces + function pointer in the specified operand with pointer to the + instrumented function version. */ +static tree +chkp_replace_function_pointer (tree *op, int *walk_subtrees, + void *data ATTRIBUTE_UNUSED) +{ + if (TREE_CODE (*op) == FUNCTION_DECL + && !lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (*op)) + && (DECL_BUILT_IN_CLASS (*op) == NOT_BUILT_IN + /* For builtins we replace pointers only for selected + function and functions having definitions. */ + || (DECL_BUILT_IN_CLASS (*op) == BUILT_IN_NORMAL + && (chkp_instrument_normal_builtin (*op) + || gimple_has_body_p (*op))))) + { + struct cgraph_node *node = cgraph_node::get_create (*op); + struct cgraph_node *clone = NULL; + + if (!node->instrumentation_clone) + clone = chkp_maybe_create_clone (*op); + + if (clone) + *op = clone->decl; + *walk_subtrees = 0; + } + + return NULL; +} + +/* This function searches for function pointers in statement + pointed by GSI and replaces them with pointers to instrumented + function versions. */ +static void +chkp_replace_function_pointers (gimple_stmt_iterator *gsi) +{ + gimple stmt = gsi_stmt (*gsi); + /* For calls we want to walk call args only. */ + if (gimple_code (stmt) == GIMPLE_CALL) + { + unsigned i; + for (i = 0; i < gimple_call_num_args (stmt); i++) + walk_tree (gimple_call_arg_ptr (stmt, i), + chkp_replace_function_pointer, NULL, NULL); + } + else + walk_gimple_stmt (gsi, NULL, chkp_replace_function_pointer, NULL); +} + +/* This function instruments all statements working with memory, + calls and rets. + + It also removes excess statements from static initializers. */ +static void +chkp_instrument_function (void) +{ + basic_block bb, next; + gimple_stmt_iterator i; + enum gimple_rhs_class grhs_class; + bool safe = lookup_attribute ("chkp ctor", DECL_ATTRIBUTES (cfun->decl)); + + bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb; + do + { + next = bb->next_bb; + for (i = gsi_start_bb (bb); !gsi_end_p (i); ) + { + gimple s = gsi_stmt (i); + + /* Skip statement marked to not be instrumented. */ + if (chkp_marked_stmt_p (s)) + { + gsi_next (&i); + continue; + } + + chkp_replace_function_pointers (&i); + + switch (gimple_code (s)) + { + case GIMPLE_ASSIGN: + chkp_process_stmt (&i, gimple_assign_lhs (s), + gimple_location (s), integer_one_node, + NULL_TREE, NULL_TREE, safe); + chkp_process_stmt (&i, gimple_assign_rhs1 (s), + gimple_location (s), integer_zero_node, + NULL_TREE, NULL_TREE, safe); + grhs_class = get_gimple_rhs_class (gimple_assign_rhs_code (s)); + if (grhs_class == GIMPLE_BINARY_RHS) + chkp_process_stmt (&i, gimple_assign_rhs2 (s), + gimple_location (s), integer_zero_node, + NULL_TREE, NULL_TREE, safe); + break; + + case GIMPLE_RETURN: + { + greturn *r = as_a (s); + if (gimple_return_retval (r) != NULL_TREE) + { + chkp_process_stmt (&i, gimple_return_retval (r), + gimple_location (r), + integer_zero_node, + NULL_TREE, NULL_TREE, safe); + + /* Additionally 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: + ; + } + + gsi_next (&i); + + /* We do not need any actual pointer stores in checker + static initializer. */ + if (lookup_attribute ("chkp ctor", DECL_ATTRIBUTES (cfun->decl)) + && gimple_code (s) == GIMPLE_ASSIGN + && gimple_store_p (s)) + { + gimple_stmt_iterator del_iter = gsi_for_stmt (s); + gsi_remove (&del_iter, true); + unlink_stmt_vdef (s); + release_defs(s); + } + } + bb = next; + } + while (bb); + + /* Some input params may have bounds and be address taken. In this case + we should store incoming bounds into bounds table. */ + tree arg; + if (flag_chkp_store_bounds) + for (arg = DECL_ARGUMENTS (cfun->decl); arg; arg = DECL_CHAIN (arg)) + if (TREE_ADDRESSABLE (arg)) + { + if (BOUNDED_P (arg)) + { + tree bounds = chkp_get_next_bounds_parm (arg); + tree def_ptr = ssa_default_def (cfun, arg); + gimple_stmt_iterator iter + = gsi_start_bb (chkp_get_entry_block ()); + chkp_build_bndstx (chkp_build_addr_expr (arg), + def_ptr ? def_ptr : arg, + bounds, &iter); + + /* Skip bounds arg. */ + arg = TREE_CHAIN (arg); + } + else if (chkp_type_has_pointer (TREE_TYPE (arg))) + { + tree orig_arg = arg; + bitmap slots = BITMAP_ALLOC (NULL); + gimple_stmt_iterator iter + = gsi_start_bb (chkp_get_entry_block ()); + bitmap_iterator bi; + unsigned bnd_no; + + chkp_find_bound_slots (TREE_TYPE (arg), slots); + + EXECUTE_IF_SET_IN_BITMAP (slots, 0, bnd_no, bi) + { + tree bounds = chkp_get_next_bounds_parm (arg); + HOST_WIDE_INT offs = bnd_no * POINTER_SIZE / BITS_PER_UNIT; + tree addr = chkp_build_addr_expr (orig_arg); + tree ptr = build2 (MEM_REF, ptr_type_node, addr, + build_int_cst (ptr_type_node, offs)); + chkp_build_bndstx (chkp_build_addr_expr (ptr), ptr, + bounds, &iter); + + arg = DECL_CHAIN (arg); + } + BITMAP_FREE (slots); + } + } +} + +/* Find init/null/copy_ptr_bounds calls and replace them + with assignments. It should allow better code + optimization. */ + +static void +chkp_remove_useless_builtins () +{ + basic_block bb; + gimple_stmt_iterator gsi; + + FOR_EACH_BB_FN (bb, cfun) + { + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple stmt = gsi_stmt (gsi); + tree fndecl; + enum built_in_function fcode; + + /* Find builtins returning first arg and replace + them with assignments. */ + if (gimple_code (stmt) == GIMPLE_CALL + && (fndecl = gimple_call_fndecl (stmt)) + && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && (fcode = DECL_FUNCTION_CODE (fndecl)) + && (fcode == BUILT_IN_CHKP_INIT_PTR_BOUNDS + || fcode == BUILT_IN_CHKP_NULL_PTR_BOUNDS + || fcode == BUILT_IN_CHKP_COPY_PTR_BOUNDS + || fcode == BUILT_IN_CHKP_SET_PTR_BOUNDS)) + { + tree res = gimple_call_arg (stmt, 0); + update_call_from_tree (&gsi, res); + stmt = gsi_stmt (gsi); + update_stmt (stmt); + } + } + } +} + +/* Initialize pass. */ +static void +chkp_init (void) +{ + basic_block bb; + gimple_stmt_iterator i; + + in_chkp_pass = true; + + for (bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb; bb; bb = bb->next_bb) + for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i)) + chkp_unmark_stmt (gsi_stmt (i)); + + chkp_invalid_bounds = new hash_set; + chkp_completed_bounds_set = new hash_set; + delete chkp_reg_bounds; + chkp_reg_bounds = new hash_map; + delete chkp_bound_vars; + chkp_bound_vars = new hash_map; + chkp_reg_addr_bounds = new hash_map; + chkp_incomplete_bounds_map = new hash_map; + delete chkp_bounds_map; + chkp_bounds_map = new hash_map; + chkp_abnormal_copies = BITMAP_GGC_ALLOC (); + + entry_block = NULL; + zero_bounds = NULL_TREE; + none_bounds = NULL_TREE; + incomplete_bounds = integer_zero_node; + tmp_var = NULL_TREE; + size_tmp_var = NULL_TREE; + + chkp_uintptr_type = lang_hooks.types.type_for_mode (ptr_mode, true); + + /* We create these constant bounds once for each object file. + These symbols go to comdat section and result in single copy + of each one in the final binary. */ + chkp_get_zero_bounds_var (); + chkp_get_none_bounds_var (); + + calculate_dominance_info (CDI_DOMINATORS); + calculate_dominance_info (CDI_POST_DOMINATORS); + + bitmap_obstack_initialize (NULL); +} + +/* Finalize instrumentation pass. */ +static void +chkp_fini (void) +{ + in_chkp_pass = false; + + delete chkp_invalid_bounds; + delete chkp_completed_bounds_set; + delete chkp_reg_addr_bounds; + delete chkp_incomplete_bounds_map; + + free_dominance_info (CDI_DOMINATORS); + free_dominance_info (CDI_POST_DOMINATORS); + + bitmap_obstack_release (NULL); + + entry_block = NULL; + zero_bounds = NULL_TREE; + none_bounds = NULL_TREE; +} + +/* Main instrumentation pass function. */ +static unsigned int +chkp_execute (void) +{ + chkp_init (); + + chkp_instrument_function (); + + chkp_remove_useless_builtins (); + + chkp_function_mark_instrumented (cfun->decl); + + chkp_fix_cfg (); + + chkp_fini (); + + return 0; +} + +/* Instrumentation pass gate. */ +static bool +chkp_gate (void) +{ + return cgraph_node::get (cfun->decl)->instrumentation_clone + || lookup_attribute ("chkp ctor", DECL_ATTRIBUTES (cfun->decl)); +} + +namespace { + +const pass_data pass_data_chkp = +{ + GIMPLE_PASS, /* type */ + "chkp", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + PROP_ssa | PROP_cfg, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_verify_il + | TODO_update_ssa /* todo_flags_finish */ +}; + +class pass_chkp : public gimple_opt_pass +{ +public: + pass_chkp (gcc::context *ctxt) + : gimple_opt_pass (pass_data_chkp, ctxt) + {} + + /* opt_pass methods: */ + virtual opt_pass * clone () + { + return new pass_chkp (m_ctxt); + } + + virtual bool gate (function *) + { + return chkp_gate (); + } + + virtual unsigned int execute (function *) + { + return chkp_execute (); + } + +}; // class pass_chkp + +} // anon namespace + +gimple_opt_pass * +make_pass_chkp (gcc::context *ctxt) +{ + return new pass_chkp (ctxt); +} + +#include "gt-tree-chkp.h" -- cgit v1.2.1