From patchwork Tue Oct 14 10:53:58 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ilya Enkovich X-Patchwork-Id: 399437 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id F3C1D14014D for ; Tue, 14 Oct 2014 21:54:19 +1100 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:references:mime-version :content-type:in-reply-to; q=dns; s=default; b=fX2MMBSL5/3UULad3 MBwJWGEmXW5f5AKHdKJmAqhu/Ktdhr9gq76XMArd0efdZqb3nXIbBcpVDIm7NFuj MmkjZ3+sb5p+08O3dqnfhgJ3TYRUKWCfco34GrjJRhmmMjKxfpruYh2bwXRp1T0s +zik2lnQ4FQuP9jyzoiXwnQrnk= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:references:mime-version :content-type:in-reply-to; s=default; bh=tD8tVeoIqsur2kZO5vgKimg I04o=; b=wkn/6V3EATbRMO7OX5v2FdmHCmurKYBL3FSYMuIz84jhU4QE0av6ZWj hNQwpCrR0zSzJif6tnTwkf+LWTTKaKtVo2Biw2KI8zkYuqkO1BgaAfxb20oYtWqZ 0ELLazjd+6lnOQf/3em57UWN+ZwXiYC2tIhDy4ey3jbfkfROY5jk= Received: (qmail 28015 invoked by alias); 14 Oct 2014 10:54:12 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 28003 invoked by uid 89); 14 Oct 2014 10:54:10 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.1 required=5.0 tests=AWL, BAYES_00, FREEMAIL_FROM, KAM_STOCKGEN, RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=no version=3.3.2 X-HELO: mail-pa0-f42.google.com Received: from mail-pa0-f42.google.com (HELO mail-pa0-f42.google.com) (209.85.220.42) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-SHA encrypted) ESMTPS; Tue, 14 Oct 2014 10:54:06 +0000 Received: by mail-pa0-f42.google.com with SMTP id bj1so7677459pad.15 for ; Tue, 14 Oct 2014 03:54:04 -0700 (PDT) X-Received: by 10.68.92.66 with SMTP id ck2mr4583639pbb.72.1413284044776; Tue, 14 Oct 2014 03:54:04 -0700 (PDT) Received: from msticlxl57.ims.intel.com ([192.55.54.42]) by mx.google.com with ESMTPSA id qh4sm2530560pbb.35.2014.10.14.03.54.02 for (version=TLSv1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 14 Oct 2014 03:54:04 -0700 (PDT) Date: Tue, 14 Oct 2014 14:53:58 +0400 From: Ilya Enkovich To: Jeff Law Cc: gcc-patches Subject: Re: [PATCH, Pointer Bounds Checker 14/x] Passes [3/n] Helper functions Message-ID: <20141014105358.GD58443@msticlxl57.ims.intel.com> References: <20141008185708.GC13454@msticlxl57.ims.intel.com> <543C3E94.10305@redhat.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.21 (2010-09-15) X-IsSubscribed: yes On 14 Oct 01:13, Ilya Enkovich wrote: > 2014-10-14 1:05 GMT+04:00 Jeff Law : > > > > Where does chkp_can_be_shared get used? Normally the thing to do would > > just be to call unshare_expr. It'll create copies as needed. If it's > > something that is supposed to be shared then it'll leave it alone. If you > > need to do something different than unshare_expr, then that needs deeper > > investigation as you're mucking around in the structure sharing assumptions > > and that's not to be done lightly. > > All its uses are like following: > > if (!chkp_can_be_shared (rhs1)) > rhs1 = unshare_expr (rhs1); > > If unshare_expr avoids copies by itself then this check is useless and > I should remove all its uses. > > Thanks, > Ilya > > > > > jeff > > > > Here is a version with no chkp_can_be_shared function. Patches having its uses were updated. Thanks, Ilya --- 2014-10-08 Ilya Enkovich * tree-chkp.c (assign_handler): New. (chkp_get_zero_bounds): New. (chkp_uintptr_type): New. (chkp_none_bounds_var): New. (entry_block): New. (zero_bounds): New. (none_bounds): New. (incomplete_bounds): New. (tmp_var): New. (size_tmp_var): New. (chkp_abnormal_copies): New. (chkp_invalid_bounds): New. (chkp_completed_bounds_set): New. (chkp_reg_bounds): New. (chkp_bound_vars): New. (chkp_reg_addr_bounds): New. (chkp_incomplete_bounds_map): New. (chkp_static_var_bounds): New. (in_chkp_pass): New. (CHKP_BOUND_TMP_NAME): New. (CHKP_SIZE_TMP_NAME): New. (CHKP_BOUNDS_OF_SYMBOL_PREFIX): New. (CHKP_STRING_BOUNDS_PREFIX): New. (CHKP_VAR_BOUNDS_PREFIX): New. (CHKP_NONE_BOUNDS_VAR_NAME): New. (chkp_get_tmp_var): New. (chkp_get_tmp_reg): New. (chkp_get_size_tmp_var): New. (chkp_register_addr_bounds): New. (chkp_get_registered_addr_bounds): New. (chkp_mark_completed_bounds): New. (chkp_completed_bounds): New. (chkp_erase_completed_bounds): New. (chkp_register_incomplete_bounds): New. (chkp_incomplete_bounds): New. (chkp_erase_incomplete_bounds): New. (chkp_mark_invalid_bounds): New. (chkp_valid_bounds): New. (chkp_mark_invalid_bounds_walker): New. (chkp_build_addr_expr): New. (chkp_get_entry_block): New. (chkp_get_bounds_var): New. (chkp_get_registered_bounds): New. (chkp_check_lower): New. (chkp_check_upper): New. (chkp_check_mem_access): New. (chkp_build_component_ref): New. (chkp_build_array_ref): New. (chkp_make_bounds): New. (chkp_get_none_bounds_var): New. (chkp_get_zero_bounds): New. (chkp_get_none_bounds): New. (chkp_get_invalid_op_bounds): New. (chkp_get_nonpointer_load_bounds): New. (chkp_get_next_bounds_parm): New. (chkp_build_bndldx): New. (chkp_make_static_bounds): New. (chkp_generate_extern_var_bounds): New. (chkp_intersect_bounds): New. (chkp_may_narrow_to_field): New. (chkp_narrow_bounds_for_field): New. (chkp_narrow_bounds_to_field): New. (chkp_walk_pointer_assignments): New. (chkp_init): New. * tree-chkp.h (chkp_get_none_bounds_var): New. (chkp_check_mem_access): New. diff --git a/gcc/tree-chkp.c b/gcc/tree-chkp.c index eb7a8df..9245fa7 100644 --- a/gcc/tree-chkp.c +++ b/gcc/tree-chkp.c @@ -60,6 +60,10 @@ along with GCC; see the file COPYING3. If not see #include "rtl.h" /* For MEM_P, assign_temp. */ #include "tree-dfa.h" +typedef void (*assign_handler)(tree, tree, void *); + +static tree chkp_get_zero_bounds (); + #define chkp_bndldx_fndecl \ (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDLDX)) #define chkp_bndstx_fndecl \ @@ -83,11 +87,37 @@ along with GCC; see the file COPYING3. If not see #define chkp_extract_upper_fndecl \ (targetm.builtin_chkp_function (BUILT_IN_CHKP_EXTRACT_UPPER)) -static GTY (()) tree chkp_zero_bounds_var; +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" /* Return 1 if function FNDECL is instrumented by Pointer Bounds Checker. */ @@ -167,6 +197,130 @@ 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 @@ -186,6 +340,45 @@ chkp_make_bounds_for_struct_addr (tree ptr) 2, ptr, size); } +/* 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. + 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; +} + /* Return 1 if type TYPE is a pointer type or a structure having a pointer type as one of its fields. Otherwise return 0. */ @@ -253,6 +446,59 @@ chkp_set_bounds (tree node, tree val) chkp_bounds_map->put (node, val); } +/* 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); +} + +/* 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; +} + +/* 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; +} + /* Force OP to be suitable for using as an argument for call. New statements (if any) go to SEQ. */ static tree @@ -271,6 +517,173 @@ chkp_force_gimple_call_op (tree op, gimple_seq *seq) 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 (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 (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); +} + +/* 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; +} + /* Fill HAVE_BOUND output bitmap with information about bounds requred for object of type TYPE. @@ -395,6 +808,59 @@ chkp_make_static_const_bounds (HOST_WIDE_INT lb, 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) @@ -406,6 +872,91 @@ chkp_get_zero_bounds_var (void) 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 bounds used as returned by call which produced SSA name VAL. */ gimple @@ -426,6 +977,22 @@ chkp_retbnd_call_by_val (tree val) 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; +} + /* Build and return CALL_EXPR for bndstx builtin with specified arguments. */ tree @@ -440,6 +1007,41 @@ chkp_build_bndldx_call (tree addr, tree ptr) 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 @@ -496,6 +1098,153 @@ chkp_build_make_bounds_call (tree lower_bound, tree size) 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) + { + 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; + + 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_with_ops (COND_EXPR, size, + 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 @@ -520,4 +1269,255 @@ chkp_variable_size_type (tree type) return res; } +/* 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); +} + +/* 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))); +} + +/* 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); +} + #include "gt-tree-chkp.h" diff --git a/gcc/tree-chkp.h b/gcc/tree-chkp.h index 0357658..9e2ccdb 100644 --- a/gcc/tree-chkp.h +++ b/gcc/tree-chkp.h @@ -34,6 +34,11 @@ extern bool chkp_type_has_pointer (const_tree type); extern unsigned chkp_type_bounds_count (const_tree type); extern tree chkp_make_bounds_for_struct_addr (tree ptr); extern tree chkp_get_zero_bounds_var (void); +extern tree chkp_get_none_bounds_var (void); +extern void chkp_check_mem_access (tree first, tree last, tree bounds, + gimple_stmt_iterator iter, + location_t location, + tree dirflag); extern bool chkp_variable_size_type (tree type); extern tree chkp_build_make_bounds_call (tree lb, tree size); extern tree chkp_build_bndldx_call (tree addr, tree ptr);