From patchwork Wed Oct 17 09:35:55 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 191993 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]) by ozlabs.org (Postfix) with SMTP id 3C9872C00B5 for ; Wed, 17 Oct 2012 20:36:10 +1100 (EST) Comment: DKIM? See http://www.dkim.org DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=gcc.gnu.org; s=default; x=1351071371; h=Comment: DomainKey-Signature:Received:Received:Received:Received:Received: Received:Received:Date:From:To:Cc:Subject:Message-ID:Reply-To: References:MIME-Version:Content-Type:Content-Disposition: In-Reply-To:User-Agent:Mailing-List:Precedence:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:Sender: Delivered-To; bh=jEC8eyx2PETeR9nfm3G16RX+ybs=; b=cg2wwefcF6kA1Dk pBqk78dPUn9U4aVvdbxT9wOQoZP7FtVdUAS1nh2w4Tky55rrDf9Adcxb5LtB+DBq No4twnQxZCO4I3uHP/MB07B86n+VGqaVO9sgx2DsNFKNKcMxgXZNYOpTH9BG53Fh kvU/NShzKauqGdQCIShubsJBFk0Q= Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=default; d=gcc.gnu.org; h=Received:Received:X-SWARE-Spam-Status:X-Spam-Check-By:Received:Received:Received:Received:Received:Date:From:To:Cc:Subject:Message-ID:Reply-To:References:MIME-Version:Content-Type:Content-Disposition:In-Reply-To:User-Agent:X-IsSubscribed:Mailing-List:Precedence:List-Id:List-Unsubscribe:List-Archive:List-Post:List-Help:Sender:Delivered-To; b=V0FCSNuuNm+nztfCEHNENNCF4vKqjZoA6p3xpsJeJzt5+ruEiFFhHiZx9AuUrt C4wtDygX2UjYwGyduvaA1Z4Pq5lq9b+6cTSoci+NhTk14rfB2nh/cx06U8aT2nB3 YIbuPgWhdyHNxkEzKm4EgC21EpHBNk1HZLOA1tpCbWwuU=; Received: (qmail 21395 invoked by alias); 17 Oct 2012 09:36:07 -0000 Received: (qmail 21200 invoked by uid 22791); 17 Oct 2012 09:36:06 -0000 X-SWARE-Spam-Status: No, hits=-6.5 required=5.0 tests=AWL, BAYES_00, KHOP_RCVD_UNTRUST, RCVD_IN_DNSWL_HI, RCVD_IN_HOSTKARMA_W, RP_MATCHES_RCVD, SPF_HELO_PASS, TW_TM X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 17 Oct 2012 09:35:58 +0000 Received: from int-mx12.intmail.prod.int.phx2.redhat.com (int-mx12.intmail.prod.int.phx2.redhat.com [10.5.11.25]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q9H9Zwd4023604 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Wed, 17 Oct 2012 05:35:58 -0400 Received: from zalov.redhat.com (vpn1-7-120.ams2.redhat.com [10.36.7.120]) by int-mx12.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q9H9ZuRg028705 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Wed, 17 Oct 2012 05:35:57 -0400 Received: from zalov.cz (localhost [127.0.0.1]) by zalov.redhat.com (8.14.5/8.14.5) with ESMTP id q9H9Ztfs010850; Wed, 17 Oct 2012 11:35:55 +0200 Received: (from jakub@localhost) by zalov.cz (8.14.5/8.14.5/Submit) id q9H9Zt1v010849; Wed, 17 Oct 2012 11:35:55 +0200 Date: Wed, 17 Oct 2012 11:35:55 +0200 From: Jakub Jelinek To: Xinliang David Li Cc: Diego Novillo , Dodji Seketeli , gcc-patches@gcc.gnu.org, Wei Mi Subject: [asan] Protection of stack vars (take 3) Message-ID: <20121017093555.GT584@tucnak.redhat.com> Reply-To: Jakub Jelinek References: <20121012162714.GO584@tucnak.redhat.com> <20121012181043.GR584@tucnak.redhat.com> <20121016103119.GG584@tucnak.redhat.com> <20121016215245.GN584@tucnak.redhat.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.21 (2010-09-15) X-IsSubscribed: yes 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 On Tue, Oct 16, 2012 at 03:56:31PM -0700, Xinliang David Li wrote: > >> 1) I am not sure if the stack slot sharing is handled correctly. If I > >> read the code correctly, the redzone var will be only created for the > >> representative variable in a partition -- will this lead to false > >> negatives? As I asked before, should stack slot sharing even turned > >> on? > > > > I thought we don't merge stack slots if the vars have different size, > > apparently I've been wrong about that. So I'll make sure those aren't > > shared with flag_asan. When the size is the same, there should be no false > > negatives. > > > >> 2) Performance tuning -- it is probably better to skip those variables > >> that are compiler generated -- is there any point guarding them? > > > > In my (admittedly very limited) testing only at -O0 some compiler generated > > vars (SSA_NAMEs) got guards. The trouble here is -fasan -fstack-protector > > combination. I wonder if there can be any DECL_ARTIFICIAL (or non-VAR_DECL/ > > RESULT_DECL) decls for which stack_protect_decl_phase returns 1 or 2. > > If it is unlikely, the best might be to protect all phase 1 and 2 vars and > > if flag_asan call expand_stack_vars once more for partitions where any of > > the protected vars are user vars, and finally the current expand_stack_vars > > (NULL) which would not do any asan protection. > > > > Do we expect people to use -fasan together with -fstack-protector? Is > it enough to just skip those DECL_ARTIFICIAL ones? most of the > ssa_name var decls are already skipped already. Why can't they? -fstack-protector is for some distros even on by default, or others turn it on e.g. in the standard CFLAGS/CXXFLAGS for building packages. And -fstack-protector has a value even with -fasan, e.g. if you call some function not instrumented with -fasan (some other library, etc.) and pass an address of an automatic array etc. to it. Anyway, here is (again, very lightly tested just by skimming assembly on a few testcases) an updated patch that won't do stack slot sharing for variables with different sizes, and will not put artificial vars/SSA_NAMEs into protected stack block, but below it, unless -fstack-protector wants to push those high in the frame. Also, it attempts to find best var name for each partition, say if some partition contains both some artificial variable, some variable without name and some with name, it will pick the non-artificial one with name. Ok for asan? 2012-10-17 Jakub Jelinek * Makefile.in (asan.o): Depend on $(EXPR_H) $(OPTABS_H). (cfgexpand.o): Depend on asan.h. * asan.c: Include expr.h and optabs.h. (asan_shadow_set): New variable. (asan_shadow_cst, asan_emit_stack_protection): New functions. (asan_init_shadow_ptr_types): Initialize also asan_shadow_set. * cfgexpand.c: Include asan.h. Define HOST_WIDE_INT heap vector. (partition_stack_vars): If i is large alignment and j small alignment or vice versa, break out of the loop instead of continue, and put the test earlier. If flag_asan, break out of the loop if for small alignment size is different. (struct stack_vars_data): New type. (expand_stack_vars): Add DATA argument. Change PRED type to function taking size_t argument instead of tree. Adjust pred calls. Fill DATA in and add needed padding in between variables if -fasan. (defer_stack_allocation): Defer everything for flag_asan. (stack_protect_decl_phase_1, stack_protect_decl_phase_2): Take size_t index into stack_vars array instead of the decl directly. (asan_decl_phase_3): New function. (expand_used_vars): Return var destruction sequence. Adjust expand_stack_vars calls, add another one for flag_asan. Call asan_emit_stack_protection if expand_stack_vars added anything to the vectors. (expand_gimple_basic_block): Add disable_tail_calls argument. (gimple_expand_cfg): Pass true to it if expand_used_vars returned non-NULL. Emit the sequence returned by expand_used_vars after return_label. * asan.h (asan_emit_stack_protection): New prototype. (asan_shadow_set): New decl. (ASAN_RED_ZONE_SIZE, ASAN_STACK_MAGIC_LEFT, ASAN_STACK_MAGIC_MIDDLE, ASAN_STACK_MAGIC_RIGHT, ASAN_STACK_FRAME_MAGIC): Define. (asan_protect_stack_decl): New inline. * toplev.c (process_options): Also disable -fasan on !FRAME_GROWS_DOWNWARDS targets. Jakub --- gcc/Makefile.in.jj 2012-10-12 23:30:31.635325772 +0200 +++ gcc/Makefile.in 2012-10-15 09:40:40.287706610 +0200 @@ -2204,7 +2204,7 @@ stor-layout.o : stor-layout.c $(CONFIG_H asan.o : asan.c asan.h $(CONFIG_H) pointer-set.h \ $(SYSTEM_H) $(TREE_H) $(GIMPLE_H) \ output.h $(DIAGNOSTIC_H) coretypes.h $(TREE_DUMP_H) $(FLAGS_H) \ - tree-pretty-print.h $(TARGET_H) + tree-pretty-print.h $(TARGET_H) $(EXPR_H) $(OPTABS_H) tree-ssa-tail-merge.o: tree-ssa-tail-merge.c \ $(SYSTEM_H) $(CONFIG_H) coretypes.h $(TM_H) $(BITMAP_H) \ $(FLAGS_H) $(TM_P_H) $(BASIC_BLOCK_H) \ @@ -3074,7 +3074,7 @@ cfgexpand.o : cfgexpand.c $(TREE_FLOW_H) $(DIAGNOSTIC_H) toplev.h $(DIAGNOSTIC_CORE_H) $(BASIC_BLOCK_H) $(FLAGS_H) debug.h $(PARAMS_H) \ value-prof.h $(TREE_INLINE_H) $(TARGET_H) $(SSAEXPAND_H) $(REGS_H) \ $(GIMPLE_PRETTY_PRINT_H) $(BITMAP_H) sbitmap.h \ - $(INSN_ATTR_H) $(CFGLOOP_H) + $(INSN_ATTR_H) $(CFGLOOP_H) asan.h cfgrtl.o : cfgrtl.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_ERROR_H) \ $(FLAGS_H) insn-config.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h \ $(FUNCTION_H) $(EXCEPT_H) $(TM_P_H) $(INSN_ATTR_H) \ --- gcc/toplev.c.jj 2012-10-12 23:30:31.717325313 +0200 +++ gcc/toplev.c 2012-10-15 09:40:03.540923935 +0200 @@ -1544,7 +1544,9 @@ process_options (void) } /* Address Sanitizer needs porting to each target architecture. */ - if (flag_asan && targetm.asan_shadow_offset == NULL) + if (flag_asan + && (targetm.asan_shadow_offset == NULL + || !FRAME_GROWS_DOWNWARD)) { warning (0, "-fasan not supported for this target"); flag_asan = 0; --- gcc/asan.c.jj 2012-10-12 23:31:55.423856689 +0200 +++ gcc/asan.c 2012-10-16 12:18:41.414029844 +0200 @@ -43,6 +43,8 @@ along with GCC; see the file COPYING3. #include "asan.h" #include "gimple-pretty-print.h" #include "target.h" +#include "expr.h" +#include "optabs.h" /* AddressSanitizer finds out-of-bounds and use-after-free bugs @@ -79,10 +81,195 @@ along with GCC; see the file COPYING3. to create redzones for stack and global object and poison them. */ +alias_set_type asan_shadow_set = -1; + /* Pointer types to 1 resp. 2 byte integers in shadow memory. A separate alias set is used for all shadow memory accesses. */ static GTY(()) tree shadow_ptr_types[2]; +/* Return a CONST_INT representing 4 subsequent shadow memory bytes. */ + +static rtx +asan_shadow_cst (unsigned char shadow_bytes[4]) +{ + int i; + unsigned HOST_WIDE_INT val = 0; + gcc_assert (WORDS_BIG_ENDIAN == BYTES_BIG_ENDIAN); + for (i = 0; i < 4; i++) + val |= (unsigned HOST_WIDE_INT) shadow_bytes[BYTES_BIG_ENDIAN ? 3 - i : i] + << (BITS_PER_UNIT * i); + return GEN_INT (trunc_int_for_mode (val, SImode)); +} + +/* Insert code to protect stack vars. The prologue sequence should be emitted + directly, epilogue sequence returned. BASE is the register holding the + stack base, against which OFFSETS array offsets are relative to, OFFSETS + array contains pairs of offsets in reverse order, always the end offset + of some gap that needs protection followed by starting offset, + and DECLS is an array of representative decls for each var partition. + LENGTH is the length of the OFFSETS array, DECLS array is LENGTH / 2 - 1 + elements long (OFFSETS include gap before the first variable as well + as gaps after each stack variable). */ + +rtx +asan_emit_stack_protection (rtx base, HOST_WIDE_INT *offsets, tree *decls, + int length) +{ + rtx shadow_base, shadow_mem, ret, mem; + unsigned char shadow_bytes[4]; + HOST_WIDE_INT base_offset = offsets[length - 1], offset, prev_offset; + HOST_WIDE_INT last_offset, last_size; + int l; + unsigned char cur_shadow_byte = ASAN_STACK_MAGIC_LEFT; + static pretty_printer pp; + static bool pp_initialized; + const char *buf; + size_t len; + tree str_cst; + + /* First of all, prepare the description string. */ + if (!pp_initialized) + { + pp_construct (&pp, /* prefix */NULL, /* line-width */0); + pp_initialized = true; + } + pp_clear_output_area (&pp); + if (DECL_NAME (current_function_decl)) + pp_base_tree_identifier (&pp, DECL_NAME (current_function_decl)); + else + pp_string (&pp, ""); + pp_space (&pp); + pp_decimal_int (&pp, length / 2 - 1); + pp_space (&pp); + for (l = length - 2; l; l -= 2) + { + tree decl = decls[l / 2 - 1]; + pp_wide_integer (&pp, offsets[l] - base_offset); + pp_space (&pp); + pp_wide_integer (&pp, offsets[l - 1] - offsets[l]); + pp_space (&pp); + if (DECL_P (decl) && DECL_NAME (decl)) + { + pp_decimal_int (&pp, IDENTIFIER_LENGTH (DECL_NAME (decl))); + pp_space (&pp); + pp_base_tree_identifier (&pp, DECL_NAME (decl)); + } + else + pp_string (&pp, "9 "); + pp_space (&pp); + } + buf = pp_base_formatted_text (&pp); + len = strlen (buf); + str_cst = build_string (len + 1, buf); + TREE_TYPE (str_cst) + = build_array_type (char_type_node, build_index_type (size_int (len))); + TREE_READONLY (str_cst) = 1; + TREE_STATIC (str_cst) = 1; + str_cst = build1 (ADDR_EXPR, build_pointer_type (char_type_node), str_cst); + + /* Emit the prologue sequence. */ + base = expand_binop (Pmode, add_optab, base, GEN_INT (base_offset), + NULL_RTX, 1, OPTAB_DIRECT); + mem = gen_rtx_MEM (ptr_mode, base); + emit_move_insn (mem, GEN_INT (ASAN_STACK_FRAME_MAGIC)); + mem = adjust_address (mem, VOIDmode, GET_MODE_SIZE (ptr_mode)); + emit_move_insn (mem, expand_normal (str_cst)); + shadow_base = expand_binop (Pmode, lshr_optab, base, + GEN_INT (ASAN_SHADOW_SHIFT), + NULL_RTX, 1, OPTAB_DIRECT); + shadow_base = expand_binop (Pmode, add_optab, shadow_base, + GEN_INT (targetm.asan_shadow_offset ()), + NULL_RTX, 1, OPTAB_DIRECT); + gcc_assert (asan_shadow_set != -1 + && (ASAN_RED_ZONE_SIZE >> ASAN_SHADOW_SHIFT) == 4); + shadow_mem = gen_rtx_MEM (SImode, shadow_base); + set_mem_alias_set (shadow_mem, asan_shadow_set); + prev_offset = base_offset; + for (l = length; l; l -= 2) + { + if (l == 2) + cur_shadow_byte = ASAN_STACK_MAGIC_RIGHT; + offset = offsets[l - 1]; + if ((offset - base_offset) & (ASAN_RED_ZONE_SIZE - 1)) + { + int i; + HOST_WIDE_INT aoff + = base_offset + ((offset - base_offset) + & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1)); + shadow_mem = adjust_address (shadow_mem, VOIDmode, + (aoff - prev_offset) + >> ASAN_SHADOW_SHIFT); + prev_offset = aoff; + for (i = 0; i < 4; i++, aoff += (1 << ASAN_SHADOW_SHIFT)) + if (aoff < offset) + { + if (aoff < offset - (1 << ASAN_SHADOW_SHIFT) + 1) + shadow_bytes[i] = 0; + else + shadow_bytes[i] = offset - aoff; + } + else + shadow_bytes[i] = ASAN_STACK_MAGIC_PARTIAL; + emit_move_insn (shadow_mem, asan_shadow_cst (shadow_bytes)); + offset = aoff; + } + while (offset <= offsets[l - 2] - ASAN_RED_ZONE_SIZE) + { + shadow_mem = adjust_address (shadow_mem, VOIDmode, + (offset - prev_offset) + >> ASAN_SHADOW_SHIFT); + prev_offset = offset; + memset (shadow_bytes, cur_shadow_byte, 4); + emit_move_insn (shadow_mem, asan_shadow_cst (shadow_bytes)); + offset += ASAN_RED_ZONE_SIZE; + } + cur_shadow_byte = ASAN_STACK_MAGIC_MIDDLE; + } + do_pending_stack_adjust (); + + /* Construct epilogue sequence. */ + start_sequence (); + + shadow_mem = gen_rtx_MEM (BLKmode, shadow_base); + set_mem_alias_set (shadow_mem, asan_shadow_set); + prev_offset = base_offset; + last_offset = base_offset; + last_size = 0; + for (l = length; l; l -= 2) + { + offset = base_offset + ((offsets[l - 1] - base_offset) + & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1)); + if (last_offset + last_size != offset) + { + shadow_mem = adjust_address (shadow_mem, VOIDmode, + (last_offset - prev_offset) + >> ASAN_SHADOW_SHIFT); + prev_offset = last_offset; + clear_storage (shadow_mem, GEN_INT (last_size >> ASAN_SHADOW_SHIFT), + BLOCK_OP_NORMAL); + last_offset = offset; + last_size = 0; + } + last_size += base_offset + ((offsets[l - 2] - base_offset) + & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1)) + - offset; + } + if (last_size) + { + shadow_mem = adjust_address (shadow_mem, VOIDmode, + (last_offset - prev_offset) + >> ASAN_SHADOW_SHIFT); + clear_storage (shadow_mem, GEN_INT (last_size >> ASAN_SHADOW_SHIFT), + BLOCK_OP_NORMAL); + } + + do_pending_stack_adjust (); + + ret = get_insns (); + end_sequence (); + return ret; +} + /* Construct a function tree for __asan_report_{load,store}{1,2,4,8,16}. IS_STORE is either 1 (for a store) or 0 (for a load). SIZE_IN_BYTES is one of 1, 2, 4, 8, 16. */ @@ -401,12 +588,12 @@ asan_finish_file (void) static void asan_init_shadow_ptr_types (void) { - alias_set_type set = new_alias_set (); + asan_shadow_set = new_alias_set (); shadow_ptr_types[0] = build_distinct_type_copy (unsigned_char_type_node); - TYPE_ALIAS_SET (shadow_ptr_types[0]) = set; + TYPE_ALIAS_SET (shadow_ptr_types[0]) = asan_shadow_set; shadow_ptr_types[0] = build_pointer_type (shadow_ptr_types[0]); shadow_ptr_types[1] = build_distinct_type_copy (short_unsigned_type_node); - TYPE_ALIAS_SET (shadow_ptr_types[1]) = set; + TYPE_ALIAS_SET (shadow_ptr_types[1]) = asan_shadow_set; shadow_ptr_types[1] = build_pointer_type (shadow_ptr_types[1]); } --- gcc/asan.h.jj 2012-10-12 23:30:31.689325469 +0200 +++ gcc/asan.h 2012-10-17 11:01:17.720950926 +0200 @@ -21,10 +21,39 @@ along with GCC; see the file COPYING3. #ifndef TREE_ASAN #define TREE_ASAN -extern void asan_finish_file(void); +extern void asan_finish_file (void); +extern rtx asan_emit_stack_protection (rtx, HOST_WIDE_INT *, tree *, int); + +/* Alias set for accessing the shadow memory. */ +extern alias_set_type asan_shadow_set; /* Shadow memory is found at (address >> ASAN_SHADOW_SHIFT) + targetm.asan_shadow_offset (). */ #define ASAN_SHADOW_SHIFT 3 +/* Red zone size, stack and global variables are padded by ASAN_RED_ZONE_SIZE + up to 2 * ASAN_RED_ZONE_SIZE - 1 bytes. */ +#define ASAN_RED_ZONE_SIZE 32 + +/* Shadow memory values for stack protection. Left is below protected vars, + the first pointer in stack corresponding to that offset contains + ASAN_STACK_FRAME_MAGIC word, the second pointer to a string describing + the frame. Middle is for padding in between variables, right is + above the last protected variable and partial immediately after variables + up to ASAN_RED_ZONE_SIZE alignment. */ +#define ASAN_STACK_MAGIC_LEFT 0xf1 +#define ASAN_STACK_MAGIC_MIDDLE 0xf2 +#define ASAN_STACK_MAGIC_RIGHT 0xf3 +#define ASAN_STACK_MAGIC_PARTIAL 0xf4 + +#define ASAN_STACK_FRAME_MAGIC 0x41b58ab3 + +/* Return true if DECL should be guarded on the stack. */ + +static inline bool +asan_protect_stack_decl (tree decl) +{ + return DECL_P (decl) && !DECL_ARTIFICIAL (decl); +} + #endif /* TREE_ASAN */ --- gcc/cfgexpand.c.jj 2012-10-12 23:30:31.772325005 +0200 +++ gcc/cfgexpand.c 2012-10-17 11:19:59.542778069 +0200 @@ -47,6 +47,7 @@ along with GCC; see the file COPYING3. #include "cfgloop.h" #include "regs.h" /* For reg_renumber. */ #include "insn-attr.h" /* For INSN_SCHEDULING. */ +#include "asan.h" /* This variable holds information helping the rewriting of SSA trees into RTL. */ @@ -734,6 +735,7 @@ partition_stack_vars (void) { size_t i = stack_vars_sorted[si]; unsigned int ialign = stack_vars[i].alignb; + HOST_WIDE_INT isize = stack_vars[i].size; /* Ignore objects that aren't partition representatives. If we see a var that is not a partition representative, it must @@ -745,19 +747,28 @@ partition_stack_vars (void) { size_t j = stack_vars_sorted[sj]; unsigned int jalign = stack_vars[j].alignb; + HOST_WIDE_INT jsize = stack_vars[j].size; /* Ignore objects that aren't partition representatives. */ if (stack_vars[j].representative != j) continue; - /* Ignore conflicting objects. */ - if (stack_var_conflict_p (i, j)) - continue; - /* Do not mix objects of "small" (supported) alignment and "large" (unsupported) alignment. */ if ((ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT) != (jalign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)) + break; + + /* For Address Sanitizer do not mix objects with different + sizes, as the shorter vars wouldn't be adequately protected. + Don't do that for "large" (unsupported) alignment objects, + those aren't protected anyway. */ + if (flag_asan && isize != jsize + && ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT) + break; + + /* Ignore conflicting objects. */ + if (stack_var_conflict_p (i, j)) continue; /* UNION the objects, placing J at OFFSET. */ @@ -835,12 +846,26 @@ expand_one_stack_var_at (tree decl, rtx set_rtl (decl, x); } +DEF_VEC_I(HOST_WIDE_INT); +DEF_VEC_ALLOC_I(HOST_WIDE_INT,heap); + +struct stack_vars_data +{ + /* Vector of offset pairs, always end of some padding followed + by start of the padding that needs Address Sanitizer protection. + The vector is in reversed, highest offset pairs come first. */ + VEC(HOST_WIDE_INT, heap) *asan_vec; + + /* Vector of partition representative decls in between the paddings. */ + VEC(tree, heap) *asan_decl_vec; +}; + /* A subroutine of expand_used_vars. Give each partition representative a unique location within the stack frame. Update each partition member with that location. */ static void -expand_stack_vars (bool (*pred) (tree)) +expand_stack_vars (bool (*pred) (size_t), struct stack_vars_data *data) { size_t si, i, j, n = stack_vars_num; HOST_WIDE_INT large_size = 0, large_alloc = 0; @@ -911,13 +936,45 @@ expand_stack_vars (bool (*pred) (tree)) /* Check the predicate to see whether this variable should be allocated in this pass. */ - if (pred && !pred (decl)) + if (pred && !pred (i)) continue; alignb = stack_vars[i].alignb; if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT) { - offset = alloc_stack_frame_space (stack_vars[i].size, alignb); + if (flag_asan && pred) + { + HOST_WIDE_INT prev_offset = frame_offset; + tree repr_decl = NULL_TREE; + + offset + = alloc_stack_frame_space (stack_vars[i].size + + ASAN_RED_ZONE_SIZE, + MAX (alignb, ASAN_RED_ZONE_SIZE)); + VEC_safe_push (HOST_WIDE_INT, heap, data->asan_vec, + prev_offset); + VEC_safe_push (HOST_WIDE_INT, heap, data->asan_vec, + offset + stack_vars[i].size); + /* Find best representative of the partition. + Prefer those with DECL_NAME, even better + satisfying asan_protect_stack_decl predicate. */ + for (j = i; j != EOC; j = stack_vars[j].next) + if (asan_protect_stack_decl (stack_vars[j].decl) + && DECL_NAME (stack_vars[j].decl)) + { + repr_decl = stack_vars[j].decl; + break; + } + else if (repr_decl == NULL_TREE + && DECL_P (stack_vars[j].decl) + && DECL_NAME (stack_vars[j].decl)) + repr_decl = stack_vars[j].decl; + if (repr_decl == NULL_TREE) + repr_decl = stack_vars[i].decl; + VEC_safe_push (tree, heap, data->asan_decl_vec, repr_decl); + } + else + offset = alloc_stack_frame_space (stack_vars[i].size, alignb); base = virtual_stack_vars_rtx; base_align = crtl->max_used_stack_slot_alignment; } @@ -1055,8 +1112,9 @@ static bool defer_stack_allocation (tree var, bool toplevel) { /* If stack protection is enabled, *all* stack variables must be deferred, - so that we can re-order the strings to the top of the frame. */ - if (flag_stack_protect) + so that we can re-order the strings to the top of the frame. + Similarly for Address Sanitizer. */ + if (flag_stack_protect || flag_asan) return true; /* We handle "large" alignment via dynamic allocation. We want to handle @@ -1327,15 +1385,31 @@ stack_protect_decl_phase (tree decl) as callbacks for expand_stack_vars. */ static bool -stack_protect_decl_phase_1 (tree decl) +stack_protect_decl_phase_1 (size_t i) { - return stack_protect_decl_phase (decl) == 1; + return stack_protect_decl_phase (stack_vars[i].decl) == 1; } static bool -stack_protect_decl_phase_2 (tree decl) +stack_protect_decl_phase_2 (size_t i) { - return stack_protect_decl_phase (decl) == 2; + return stack_protect_decl_phase (stack_vars[i].decl) == 2; +} + +/* And helper function that checks for asan phase (with stack protector + it is phase 3). This is used as callback for expand_stack_vars. + Returns true if any of the vars in the partition need to be protected. */ + +static bool +asan_decl_phase_3 (size_t i) +{ + while (i != EOC) + { + if (asan_protect_stack_decl (stack_vars[i].decl)) + return true; + i = stack_vars[i].next; + } + return false; } /* Ensure that variables in different stack protection phases conflict @@ -1446,11 +1520,12 @@ estimated_stack_frame_size (struct cgrap /* Expand all variables used in the function. */ -static void +static rtx expand_used_vars (void) { tree var, outer_block = DECL_INITIAL (current_function_decl); VEC(tree,heap) *maybe_local_decls = NULL; + rtx var_end_seq = NULL_RTX; struct pointer_map_t *ssa_name_decls; unsigned i; unsigned len; @@ -1601,6 +1676,11 @@ expand_used_vars (void) /* Assign rtl to each variable based on these partitions. */ if (stack_vars_num > 0) { + struct stack_vars_data data; + + data.asan_vec = NULL; + data.asan_decl_vec = NULL; + /* Reorder decls to be protected by iterating over the variables array multiple times, and allocating out of each phase in turn. */ /* ??? We could probably integrate this into the qsort we did @@ -1609,14 +1689,41 @@ expand_used_vars (void) if (has_protected_decls) { /* Phase 1 contains only character arrays. */ - expand_stack_vars (stack_protect_decl_phase_1); + expand_stack_vars (stack_protect_decl_phase_1, &data); /* Phase 2 contains other kinds of arrays. */ if (flag_stack_protect == 2) - expand_stack_vars (stack_protect_decl_phase_2); + expand_stack_vars (stack_protect_decl_phase_2, &data); + } + + if (flag_asan) + /* Phase 3, any partitions that need asan protection + in addition to phase 1 and 2. */ + expand_stack_vars (asan_decl_phase_3, &data); + + if (!VEC_empty (HOST_WIDE_INT, data.asan_vec)) + { + HOST_WIDE_INT prev_offset = frame_offset; + HOST_WIDE_INT offset + = alloc_stack_frame_space (ASAN_RED_ZONE_SIZE, + ASAN_RED_ZONE_SIZE); + VEC_safe_push (HOST_WIDE_INT, heap, data.asan_vec, prev_offset); + VEC_safe_push (HOST_WIDE_INT, heap, data.asan_vec, offset); + + var_end_seq + = asan_emit_stack_protection (virtual_stack_vars_rtx, + VEC_address (HOST_WIDE_INT, + data.asan_vec), + VEC_address (tree, + data.asan_decl_vec), + VEC_length (HOST_WIDE_INT, + data.asan_vec)); } - expand_stack_vars (NULL); + expand_stack_vars (NULL, &data); + + VEC_free (HOST_WIDE_INT, heap, data.asan_vec); + VEC_free (tree, heap, data.asan_decl_vec); } fini_vars_expansion (); @@ -1643,6 +1750,8 @@ expand_used_vars (void) frame_offset += align - 1; frame_offset &= -align; } + + return var_end_seq; } @@ -3639,7 +3748,7 @@ expand_debug_locations (void) /* Expand basic block BB from GIMPLE trees to RTL. */ static basic_block -expand_gimple_basic_block (basic_block bb) +expand_gimple_basic_block (basic_block bb, bool disable_tail_calls) { gimple_stmt_iterator gsi; gimple_seq stmts; @@ -3927,6 +4036,11 @@ expand_gimple_basic_block (basic_block b } else { + if (is_gimple_call (stmt) + && gimple_call_tail_p (stmt) + && disable_tail_calls) + gimple_call_set_tail (stmt, false); + if (is_gimple_call (stmt) && gimple_call_tail_p (stmt)) { bool can_fallthru; @@ -4286,7 +4400,7 @@ gimple_expand_cfg (void) sbitmap blocks; edge_iterator ei; edge e; - rtx var_seq; + rtx var_seq, var_ret_seq; unsigned i; timevar_push (TV_OUT_OF_SSA); @@ -4346,7 +4460,7 @@ gimple_expand_cfg (void) timevar_push (TV_VAR_EXPAND); start_sequence (); - expand_used_vars (); + var_ret_seq = expand_used_vars (); var_seq = get_insns (); end_sequence (); @@ -4472,7 +4586,7 @@ gimple_expand_cfg (void) lab_rtx_for_bb = pointer_map_create (); FOR_BB_BETWEEN (bb, init_block->next_bb, EXIT_BLOCK_PTR, next_bb) - bb = expand_gimple_basic_block (bb); + bb = expand_gimple_basic_block (bb, var_ret_seq != NULL_RTX); if (MAY_HAVE_DEBUG_INSNS) expand_debug_locations (); @@ -4500,6 +4614,9 @@ gimple_expand_cfg (void) construct_exit_block (); insn_locations_finalize (); + if (var_ret_seq) + emit_insn_after (var_ret_seq, return_label); + /* Zap the tree EH table. */ set_eh_throw_stmt_table (cfun, NULL);