public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
@ 2021-07-07 17:38 Qing Zhao
  2021-07-08 13:29 ` Martin Jambor
  2021-07-12 17:56 ` Kees Cook
  0 siblings, 2 replies; 25+ messages in thread
From: Qing Zhao @ 2021-07-07 17:38 UTC (permalink / raw)
  To: Richard Biener, Richard Sandiford, kees cook; +Cc: gcc-patches Qing Zhao via

[-- Attachment #1: Type: text/plain, Size: 8449 bytes --]

Hi, 

This is the 4th version of the patch for the new security feature for GCC.

I have tested it with bootstrap on both x86 and aarch64, regression testing on both x86 and aarch64.
Also compile and run CPU2017, without any issue.

Please take a look and let me know your comments and suggestions.

thanks.

Qing

******Compare with the 3rd version, the following are the major changes:

1. change -ftrivial-auto-var-init as an optimization option in order to refer it with "opt_for_fn";
2. delete option -fauto-var-init-approach;
3. update -ftrivial-auto-var-init doc by adding padding initialization and some clarification for pattern initializations.
4. delete the changes in the routine "store_constructor" and "gimplify_init_constructor" about padding initializations.
5. for VLA variable, also generate calls to .DEFERRED_INIT instead of calls to "meset", consistent with non-VLA.  
   delete all changes on "memset" calls for VLA initialization. 
6. change the definition of .DEFERRED_INIT:

From:
temp = .DEFERRED_INIT (temp, init_type)

To:

temp = .DEFERRED_INIT (SIZE_of_temp, init_type, IS_VLA) 

the first argument of .DEFERRED_INIT will be the size of the "temp", and the 
additional 3rd argument is a bool on whether it's a VLA or not.
 
7. in tree-cfg.c, update verifications of call to .DEFERRED_INIT according to 6.
8. "expand_DEFERRED_INIT" will do rtl expansion for both VLA and nonVLA;
   pattern initialization will use byte-repeatable pattern "0xFE" for initialization;
   for VLA and varibles that in stack, expand the call to a memset of zeroes or "FE"s;
   for variables that are in registers, expand the call by using "expand_assignment" 
    in order to generate better code for Integer, pointer and float type variables.
9. delete routines "build_pattern_cst_for_auto_init"; type_has_padding";
		    
10. update testing cases accordingly.

******Please see the version 3 at:

https://gcc.gnu.org/pipermail/gcc-patches/2021-May/570208.html

******Changelog is:

gcc/ChangeLog:

2021-07-06  qing zhao  <qing.zhao@oracle.com>

	* builtins.c (expand_builtin_memset): Make external visible. 
	* builtins.h (expand_builtin_memset): Declare extern.
	* common.opt (ftrivial-auto-var-init=): New option.
	* doc/extend.texi: Document the uninitialized attribute.
	* doc/invoke.texi: Document -ftrivial-auto-var-init.
	* flag-types.h (enum auto_init_type): New enumerated type
	auto_init_type.
	* gimplify.c (gimple_add_init_for_auto_var): New function.
	(maybe_with_size_expr): Forword declaration.
	(build_deferred_init): New function.
	(gimplify_decl_expr): Add initialization to automatic variables per
	users' requests.
	* internal-fn.c (INIT_PATTERN_VALUE): New macro.
	(expand_DEFERRED_INIT): New function.
	* internal-fn.def (DEFERRED_INIT): New internal function.
	* tree-cfg.c (verify_gimple_call): Verify calls to .DEFERRED_INIT.
	* tree-sra.c (generate_subtree_deferred_init): New function.
	(sra_modify_deferred_init): Likewise;
	(sra_modify_function_body): Handle calls to DEFERRED_INIT specially.
	* tree-ssa-structalias.c (find_func_aliases_for_call):
	* tree-ssa-uninit.c (warn_uninit): Handle calls to DEFERRED_INIT
	specially.
	(check_defs): Likewise.
	(warn_uninitialized_vars): Likewise.
	* tree-ssa.c (ssa_undefined_value_p): Likewise.

gcc/c-family/ChangeLog:

2021-07-06  qing zhao  <qing.zhao@oracle.com>

	* c-attribs.c (handle_uninitialized_attribute): New function.
       (c_common_attribute_table): Add "uninitialized" attribute.

gcc/testsuite/ChangeLog:

2021-07-06  qing zhao  <qing.zhao@oracle.com>

	* c-c++-common/auto-init-1.c: New test.
	* c-c++-common/auto-init-10.c: New test.
	* c-c++-common/auto-init-11.c: New test.
	* c-c++-common/auto-init-12.c: New test.
	* c-c++-common/auto-init-13.c: New test.
	* c-c++-common/auto-init-14.c: New test.
	* c-c++-common/auto-init-15.c: New test.
	* c-c++-common/auto-init-16.c: New test.
	* c-c++-common/auto-init-2.c: New test.
	* c-c++-common/auto-init-3.c: New test.
	* c-c++-common/auto-init-4.c: New test.
	* c-c++-common/auto-init-5.c: New test.
	* c-c++-common/auto-init-6.c: New test.
	* c-c++-common/auto-init-7.c: New test.
	* c-c++-common/auto-init-8.c: New test.
	* c-c++-common/auto-init-9.c: New test.
	* c-c++-common/auto-init-esra.c: New test.
	* g++.dg/auto-init-uninit-pred-1_a.C: New test.
	* g++.dg/auto-init-uninit-pred-1_b.C: New test.
	* g++.dg/auto-init-uninit-pred-2_a.C: New test.
	* g++.dg/auto-init-uninit-pred-2_b.C: New test.
	* g++.dg/auto-init-uninit-pred-3_a.C: New test.
	* g++.dg/auto-init-uninit-pred-3_b.C: New test.
	* g++.dg/auto-init-uninit-pred-4.C: New test.
	* g++.dg/auto-init-uninit-pred-loop-1_a.cc: New test.
	* g++.dg/auto-init-uninit-pred-loop-1_b.cc: New test.
	* g++.dg/auto-init-uninit-pred-loop-1_c.cc: New test.
	* g++.dg/auto-init-uninit-pred-loop_1.cc: New test.
	* gcc.dg/auto-init-uninit-1.c: New test.
	* gcc.dg/auto-init-uninit-11.c: New test.
	* gcc.dg/auto-init-uninit-12.c: New test.
	* gcc.dg/auto-init-uninit-13.c: New test.
	* gcc.dg/auto-init-uninit-14.c: New test.
	* gcc.dg/auto-init-uninit-15.c: New test.
	* gcc.dg/auto-init-uninit-16.c: New test.
	* gcc.dg/auto-init-uninit-17.c: New test.
	* gcc.dg/auto-init-uninit-18.c: New test.
	* gcc.dg/auto-init-uninit-19.c: New test.
	* gcc.dg/auto-init-uninit-2.c: New test.
	* gcc.dg/auto-init-uninit-20.c: New test.
	* gcc.dg/auto-init-uninit-21.c: New test.
	* gcc.dg/auto-init-uninit-22.c: New test.
	* gcc.dg/auto-init-uninit-23.c: New test.
	* gcc.dg/auto-init-uninit-24.c: New test.
	* gcc.dg/auto-init-uninit-25.c: New test.
	* gcc.dg/auto-init-uninit-26.c: New test.
	* gcc.dg/auto-init-uninit-3.c: New test.
	* gcc.dg/auto-init-uninit-34.c: New test.
	* gcc.dg/auto-init-uninit-36.c: New test.
	* gcc.dg/auto-init-uninit-37.c: New test.
	* gcc.dg/auto-init-uninit-4.c: New test.
	* gcc.dg/auto-init-uninit-5.c: New test.
	* gcc.dg/auto-init-uninit-6.c: New test.
	* gcc.dg/auto-init-uninit-8.c: New test.
	* gcc.dg/auto-init-uninit-9.c: New test.
	* gcc.dg/auto-init-uninit-A.c: New test.
	* gcc.dg/auto-init-uninit-B.c: New test.
	* gcc.dg/auto-init-uninit-C.c: New test.
	* gcc.dg/auto-init-uninit-H.c: New test.
	* gcc.dg/auto-init-uninit-I.c: New test.
	* gcc.target/aarch64/auto-init-1.c: New test.
	* gcc.target/aarch64/auto-init-10.c: New test.
	* gcc.target/aarch64/auto-init-11.c: New test.
	* gcc.target/aarch64/auto-init-12.c: New test.
	* gcc.target/aarch64/auto-init-13.c: New test.
	* gcc.target/aarch64/auto-init-14.c: New test.
	* gcc.target/aarch64/auto-init-15.c: New test.
	* gcc.target/aarch64/auto-init-16.c: New test.
	* gcc.target/aarch64/auto-init-17.c: New test.
	* gcc.target/aarch64/auto-init-18.c: New test.
	* gcc.target/aarch64/auto-init-19.c: New test.
	* gcc.target/aarch64/auto-init-2.c: New test.
	* gcc.target/aarch64/auto-init-20.c: New test.
	* gcc.target/aarch64/auto-init-3.c: New test.
	* gcc.target/aarch64/auto-init-4.c: New test.
	* gcc.target/aarch64/auto-init-5.c: New test.
	* gcc.target/aarch64/auto-init-6.c: New test.
	* gcc.target/aarch64/auto-init-7.c: New test.
	* gcc.target/aarch64/auto-init-8.c: New test.
	* gcc.target/aarch64/auto-init-9.c: New test.
	* gcc.target/i386/auto-init-1.c: New test.
	* gcc.target/i386/auto-init-10.c: New test.
	* gcc.target/i386/auto-init-11.c: New test.
	* gcc.target/i386/auto-init-12.c: New test.
	* gcc.target/i386/auto-init-13.c: New test.
	* gcc.target/i386/auto-init-14.c: New test.
	* gcc.target/i386/auto-init-15.c: New test.
	* gcc.target/i386/auto-init-16.c: New test.
	* gcc.target/i386/auto-init-17.c: New test.
	* gcc.target/i386/auto-init-18.c: New test.
	* gcc.target/i386/auto-init-19.c: New test.
	* gcc.target/i386/auto-init-2.c: New test.
	* gcc.target/i386/auto-init-20.c: New test.
	* gcc.target/i386/auto-init-21.c: New test.
	* gcc.target/i386/auto-init-22.c: New test.
	* gcc.target/i386/auto-init-23.c: New test.
	* gcc.target/i386/auto-init-24.c: New test.
	* gcc.target/i386/auto-init-3.c: New test.
	* gcc.target/i386/auto-init-4.c: New test.
	* gcc.target/i386/auto-init-5.c: New test.
	* gcc.target/i386/auto-init-6.c: New test.
	* gcc.target/i386/auto-init-7.c: New test.
	* gcc.target/i386/auto-init-8.c: New test.
	* gcc.target/i386/auto-init-9.c: New test.

******The complete 4th version of the patch is:

[-- Attachment #2: 0001-4th-version-master.patch --]
[-- Type: application/octet-stream, Size: 147189 bytes --]

From f45baeba40c1fcea0e3aec86edd5169c8765cd37 Mon Sep 17 00:00:00 2001
From: Qing Zhao <qing.zhao@oracle.com>
Date: Sat, 3 Jul 2021 00:11:10 +0000
Subject: [PATCH] 4th version master

---
 gcc/builtins.c                                |   3 +-
 gcc/builtins.h                                |   1 +
 gcc/c-family/c-attribs.c                      |  19 ++
 gcc/common.opt                                |  16 ++
 gcc/doc/extend.texi                           |  16 ++
 gcc/doc/invoke.texi                           |  41 ++-
 gcc/flag-types.h                              |   7 +
 gcc/gimplify.c                                |  67 +++++
 gcc/internal-fn.c                             | 108 ++++++++
 gcc/internal-fn.def                           |   5 +
 gcc/testsuite/c-c++-common/auto-init-1.c      |  39 +++
 gcc/testsuite/c-c++-common/auto-init-10.c     |  17 ++
 gcc/testsuite/c-c++-common/auto-init-11.c     |  14 ++
 gcc/testsuite/c-c++-common/auto-init-12.c     |  14 ++
 gcc/testsuite/c-c++-common/auto-init-13.c     |  23 ++
 gcc/testsuite/c-c++-common/auto-init-14.c     |  23 ++
 gcc/testsuite/c-c++-common/auto-init-15.c     |  13 +
 gcc/testsuite/c-c++-common/auto-init-16.c     |  13 +
 gcc/testsuite/c-c++-common/auto-init-2.c      |  39 +++
 gcc/testsuite/c-c++-common/auto-init-3.c      |  19 ++
 gcc/testsuite/c-c++-common/auto-init-4.c      |  19 ++
 gcc/testsuite/c-c++-common/auto-init-5.c      |  21 ++
 gcc/testsuite/c-c++-common/auto-init-6.c      |  21 ++
 gcc/testsuite/c-c++-common/auto-init-7.c      |  35 +++
 gcc/testsuite/c-c++-common/auto-init-8.c      |  35 +++
 gcc/testsuite/c-c++-common/auto-init-9.c      |  17 ++
 gcc/testsuite/c-c++-common/auto-init-esra.c   |  35 +++
 .../g++.dg/auto-init-uninit-pred-1_a.C        |  63 +++++
 .../g++.dg/auto-init-uninit-pred-1_b.C        |  63 +++++
 .../g++.dg/auto-init-uninit-pred-2_a.C        |  62 +++++
 .../g++.dg/auto-init-uninit-pred-2_b.C        |  62 +++++
 .../g++.dg/auto-init-uninit-pred-3_a.C        |  77 ++++++
 .../g++.dg/auto-init-uninit-pred-3_b.C        |  87 +++++++
 .../g++.dg/auto-init-uninit-pred-4.C          |  16 ++
 .../g++.dg/auto-init-uninit-pred-loop-1_a.cc  |  21 ++
 .../g++.dg/auto-init-uninit-pred-loop-1_b.cc  |  21 ++
 .../g++.dg/auto-init-uninit-pred-loop-1_c.cc  |  23 ++
 .../g++.dg/auto-init-uninit-pred-loop_1.cc    |  21 ++
 gcc/testsuite/gcc.dg/auto-init-uninit-1.c     |  30 +++
 gcc/testsuite/gcc.dg/auto-init-uninit-11.c    |  42 ++++
 gcc/testsuite/gcc.dg/auto-init-uninit-12.c    |  12 +
 gcc/testsuite/gcc.dg/auto-init-uninit-13.c    |  10 +
 gcc/testsuite/gcc.dg/auto-init-uninit-14.c    |  20 ++
 gcc/testsuite/gcc.dg/auto-init-uninit-15.c    |  26 ++
 gcc/testsuite/gcc.dg/auto-init-uninit-16.c    |  23 ++
 gcc/testsuite/gcc.dg/auto-init-uninit-17.c    |  15 ++
 gcc/testsuite/gcc.dg/auto-init-uninit-18.c    |  24 ++
 gcc/testsuite/gcc.dg/auto-init-uninit-19.c    |  26 ++
 gcc/testsuite/gcc.dg/auto-init-uninit-2.c     |  52 ++++
 gcc/testsuite/gcc.dg/auto-init-uninit-20.c    |  18 ++
 gcc/testsuite/gcc.dg/auto-init-uninit-21.c    |  33 +++
 gcc/testsuite/gcc.dg/auto-init-uninit-22.c    |  69 +++++
 gcc/testsuite/gcc.dg/auto-init-uninit-23.c    |  27 ++
 gcc/testsuite/gcc.dg/auto-init-uninit-24.c    |  10 +
 gcc/testsuite/gcc.dg/auto-init-uninit-25.c    |  23 ++
 gcc/testsuite/gcc.dg/auto-init-uninit-26.c    |  23 ++
 gcc/testsuite/gcc.dg/auto-init-uninit-3.c     |  33 +++
 gcc/testsuite/gcc.dg/auto-init-uninit-34.c    |  58 +++++
 gcc/testsuite/gcc.dg/auto-init-uninit-36.c    | 238 ++++++++++++++++++
 gcc/testsuite/gcc.dg/auto-init-uninit-37.c    | 154 ++++++++++++
 gcc/testsuite/gcc.dg/auto-init-uninit-4.c     |  52 ++++
 gcc/testsuite/gcc.dg/auto-init-uninit-5.c     |  39 +++
 gcc/testsuite/gcc.dg/auto-init-uninit-6.c     |  47 ++++
 gcc/testsuite/gcc.dg/auto-init-uninit-8.c     |  32 +++
 gcc/testsuite/gcc.dg/auto-init-uninit-9.c     |  42 ++++
 gcc/testsuite/gcc.dg/auto-init-uninit-A.c     | 117 +++++++++
 gcc/testsuite/gcc.dg/auto-init-uninit-B.c     |  15 ++
 gcc/testsuite/gcc.dg/auto-init-uninit-C.c     |  21 ++
 gcc/testsuite/gcc.dg/auto-init-uninit-H.c     |  33 +++
 gcc/testsuite/gcc.dg/auto-init-uninit-I.c     |   8 +
 .../gcc.target/aarch64/auto-init-1.c          |  32 +++
 .../gcc.target/aarch64/auto-init-10.c         |  18 ++
 .../gcc.target/aarch64/auto-init-11.c         |  27 ++
 .../gcc.target/aarch64/auto-init-12.c         |  27 ++
 .../gcc.target/aarch64/auto-init-13.c         |  22 ++
 .../gcc.target/aarch64/auto-init-14.c         |  20 ++
 .../gcc.target/aarch64/auto-init-15.c         |  20 ++
 .../gcc.target/aarch64/auto-init-16.c         |  22 ++
 .../gcc.target/aarch64/auto-init-17.c         |  21 ++
 .../gcc.target/aarch64/auto-init-18.c         |  22 ++
 .../gcc.target/aarch64/auto-init-19.c         |  27 ++
 .../gcc.target/aarch64/auto-init-2.c          |  35 +++
 .../gcc.target/aarch64/auto-init-20.c         |  27 ++
 .../gcc.target/aarch64/auto-init-3.c          |  17 ++
 .../gcc.target/aarch64/auto-init-4.c          |  19 ++
 .../gcc.target/aarch64/auto-init-5.c          |  19 ++
 .../gcc.target/aarch64/auto-init-6.c          |  18 ++
 .../gcc.target/aarch64/auto-init-7.c          |  32 +++
 .../gcc.target/aarch64/auto-init-8.c          |  32 +++
 .../gcc.target/aarch64/auto-init-9.c          |  17 ++
 gcc/testsuite/gcc.target/i386/auto-init-1.c   |  32 +++
 gcc/testsuite/gcc.target/i386/auto-init-10.c  |  19 ++
 gcc/testsuite/gcc.target/i386/auto-init-11.c  |  30 +++
 gcc/testsuite/gcc.target/i386/auto-init-12.c  |  28 +++
 gcc/testsuite/gcc.target/i386/auto-init-13.c  |  22 ++
 gcc/testsuite/gcc.target/i386/auto-init-14.c  |  22 ++
 gcc/testsuite/gcc.target/i386/auto-init-15.c  |  22 ++
 gcc/testsuite/gcc.target/i386/auto-init-16.c  |  22 ++
 gcc/testsuite/gcc.target/i386/auto-init-17.c  |  23 ++
 gcc/testsuite/gcc.target/i386/auto-init-18.c  |  21 ++
 gcc/testsuite/gcc.target/i386/auto-init-19.c  |  26 ++
 gcc/testsuite/gcc.target/i386/auto-init-2.c   |  36 +++
 gcc/testsuite/gcc.target/i386/auto-init-20.c  |  26 ++
 gcc/testsuite/gcc.target/i386/auto-init-21.c  |  14 ++
 gcc/testsuite/gcc.target/i386/auto-init-22.c  |  14 ++
 gcc/testsuite/gcc.target/i386/auto-init-23.c  |  13 +
 gcc/testsuite/gcc.target/i386/auto-init-24.c  |  13 +
 gcc/testsuite/gcc.target/i386/auto-init-3.c   |  17 ++
 gcc/testsuite/gcc.target/i386/auto-init-4.c   |  20 ++
 gcc/testsuite/gcc.target/i386/auto-init-5.c   |  20 ++
 gcc/testsuite/gcc.target/i386/auto-init-6.c   |  19 ++
 gcc/testsuite/gcc.target/i386/auto-init-7.c   |  32 +++
 gcc/testsuite/gcc.target/i386/auto-init-8.c   |  35 +++
 gcc/testsuite/gcc.target/i386/auto-init-9.c   |  19 ++
 gcc/tree-cfg.c                                |  99 ++++++--
 gcc/tree-sra.c                                | 137 +++++++++-
 gcc/tree-ssa-structalias.c                    |   3 +
 gcc/tree-ssa-uninit.c                         |  47 ++++
 gcc/tree-ssa.c                                |  40 +++
 119 files changed, 3881 insertions(+), 32 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/auto-init-1.c
 create mode 100644 gcc/testsuite/c-c++-common/auto-init-10.c
 create mode 100644 gcc/testsuite/c-c++-common/auto-init-11.c
 create mode 100644 gcc/testsuite/c-c++-common/auto-init-12.c
 create mode 100644 gcc/testsuite/c-c++-common/auto-init-13.c
 create mode 100644 gcc/testsuite/c-c++-common/auto-init-14.c
 create mode 100644 gcc/testsuite/c-c++-common/auto-init-15.c
 create mode 100644 gcc/testsuite/c-c++-common/auto-init-16.c
 create mode 100644 gcc/testsuite/c-c++-common/auto-init-2.c
 create mode 100644 gcc/testsuite/c-c++-common/auto-init-3.c
 create mode 100644 gcc/testsuite/c-c++-common/auto-init-4.c
 create mode 100644 gcc/testsuite/c-c++-common/auto-init-5.c
 create mode 100644 gcc/testsuite/c-c++-common/auto-init-6.c
 create mode 100644 gcc/testsuite/c-c++-common/auto-init-7.c
 create mode 100644 gcc/testsuite/c-c++-common/auto-init-8.c
 create mode 100644 gcc/testsuite/c-c++-common/auto-init-9.c
 create mode 100644 gcc/testsuite/c-c++-common/auto-init-esra.c
 create mode 100644 gcc/testsuite/g++.dg/auto-init-uninit-pred-1_a.C
 create mode 100644 gcc/testsuite/g++.dg/auto-init-uninit-pred-1_b.C
 create mode 100644 gcc/testsuite/g++.dg/auto-init-uninit-pred-2_a.C
 create mode 100644 gcc/testsuite/g++.dg/auto-init-uninit-pred-2_b.C
 create mode 100644 gcc/testsuite/g++.dg/auto-init-uninit-pred-3_a.C
 create mode 100644 gcc/testsuite/g++.dg/auto-init-uninit-pred-3_b.C
 create mode 100644 gcc/testsuite/g++.dg/auto-init-uninit-pred-4.C
 create mode 100644 gcc/testsuite/g++.dg/auto-init-uninit-pred-loop-1_a.cc
 create mode 100644 gcc/testsuite/g++.dg/auto-init-uninit-pred-loop-1_b.cc
 create mode 100644 gcc/testsuite/g++.dg/auto-init-uninit-pred-loop-1_c.cc
 create mode 100644 gcc/testsuite/g++.dg/auto-init-uninit-pred-loop_1.cc
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-1.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-11.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-12.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-13.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-14.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-15.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-16.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-17.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-18.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-19.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-2.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-20.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-21.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-22.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-23.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-24.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-25.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-26.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-3.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-34.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-36.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-37.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-4.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-5.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-6.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-8.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-9.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-A.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-B.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-C.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-H.c
 create mode 100644 gcc/testsuite/gcc.dg/auto-init-uninit-I.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-10.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-11.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-12.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-13.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-14.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-15.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-16.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-17.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-18.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-19.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-2.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-20.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-3.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-4.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-5.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-6.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-7.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-8.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/auto-init-9.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-10.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-11.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-12.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-13.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-14.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-15.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-16.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-17.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-18.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-19.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-20.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-21.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-22.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-23.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-24.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-3.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-4.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-5.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-6.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-7.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-8.c
 create mode 100644 gcc/testsuite/gcc.target/i386/auto-init-9.c

diff --git a/gcc/builtins.c b/gcc/builtins.c
index e5e39386a93d..f3252403780e 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -145,7 +145,6 @@ static rtx expand_builtin_stpcpy (tree, rtx, machine_mode);
 static rtx expand_builtin_stpncpy (tree, rtx);
 static rtx expand_builtin_strncat (tree, rtx);
 static rtx expand_builtin_strncpy (tree, rtx);
-static rtx expand_builtin_memset (tree, rtx, machine_mode);
 static rtx expand_builtin_memset_args (tree, tree, tree, rtx, machine_mode, tree);
 static rtx expand_builtin_bzero (tree);
 static rtx expand_builtin_strlen (tree, rtx, machine_mode);
@@ -6710,7 +6709,7 @@ builtin_memset_gen_str (void *data, void *prevp,
    try to get the result in TARGET, if convenient (and in mode MODE if that's
    convenient).  */
 
-static rtx
+rtx
 expand_builtin_memset (tree exp, rtx target, machine_mode mode)
 {
   if (!validate_arglist (exp,
diff --git a/gcc/builtins.h b/gcc/builtins.h
index e71f40c300a1..7e92692ddf41 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -114,6 +114,7 @@ extern rtx builtin_strncpy_read_str (void *, void *, HOST_WIDE_INT,
 				     scalar_int_mode);
 extern rtx builtin_memset_read_str (void *, void *, HOST_WIDE_INT,
 				    scalar_int_mode);
+extern rtx expand_builtin_memset (tree, rtx, machine_mode);
 extern rtx expand_builtin_saveregs (void);
 extern tree std_build_builtin_va_list (void);
 extern tree std_fn_abi_va_list (tree);
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index e60fb31d8c8e..9b594006fe44 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -83,6 +83,7 @@ static tree handle_artificial_attribute (tree *, tree, tree, int, bool *);
 static tree handle_flatten_attribute (tree *, tree, tree, int, bool *);
 static tree handle_error_attribute (tree *, tree, tree, int, bool *);
 static tree handle_used_attribute (tree *, tree, tree, int, bool *);
+static tree handle_uninitialized_attribute (tree *, tree, tree, int, bool *);
 static tree handle_externally_visible_attribute (tree *, tree, tree, int,
 						 bool *);
 static tree handle_no_reorder_attribute (tree *, tree, tree, int,
@@ -331,6 +332,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_used_attribute, NULL },
   { "unused",                 0, 0, false, false, false, false,
 			      handle_unused_attribute, NULL },
+  { "uninitialized",	      0, 0, true, false, false, false,
+			      handle_uninitialized_attribute, NULL },
   { "retain",                 0, 0, true,  false, false, false,
 			      handle_retain_attribute, NULL },
   { "externally_visible",     0, 0, true,  false, false, false,
@@ -1613,6 +1616,22 @@ handle_retain_attribute (tree *pnode, tree name, tree ARG_UNUSED (args),
   return NULL_TREE;
 }
 
+/* Handle an "uninitialized" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_uninitialized_attribute (tree *node, tree name, tree ARG_UNUSED (args),
+				int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  if (!VAR_P (*node))
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
 /* Handle a "externally_visible" attribute; arguments as in
    struct attribute_spec.handler.  */
 
diff --git a/gcc/common.opt b/gcc/common.opt
index 5b03bbc66620..7ef182f1da33 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -3074,6 +3074,22 @@ ftree-scev-cprop
 Common Var(flag_tree_scev_cprop) Init(1) Optimization
 Enable copy propagation of scalar-evolution information.
 
+ftrivial-auto-var-init=
+Common Joined RejectNegative Enum(auto_init_type) Var(flag_auto_var_init) Init(AUTO_INIT_UNINITIALIZED) Optimization
+-ftrivial-auto-var-init=[uninitialized|pattern|zero]	Add initializations to automatic variables.
+
+Enum
+Name(auto_init_type) Type(enum auto_init_type) UnknownError(unrecognized automatic variable initialization type %qs)
+
+EnumValue
+Enum(auto_init_type) String(uninitialized) Value(AUTO_INIT_UNINITIALIZED)
+
+EnumValue
+Enum(auto_init_type) String(pattern) Value(AUTO_INIT_PATTERN)
+
+EnumValue
+Enum(auto_init_type) String(zero) Value(AUTO_INIT_ZERO)
+
 ; -fverbose-asm causes extra commentary information to be produced in
 ; the generated assembly code (to make it more readable).  This option
 ; is generally only of use to those who actually need to read the
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index b83cd4919bb1..54ee99d29407 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -7540,6 +7540,22 @@ will be placed in new, unique sections.
 
 This additional functionality requires Binutils version 2.36 or later.
 
+@item uninitialized
+@cindex @code{uninitialized} variable attribute
+This attribute, attached to a variable with automatic storage, means that
+the variable should not be automatically initialized by the compiler when
+the option @code{-ftrivial-auto-var-init} presents.
+
+With the option @code{-ftrivial-auto-var-init}, all the automatic variables
+that do not have explicit initializers will be initialized by the compiler.
+These additional compiler initializations might incur run-time overhead,
+sometimes dramatically.  This attribute can be used to mark some variables
+to be excluded from such automatical initialization in order to reduce runtime
+overhead.
+
+This attribute has no effect when the option @code{-ftrivial-auto-var-init}
+does not present.
+
 @item vector_size (@var{bytes})
 @cindex @code{vector_size} variable attribute
 This attribute specifies the vector size for the type of the declared
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index a9fd5fdc104c..0e9aac6e5d5a 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -572,9 +572,9 @@ Objective-C and Objective-C++ Dialects}.
 -ftree-parallelize-loops=@var{n}  -ftree-pre  -ftree-partial-pre  -ftree-pta @gol
 -ftree-reassoc  -ftree-scev-cprop  -ftree-sink  -ftree-slsr  -ftree-sra @gol
 -ftree-switch-conversion  -ftree-tail-merge @gol
--ftree-ter  -ftree-vectorize  -ftree-vrp  -funconstrained-commons @gol
--funit-at-a-time  -funroll-all-loops  -funroll-loops @gol
--funsafe-math-optimizations  -funswitch-loops @gol
+-ftree-ter  -ftree-vectorize  -ftree-vrp  -ftrivial-auto-var-init @gol
+-funconstrained-commons -funit-at-a-time  -funroll-all-loops @gol
+-funroll-loops -funsafe-math-optimizations  -funswitch-loops @gol
 -fipa-ra  -fvariable-expansion-in-unroller  -fvect-cost-model  -fvpt @gol
 -fweb  -fwhole-program  -fwpa  -fuse-linker-plugin -fzero-call-used-regs @gol
 --param @var{name}=@var{value}
@@ -11801,6 +11801,41 @@ Perform basic block vectorization on trees. This flag is enabled by default at
 @option{-O3} and by @option{-ftree-vectorize}, @option{-fprofile-use},
 and @option{-fauto-profile}.
 
+@item -ftrivial-auto-var-init=@var{choice}
+@opindex ftrivial-auto-var-init
+Initialize automatic variables with either a pattern or with zeroes to increase
+the security and predictability of a program by preventing uninitialized memory
+disclosure and use.
+GCC still considers an automatic variable that doesn't have an explicit
+initializer as uninitialized, -Wuninitialized will still report warning messages
+on such automatic variables.
+With this option, GCC will also initialize any padding of automatic variables
+that have structure or union types.
+
+The three values of @var{choice} are:
+
+@itemize @bullet
+@item
+@samp{uninitialized} doesn't initialize any automatic variables.
+This is C and C++'s default.
+
+@item
+@samp{pattern} Initialize automatic variables with values which will likely
+transform logic bugs into crashes down the line, are easily recognized in a
+crash dump and without being values that programmers can rely on for useful
+program semantics.
+The current value is byte-repeatable pattern with byte "0xFE".
+The values used for pattern initialization might be changed in the future.
+
+@item
+@samp{zero} Initialize automatic variables with zeroes.
+@end itemize
+
+The default is @samp{uninitialized}.
+
+You can control this behavior for a specific variable by using the variable
+attribute @code{uninitialized} (@pxref{Variable Attributes}).
+
 @item -fvect-cost-model=@var{model}
 @opindex fvect-cost-model
 Alter the cost model used for vectorization.  The @var{model} argument
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index e43d1de490df..6fb0aefbfc7b 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -276,6 +276,13 @@ enum vect_cost_model {
   VECT_COST_MODEL_DEFAULT = 1
 };
 
+/* Automatic variable initialization type.  */
+enum auto_init_type {
+  AUTO_INIT_UNINITIALIZED = 0,
+  AUTO_INIT_PATTERN = 1,
+  AUTO_INIT_ZERO = 2
+};
+
 /* Different instrumentation modes.  */
 enum sanitize_code {
   /* AddressSanitizer.  */
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 75a4a9d59fd3..4db53cda77f8 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -249,6 +249,10 @@ static enum gimplify_status gimplify_compound_expr (tree *, gimple_seq *, bool);
 static hash_map<tree, tree> *oacc_declare_returns;
 static enum gimplify_status gimplify_expr (tree *, gimple_seq *, gimple_seq *,
 					   bool (*) (tree), fallback_t, bool);
+static void gimple_add_init_for_auto_var (tree, enum auto_init_type, bool,
+					  gimple_seq *);
+static void maybe_with_size_expr (tree *);
+
 
 /* Shorter alias name for the above function for use in gimplify.c
    only.  */
@@ -1743,6 +1747,57 @@ force_labels_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
   return NULL_TREE;
 }
 
+
+/* Build a call to internal const function DEFERRED_INIT:
+   1st argument: SIZE of the DECL;
+   2nd argument: INIT_TYPE;
+   3rd argument: IS_VLA, 0 NO, 1 YES;
+
+   as DEFERRED_INIT (SIZE of the DECL, INIT_TYPE, IS_VLA).  */
+
+static gimple *
+build_deferred_init (tree decl,
+		     enum auto_init_type init_type,
+		     bool is_vla)
+{
+  gcc_assert ((is_vla && TREE_CODE (decl) == WITH_SIZE_EXPR)
+	      || (!is_vla && TREE_CODE (decl) != WITH_SIZE_EXPR));
+
+  tree decl_size = NULL_TREE;
+  tree init_type_node
+    = build_int_cst (integer_type_node, (int) init_type);
+  tree is_vla_node
+    = build_int_cst (integer_type_node, (int) is_vla);
+
+   if (TREE_CODE (decl) == WITH_SIZE_EXPR)
+     decl_size = TREE_OPERAND (decl, 1);
+   else
+     decl_size = TYPE_SIZE_UNIT (TREE_TYPE (decl));
+
+   return gimple_build_call_internal (IFN_DEFERRED_INIT, 3,
+				      decl_size, init_type_node, is_vla_node);
+}
+
+/* Generate initialization to automatic variable DECL based on INIT_TYPE.  */
+static void
+gimple_add_init_for_auto_var (tree decl,
+			      enum auto_init_type init_type,
+			      bool is_vla,
+			      gimple_seq *seq_p)
+{
+  gcc_assert (VAR_P (decl) && !DECL_EXTERNAL (decl) && !TREE_STATIC (decl));
+  gcc_assert (init_type > AUTO_INIT_UNINITIALIZED);
+
+  /* If this DECL is a variable sized type, we must remember the size.  */
+  tree orig_decl = decl;
+  maybe_with_size_expr (&decl);
+
+  gimple *call = build_deferred_init (decl, init_type, is_vla);
+  gimple_call_set_lhs (call, orig_decl);
+  gimplify_seq_add_stmt (seq_p, call);
+}
+
+
 /* Gimplify a DECL_EXPR node *STMT_P by making any necessary allocation
    and initialization explicit.  */
 
@@ -1839,6 +1894,18 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
 	       as they may contain a label address.  */
 	    walk_tree (&init, force_labels_r, NULL, NULL);
 	}
+      /* When there is no explicit initializer, if the user requested,
+	 We should insert an artifical initializer for this automatic
+	 variable.  */
+      else if (opt_for_fn (current_function_decl, flag_auto_var_init)
+		 > AUTO_INIT_UNINITIALIZED
+	       && !lookup_attribute ("uninitialized", DECL_ATTRIBUTES (decl))
+	       && !TREE_STATIC (decl))
+	gimple_add_init_for_auto_var (decl,
+				      opt_for_fn (current_function_decl,
+						  flag_auto_var_init),
+				      is_vla,
+				      seq_p);
     }
 
   return GS_ALL_DONE;
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index fb8b43d1ce2d..53798ac45177 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -2977,6 +2977,114 @@ expand_UNIQUE (internal_fn, gcall *stmt)
     emit_insn (pattern);
 }
 
+/* Expand the IFN_DEFERRED_INIT function:
+   Initialize the LHS variable with zero/pattern according to its second
+   argument INIT_TYPE.
+   if INIT_TYPE is AUTO_INIT_ZERO, use zeroes to initialize;
+   if INIT_TYPE is AUTO_INIT_PATTERN, use 0xFE byte-repeatable pattern
+     to initialize;
+   all the memory covered by this LHS variable is initialized including
+   paddings.
+   The reasons to choose 0xFE for pattern initialization are:
+     1. It is a non-canonical virtual address on x86_64, and at the
+	high end of the i386 kernel address space.
+     2. It is a very large float value (-1.694739530317379e+38).
+     3. It is also an unusual number for integers.  */
+#define INIT_PATTERN_VALUE  0xFE
+static void
+expand_DEFERRED_INIT (internal_fn, gcall *stmt)
+{
+  tree var = gimple_call_lhs (stmt);
+  tree size_of_var = gimple_call_arg (stmt, 0);
+  tree vlaaddr = NULL_TREE;
+  tree var_type = TREE_TYPE (var);
+  bool is_vla = (bool) TREE_INT_CST_LOW (gimple_call_arg (stmt, 2));
+  enum auto_init_type init_type
+    = (enum auto_init_type) TREE_INT_CST_LOW (gimple_call_arg (stmt, 1));
+
+  gcc_assert (init_type > AUTO_INIT_UNINITIALIZED);
+
+  /* if this variable is a VLA, get its SIZE and ADDR first.  */
+  if (is_vla)
+    {
+      /* The temporary address variable for this vla should have been
+	 created during gimplification phase.  Refer to gimplify_vla_decl
+	 for details.  */
+      tree var_decl = (TREE_CODE (var) == SSA_NAME) ?
+		       SSA_NAME_VAR (var) : var;
+      gcc_assert (DECL_HAS_VALUE_EXPR_P (var_decl));
+      gcc_assert (TREE_CODE (DECL_VALUE_EXPR (var_decl)) == INDIRECT_REF);
+      /* Get the address of this vla variable.  */
+      vlaaddr = TREE_OPERAND (DECL_VALUE_EXPR (var_decl), 0);
+    }
+
+  if (is_vla || (!use_register_for_decl (var)))
+    {
+    /* If this is a VLA or the variable is not in register,
+       expand to a memset to initialize it.  */
+      if (TREE_CODE (var) == SSA_NAME)
+	var = SSA_NAME_VAR (var);
+      tree addr = is_vla ? vlaaddr: build_fold_addr_expr (var);
+      tree value = (init_type == AUTO_INIT_PATTERN) ?
+		    build_int_cst (unsigned_char_type_node,
+				   INIT_PATTERN_VALUE) :
+		    build_zero_cst (unsigned_char_type_node);
+      tree m_call = build_call_expr (builtin_decl_implicit (BUILT_IN_MEMSET),
+				     3, addr, value, size_of_var);
+      /* Expand this memset call.  */
+      expand_builtin_memset (m_call, NULL_RTX, TYPE_MODE (var_type));
+    }
+  else
+    {
+    /* If this variable is in a register, use expand_assignment might
+       generate better code.  */
+      tree pattern = NULL_TREE;
+      unsigned HOST_WIDE_INT total_bytes
+	= tree_to_uhwi (TYPE_SIZE_UNIT (var_type));
+
+      if (INTEGRAL_TYPE_P (var_type) || POINTER_TYPE_P (var_type))
+	{
+	  unsigned char *ptr = (unsigned char *)xmalloc (total_bytes);
+	  for (unsigned int i = 0; i < total_bytes; i++)
+	    ptr[i] = INIT_PATTERN_VALUE;
+	  wide_int result = wi::from_buffer (ptr, total_bytes);
+	  pattern =  wide_int_to_tree (var_type, result);
+	}
+      else if (SCALAR_FLOAT_TYPE_P (var_type))
+	{
+	  unsigned int max_index = 2 + 2 * total_bytes;
+	  char *ptr= (char *)xmalloc (max_index);
+	  ptr[0] = '0';
+	  ptr[1] = 'x';
+	  for (unsigned int i = 2; i < max_index; i += 2)
+	    ptr[i] = 'F';
+	  for (unsigned int i = 3; i < max_index; i += 2)
+	    ptr[i] = 'E';
+	  REAL_VALUE_TYPE r_var;
+	  real_from_string (&r_var, ptr);
+	  pattern = build_real (var_type, r_var);
+	}
+      else
+	{
+	  tree index_type = build_index_type (size_int (total_bytes - 1));
+	  tree array_type = build_array_type (unsigned_char_type_node,
+					      index_type);
+	  tree element = build_int_cst (unsigned_char_type_node,
+					INIT_PATTERN_VALUE);
+	  vec<constructor_elt, va_gc> *elts = NULL;
+	  for (unsigned int i = 0; i < total_bytes; i++)
+	    CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, element);
+	  pattern = build_constructor (array_type, elts);
+	  pattern = build1 (VIEW_CONVERT_EXPR, var_type, pattern);
+	}
+
+      tree init = (init_type == AUTO_INIT_PATTERN) ?
+		   pattern :
+		   build_zero_cst (TREE_TYPE (var));
+      expand_assignment (var, init, false);
+    }
+}
+
 /* The size of an OpenACC compute dimension.  */
 
 static void
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index c3b8e730960c..cc3cd018f89d 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -357,6 +357,11 @@ DEF_INTERNAL_FN (VEC_CONVERT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (UNIQUE, ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (PHI, 0, NULL)
 
+/* A function to represent an artifical initialization to an uninitialized
+   automatic variable.  The first argument is the variable itself, the
+   second argument is the initialization type.  */
+DEF_INTERNAL_FN (DEFERRED_INIT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+
 /* DIM_SIZE and DIM_POS return the size of a particular compute
    dimension and the executing thread's position within that
    dimension.  DIM_POS is pure (and not const) so that it isn't
diff --git a/gcc/testsuite/c-c++-common/auto-init-1.c b/gcc/testsuite/c-c++-common/auto-init-1.c
new file mode 100644
index 000000000000..8b8ba3dd7e8d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/auto-init-1.c
@@ -0,0 +1,39 @@
+/* Verify zero initialization for integer and pointer type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-tree-gimple" } */
+
+#ifndef __cplusplus
+# define bool _Bool
+#endif
+
+enum E {
+  N1 = 0,
+  N2,
+  N3
+};
+
+extern void bar (char, short, int, enum E, long, long long, int *, bool);
+
+void foo()
+{
+  char temp1;
+  short temp2;
+  int temp3;
+  enum E temp4;
+  long temp5;
+  long long temp6;
+  int *temp7;
+  bool temp8;
+
+  bar (temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8);
+  return;
+}
+
+/* { dg-final { scan-tree-dump "temp1 = .DEFERRED_INIT \\(1, 2, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp2 = .DEFERRED_INIT \\(2, 2, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp3 = .DEFERRED_INIT \\(4, 2, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp4 = .DEFERRED_INIT \\(4, 2, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp5 = .DEFERRED_INIT \\(8, 2, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp6 = .DEFERRED_INIT \\(8, 2, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp7 = .DEFERRED_INIT \\(8, 2, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp8 = .DEFERRED_INIT \\(1, 2, 0\\)" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/auto-init-10.c b/gcc/testsuite/c-c++-common/auto-init-10.c
new file mode 100644
index 000000000000..0a8841e6e0f9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/auto-init-10.c
@@ -0,0 +1,17 @@
+/* Verify the variable attribute "uninitialized".  */ 
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-tree-gimple" } */
+
+extern void bar (char, long long *) __attribute__ ((uninitialized)); /* { dg-warning "'uninitialized' attribute ignored" } */
+
+void foo()
+{
+  short temp1;
+  long long __attribute__ ((uninitialized)) temp2[10];
+
+  bar (temp1, temp2);
+  return;
+}
+
+/* { dg-final { scan-tree-dump "temp1 = .DEFERRED_INIT \\(2, 1, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump-not "temp2 = .DEFERRED_INIT \\(" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/auto-init-11.c b/gcc/testsuite/c-c++-common/auto-init-11.c
new file mode 100644
index 000000000000..a2d66908d83e
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/auto-init-11.c
@@ -0,0 +1,14 @@
+/* Verify zero initialization for VLA automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-tree-gimple" } */
+
+extern void bar (int);
+
+void foo(int n)
+{
+  int arr[n];
+  bar (arr[2]);
+  return;
+}
+
+/* { dg-final { scan-tree-dump ".DEFERRED_INIT \\(D.\\d*, 2, 1\\)" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/auto-init-12.c b/gcc/testsuite/c-c++-common/auto-init-12.c
new file mode 100644
index 000000000000..f05d743fda02
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/auto-init-12.c
@@ -0,0 +1,14 @@
+/* Verify zero initialization for VLA automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-tree-gimple" } */
+
+extern void bar (int);
+
+void foo(int n)
+{
+  int arr[n];
+  bar (arr[2]);
+  return;
+}
+
+/* { dg-final { scan-tree-dump ".DEFERRED_INIT \\(D.\\d*, 1, 1\\)" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/auto-init-13.c b/gcc/testsuite/c-c++-common/auto-init-13.c
new file mode 100644
index 000000000000..b0c0365b288a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/auto-init-13.c
@@ -0,0 +1,23 @@
+/* Verify the auto initialization of structure or union with a flexible array
+   member.  */ 
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-tree-gimple" } */
+
+struct a {
+  int b;
+  int array[];
+};
+union tar {
+  struct a bar;
+  char buf;
+};
+
+int foo()
+{
+  struct a d;
+  union tar var;
+  return d.b + var.bar.b;
+}
+
+/* { dg-final { scan-tree-dump "d = .DEFERRED_INIT \\(4, 1, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "var = .DEFERRED_INIT \\(4, 1, 0\\)" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/auto-init-14.c b/gcc/testsuite/c-c++-common/auto-init-14.c
new file mode 100644
index 000000000000..986bb19faaf0
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/auto-init-14.c
@@ -0,0 +1,23 @@
+/* Verify the auto initialization of structure or union with a flexible array
+   member.  */ 
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-tree-gimple" } */
+
+struct a {
+  int b;
+  int array[];
+};
+union tar {
+  struct a bar;
+  char buf;
+};
+
+int foo()
+{
+  struct a d;
+  union tar var;
+  return d.b + var.bar.b;
+}
+
+/* { dg-final { scan-tree-dump "d = .DEFERRED_INIT \\(4, 2, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "var = .DEFERRED_INIT \\(4, 2, 0\\)" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/auto-init-15.c b/gcc/testsuite/c-c++-common/auto-init-15.c
new file mode 100644
index 000000000000..aa9d7fab68a6
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/auto-init-15.c
@@ -0,0 +1,13 @@
+/* Verify the auto initialization of nested VLA.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-tree-gimple" } */
+
+void g(void *);
+
+void foo(int a)
+{
+  int x[a][a];
+  g(x);
+}
+
+/* { dg-final { scan-tree-dump ".DEFERRED_INIT \\(D.\\d*, 2, 1\\)" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/auto-init-16.c b/gcc/testsuite/c-c++-common/auto-init-16.c
new file mode 100644
index 000000000000..86493eef9e47
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/auto-init-16.c
@@ -0,0 +1,13 @@
+/* Verify the auto initialization of nested VLA.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-tree-gimple" } */
+
+void g(void *);
+
+void foo(int a)
+{
+  int x[a][a];
+  g(x);
+}
+
+/* { dg-final { scan-tree-dump ".DEFERRED_INIT \\(D.\\d*, 1, 1\\)" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/auto-init-2.c b/gcc/testsuite/c-c++-common/auto-init-2.c
new file mode 100644
index 000000000000..71955b54ba03
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/auto-init-2.c
@@ -0,0 +1,39 @@
+/* Verify pattern initialization for integer and pointer type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-tree-gimple" } */
+
+#ifndef __cplusplus
+# define bool _Bool
+#endif
+
+enum E {
+  N1 = 0,
+  N2,
+  N3
+};
+
+extern void bar (char, short, int, enum E, long, long long, int *, bool);
+
+void foo()
+{
+  char temp1;
+  short temp2;
+  int temp3;
+  enum E temp4;
+  long temp5;
+  long long temp6;
+  int *temp7;
+  bool temp8;
+
+  bar (temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8);
+  return;
+}
+
+/* { dg-final { scan-tree-dump "temp1 = .DEFERRED_INIT \\(1, 1, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp2 = .DEFERRED_INIT \\(2, 1, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp3 = .DEFERRED_INIT \\(4, 1, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp4 = .DEFERRED_INIT \\(4, 1, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp5 = .DEFERRED_INIT \\(8, 1, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp6 = .DEFERRED_INIT \\(8, 1, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp7 = .DEFERRED_INIT \\(8, 1, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp8 = .DEFERRED_INIT \\(1, 1, 0\\)" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/auto-init-3.c b/gcc/testsuite/c-c++-common/auto-init-3.c
new file mode 100644
index 000000000000..312275666381
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/auto-init-3.c
@@ -0,0 +1,19 @@
+/* Verify zero initialization for floating point type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-tree-gimple" } */
+
+long double result;
+
+long double foo()
+{
+  float temp1;
+  double temp2;
+  long double temp3;
+  
+  result = temp1 + temp2 + temp3;
+  return result;
+}
+
+/* { dg-final { scan-tree-dump "temp1 = .DEFERRED_INIT \\(4, 2, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp2 = .DEFERRED_INIT \\(8, 2, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp3 = .DEFERRED_INIT \\(16, 2, 0\\)" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/auto-init-4.c b/gcc/testsuite/c-c++-common/auto-init-4.c
new file mode 100644
index 000000000000..15a431726dcd
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/auto-init-4.c
@@ -0,0 +1,19 @@
+/* Verify pattern initialization for floating point type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-tree-gimple" } */
+
+long double result;
+
+long double foo()
+{
+  float temp1;
+  double temp2;
+  long double temp3;
+  
+  result = temp1 + temp2 + temp3;
+  return result;
+}
+
+/* { dg-final { scan-tree-dump "temp1 = .DEFERRED_INIT \\(4, 1, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp2 = .DEFERRED_INIT \\(8, 1, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp3 = .DEFERRED_INIT \\(16, 1, 0\\)" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/auto-init-5.c b/gcc/testsuite/c-c++-common/auto-init-5.c
new file mode 100644
index 000000000000..54f90bc890b5
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/auto-init-5.c
@@ -0,0 +1,21 @@
+/* Verify zero initialization for complex type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-tree-gimple" } */
+
+
+_Complex long double result;
+
+_Complex long double foo()
+{
+  _Complex float temp1;
+  _Complex double temp2;
+  _Complex long double temp3;
+
+  result = temp1 + temp2 + temp3;
+  return result;
+}
+
+/* { dg-final { scan-tree-dump "temp1 = .DEFERRED_INIT \\(8, 2, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp2 = .DEFERRED_INIT \\(16, 2, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp3 = .DEFERRED_INIT \\(32, 2, 0\\)" "gimple" } } */
+
diff --git a/gcc/testsuite/c-c++-common/auto-init-6.c b/gcc/testsuite/c-c++-common/auto-init-6.c
new file mode 100644
index 000000000000..4124a2b183aa
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/auto-init-6.c
@@ -0,0 +1,21 @@
+/* Verify pattern initialization for complex type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-tree-gimple" } */
+
+
+_Complex long double result;
+
+_Complex long double foo()
+{
+  _Complex float temp1;
+  _Complex double temp2;
+  _Complex long double temp3;
+
+  result = temp1 + temp2 + temp3;
+  return result;
+}
+
+/* { dg-final { scan-tree-dump "temp1 = .DEFERRED_INIT \\(8, 1, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp2 = .DEFERRED_INIT \\(16, 1, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp3 = .DEFERRED_INIT \\(32, 1, 0\\)" "gimple" } } */
+
diff --git a/gcc/testsuite/c-c++-common/auto-init-7.c b/gcc/testsuite/c-c++-common/auto-init-7.c
new file mode 100644
index 000000000000..19986969a8f7
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/auto-init-7.c
@@ -0,0 +1,35 @@
+/* Verify zero initialization for array, union, and structure type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-tree-gimple" } */
+
+struct S
+{
+  int f1;
+  float f2;
+  char f3[20];
+};
+
+union U
+{
+  char u1[5];
+  int u2;
+  float u3; 
+};
+
+double result;
+
+double foo()
+{
+  int temp1[3];
+  double temp2[3];
+  struct S temp3;
+  union U temp4;
+  
+  result = temp1[2] + temp2[1] + temp3.f2 + temp4.u3;
+  return result;
+}
+
+/* { dg-final { scan-tree-dump "temp1 = .DEFERRED_INIT \\(12, 2, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp2 = .DEFERRED_INIT \\(24, 2, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp3 = .DEFERRED_INIT \\(28, 2, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp4 = .DEFERRED_INIT \\(8, 2, 0\\)" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/auto-init-8.c b/gcc/testsuite/c-c++-common/auto-init-8.c
new file mode 100644
index 000000000000..9778e911e3ad
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/auto-init-8.c
@@ -0,0 +1,35 @@
+/* Verify pattern initialization for array, union, and structure type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-tree-gimple" } */
+
+struct S
+{
+  int f1;
+  float f2;
+  char f3[20];
+};
+
+union U
+{
+  char u1[5];
+  int u2;
+  float u3; 
+};
+
+double result;
+
+double foo()
+{
+  int temp1[3];
+  double temp2[3];
+  struct S temp3;
+  union U temp4;
+  
+  result = temp1[2] + temp2[1] + temp3.f2 + temp4.u3;
+  return result;
+}
+
+/* { dg-final { scan-tree-dump "temp1 = .DEFERRED_INIT \\(12, 1, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp2 = .DEFERRED_INIT \\(24, 1, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp3 = .DEFERRED_INIT \\(28, 1, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "temp4 = .DEFERRED_INIT \\(8, 1, 0\\)" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/auto-init-9.c b/gcc/testsuite/c-c++-common/auto-init-9.c
new file mode 100644
index 000000000000..90bbd8cb1afe
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/auto-init-9.c
@@ -0,0 +1,17 @@
+/* Verify the variable attribute "uninitialized".  */ 
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-tree-gimple" } */
+
+extern void bar (char, long long *) __attribute__ ((uninitialized)); /* { dg-warning "'uninitialized' attribute ignored" } */
+
+void foo()
+{
+  short temp1;
+  long long __attribute__ ((uninitialized)) temp2[10];
+
+  bar (temp1, temp2);
+  return;
+}
+
+/* { dg-final { scan-tree-dump "temp1 = .DEFERRED_INIT \\(2, 2, 0\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump-not "temp2 = .DEFERRED_INIT \\(8, 2, 0\\)" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/auto-init-esra.c b/gcc/testsuite/c-c++-common/auto-init-esra.c
new file mode 100644
index 000000000000..77ec02355df4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/auto-init-esra.c
@@ -0,0 +1,35 @@
+/* Verify the strength reduction adjustment for -ftrivial-auto-var-init.  */ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -ftrivial-auto-var-init=zero -fdump-tree-gimple -fdump-tree-esra" } */
+
+
+typedef double VECTOR[3];
+
+enum
+{
+ X = 0,
+ Y = 1,
+ Z = 2,
+ T = 3
+};
+
+void Assign_Vector(VECTOR d, VECTOR s)
+{
+ d[X] = s[X];
+ d[Y] = s[Y];
+ d[Z] = s[Z];
+}
+
+void VCross(VECTOR a, const VECTOR b, const VECTOR c)
+{
+ VECTOR tmp;
+
+ tmp[X] = b[Y] * c[Z] - b[Z] * c[Y];
+ tmp[Y] = b[Z] * c[X] - b[X] * c[Z];
+ tmp[Z] = b[X] * c[Y] - b[Y] * c[X];
+
+ Assign_Vector(a, tmp);
+}
+
+/* { dg-final { scan-tree-dump-times "tmp = .DEFERRED_INIT \\(24, 2, 0\\)" 1 "gimple" } } */
+/* { dg-final { scan-tree-dump-times ".DEFERRED_INIT \\(8, 2, 0\\)" 3 "esra" } } */
diff --git a/gcc/testsuite/g++.dg/auto-init-uninit-pred-1_a.C b/gcc/testsuite/g++.dg/auto-init-uninit-pred-1_a.C
new file mode 100644
index 000000000000..33c17441384f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/auto-init-uninit-pred-1_a.C
@@ -0,0 +1,63 @@
+/* { dg-do compile } */
+/* { dg-options "-Wuninitialized -O2 -ftrivial-auto-var-init=zero" } */
+
+typedef long long int64;
+void incr ();
+bool is_valid (int);
+int  get_time ();
+
+class A 
+{
+public:
+  A ();
+  ~A () {
+    if (I) delete I;
+  }
+
+private:
+  int* I;
+};
+
+bool get_url (A *);
+
+class M {
+
+ public:
+__attribute__ ((always_inline))  int GetC (int *c)  {
+
+    A details_str;
+    if (!get_url (&details_str))
+      {
+        incr ();
+        return 1;
+      }
+
+    *c = get_time ();
+    return -1;
+  }
+
+  void do_sth();
+  void do_sth2();
+   
+  void P (int64 t)
+    {
+      int cc; /* { dg-bogus "uninitialized" "uninitialized variable warning" }  */ 
+      if (GetC (&cc) >= 0 )
+        return;
+      
+      if (t && cc <= 0 )  /* { dg-bogus "uninitialized" "uninitialized variable warning" } */
+        {
+          this->do_sth();
+          return;
+        }
+
+    do_sth2();
+  }
+};
+
+M* m; 
+void foo(int x)
+{
+  m = new M;
+  m->P(x);
+}
diff --git a/gcc/testsuite/g++.dg/auto-init-uninit-pred-1_b.C b/gcc/testsuite/g++.dg/auto-init-uninit-pred-1_b.C
new file mode 100644
index 000000000000..6b6bdaed71e0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/auto-init-uninit-pred-1_b.C
@@ -0,0 +1,63 @@
+/* { dg-do compile } */
+/* { dg-options "-Wuninitialized -O2 -ftrivial-auto-var-init=zero" } */
+
+typedef long long int64;
+void incr ();
+bool is_valid (int);
+int  get_time ();
+
+class A 
+{
+public:
+  A ();
+  ~A () {
+    if (I) delete I;
+  }
+
+private:
+  int* I;
+};
+
+bool get_url (A *);
+
+class M {
+
+ public:
+__attribute__ ((always_inline))  int GetC (int *c)  {
+
+    A details_str;
+    if (!get_url (&details_str))
+      {
+        incr ();
+        return 1;
+      }
+
+    *c = get_time ();
+    return -1;
+  }
+
+  void do_sth();
+  void do_sth2();
+   
+  void P (int64 t)
+    {
+      int cc; /* { dg-message "note: 'cc' was declared here" } */
+      if (GetC (&cc) <= 0 ) /* return flag checked wrongly */
+        return;
+      
+      if (t && cc <= 0 )  /* { dg-warning "uninitialized" "uninitialized variable warning" } */
+        {
+          this->do_sth();
+          return;
+        }
+
+    do_sth2();
+  }
+};
+
+M* m; 
+void foo(int x)
+{
+  m = new M;
+  m->P(x);
+}
diff --git a/gcc/testsuite/g++.dg/auto-init-uninit-pred-2_a.C b/gcc/testsuite/g++.dg/auto-init-uninit-pred-2_a.C
new file mode 100644
index 000000000000..fc18cb1e7c91
--- /dev/null
+++ b/gcc/testsuite/g++.dg/auto-init-uninit-pred-2_a.C
@@ -0,0 +1,62 @@
+/* { dg-do compile } */
+/* { dg-options "-Wuninitialized -O2 -ftrivial-auto-var-init=zero" } */
+
+typedef long long int64;
+void incr ();
+bool is_valid (int);
+int  get_time ();
+
+class A 
+{
+public:
+  A ();
+  ~A () {
+    if (I) delete I;
+  }
+
+private:
+  int* I;
+};
+
+bool get_url (A *);
+
+class M {
+
+ public:
+__attribute__ ((always_inline))  bool GetC (int *c)  {
+
+    A details_str;
+    if (get_url (&details_str))
+      {
+        *c = get_time ();
+        return true;
+      }
+
+    return false;
+  }
+
+  void do_sth();
+  void do_sth2();
+   
+  void P (int64 t)
+    {
+      int cc; 
+      if (!GetC (&cc)) /* return flag checked properly */
+        return;
+      
+      if (cc <= 0)  /* { dg-bogus "uninitialized" "uninitialized variable warning" } */
+        {
+          this->do_sth();
+          return;
+        }
+
+    do_sth2();
+  }
+};
+
+M* m; 
+void foo(int x)
+{
+  m = new M;
+  m->P(x);
+}
diff --git a/gcc/testsuite/g++.dg/auto-init-uninit-pred-2_b.C b/gcc/testsuite/g++.dg/auto-init-uninit-pred-2_b.C
new file mode 100644
index 000000000000..e85a36f16fff
--- /dev/null
+++ b/gcc/testsuite/g++.dg/auto-init-uninit-pred-2_b.C
@@ -0,0 +1,62 @@
+/* { dg-do compile } */
+/* { dg-options "-Wuninitialized -O2 -ftrivial-auto-var-init=zero" } */
+
+typedef long long int64;
+void incr ();
+bool is_valid (int);
+int  get_time ();
+
+class A 
+{
+public:
+  A ();
+  ~A () {
+    if (I) delete I;
+  }
+
+private:
+  int* I;
+};
+
+bool get_url (A *);
+
+class M {
+
+ public:
+__attribute__ ((always_inline))  bool GetC (int *c)  {
+
+    A details_str;
+    if (get_url (&details_str))
+      {
+        *c = get_time ();
+        return true;
+      }
+
+    return false;
+  }
+
+  void do_sth();
+  void do_sth2();
+   
+  void P (int64 t)
+    {
+      int cc; /* { dg-message "note: 'cc' was declared here" } */
+      if (GetC (&cc)) /* return flag checked wrongly */
+        return;
+      
+      if (cc <= 0)  /* { dg-warning "uninitialized" "uninitialized variable warning" } */
+        {
+          this->do_sth();
+          return;
+        }
+
+    do_sth2();
+  }
+};
+
+M* m; 
+void foo(int x)
+{
+  m = new M;
+  m->P(x);
+}
diff --git a/gcc/testsuite/g++.dg/auto-init-uninit-pred-3_a.C b/gcc/testsuite/g++.dg/auto-init-uninit-pred-3_a.C
new file mode 100644
index 000000000000..09ed69215320
--- /dev/null
+++ b/gcc/testsuite/g++.dg/auto-init-uninit-pred-3_a.C
@@ -0,0 +1,77 @@
+/* { dg-do compile } */
+/* { dg-options "-Wuninitialized -O2 -ftrivial-auto-var-init=zero" } */
+
+/* Multiple initialization paths.  */
+
+typedef long long int64;
+void incr ();
+bool is_valid (int);
+int  get_time ();
+
+class A
+{
+public:
+  A ();
+  ~A () {
+    if (I) delete I;
+  }
+
+private:
+  int* I;
+};
+
+bool get_url (A *);
+bool get_url2 (A *);
+
+class M {
+
+ public:
+ __attribute__ ((always_inline))
+ bool GetC (int *c)  {
+
+    A details_str;
+    /* Intialization path 1  */
+    if (get_url (&details_str))
+      {
+        *c = get_time ();
+        return true;
+      }
+
+    /* insert dtor calls (inlined) into following return paths  */
+    A tmp_str;
+
+    /* Intializtion path 2  */
+    if (get_url2 (&details_str))
+      {
+        *c = get_time ();
+        return true;
+      }
+
+    return false;
+  }
+
+  void do_sth();
+  void do_sth2();
+
+  void P (int64 t)
+    {
+      int cc;
+      if (!GetC (&cc)) /* return flag checked properly */
+        return;
+
+      if (cc <= 0)   /* { dg-bogus "uninitialized" "uninitialized variable warning" } */
+        {
+          this->do_sth();
+          return;
+        }
+
+    do_sth2();
+  }
+};
+
+M* m;
+void test(int x)
+{
+  m = new M;
+  m->P(x);
+}
diff --git a/gcc/testsuite/g++.dg/auto-init-uninit-pred-3_b.C b/gcc/testsuite/g++.dg/auto-init-uninit-pred-3_b.C
new file mode 100644
index 000000000000..8e7b8541725e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/auto-init-uninit-pred-3_b.C
@@ -0,0 +1,87 @@
+/* { dg-do compile } */
+/* { dg-options "-Wuninitialized -O2 -ftrivial-auto-var-init=zero" } */
+
+/* Multiple initialization paths.  */
+
+typedef long long int64;
+void incr ();
+bool is_valid (int);
+int  get_time ();
+
+class A
+{
+public:
+  A ();
+  ~A () {
+    if (I) delete I;
+  }
+
+private:
+  int* I;
+};
+
+bool get_url (A *);
+bool get_url2 (A *);
+bool get_url3 (A *);
+
+class M {
+
+ public:
+ __attribute__ ((always_inline))
+ bool GetC (int *c)  {
+
+    A details_str;
+
+    /* Initialization path 1  */
+    if (get_url (&details_str))
+      {
+        *c = get_time ();
+        return true;
+      }
+
+    /* Destructor call before return*/
+    A tmp_str;
+
+    /* Initialization path 2  */
+    if (get_url2 (&details_str))
+      {
+        *c = get_time ();
+        return true;
+      }
+
+    /* Fail to initialize in this path but
+       still returns true  */
+    if (get_url2 (&details_str))
+      {
+        /* Fail to initialize *c  */
+        return true;
+      }
+
+    return false;
+  }
+
+  void do_sth();
+  void do_sth2();
+
+  void P (int64 t)
+    {
+      int cc;
+      if (!GetC (&cc))
+        return;
+
+      if (cc <= 0)   /* { dg-warning "uninitialized" "uninitialized variable warning" } */
+        {
+          this->do_sth();
+          return;
+        }
+
+    do_sth2();
+  }
+};
+
+M* m;
+void test(int x)
+{
+  m = new M;
+  m->P(x);
+}
diff --git a/gcc/testsuite/g++.dg/auto-init-uninit-pred-4.C b/gcc/testsuite/g++.dg/auto-init-uninit-pred-4.C
new file mode 100644
index 000000000000..c48770ae44dd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/auto-init-uninit-pred-4.C
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-Wuninitialized -Og -ftrivial-auto-var-init=zero" } */
+
+int pop ();
+int pop_first_bucket;
+
+int my_pop ()
+{
+  int out;  // { dg-bogus "uninitialized" "uninitialized variable warning" }
+
+  while (pop_first_bucket)
+    if (pop_first_bucket && (out = pop()))
+      return out;
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/auto-init-uninit-pred-loop-1_a.cc b/gcc/testsuite/g++.dg/auto-init-uninit-pred-loop-1_a.cc
new file mode 100644
index 000000000000..629677a60259
--- /dev/null
+++ b/gcc/testsuite/g++.dg/auto-init-uninit-pred-loop-1_a.cc
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-Wuninitialized -O2 -ftrivial-auto-var-init=zero" } */
+
+extern int bar();
+int foo(void)
+{
+ for (;;) {
+   int err = ({int _err; /*  { dg-bogus "uninitialized" "false warning" } */
+     for (int i = 0; i < 16; ++i) {
+       _err = 17;
+       _err = bar();
+     }
+     _err; /*  { dg-bogus "uninitialized" "false warning" } */
+   });
+
+   if (err == 0) return 17; 
+ }
+
+ return 18;
+}
+
diff --git a/gcc/testsuite/g++.dg/auto-init-uninit-pred-loop-1_b.cc b/gcc/testsuite/g++.dg/auto-init-uninit-pred-loop-1_b.cc
new file mode 100644
index 000000000000..04ab364ac83e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/auto-init-uninit-pred-loop-1_b.cc
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-Wuninitialized -O2 -ftrivial-auto-var-init=zero" } */
+
+extern int bar();
+int foo(int n)
+{
+ for (;;) {
+   int err = ({int _err; 
+     for (int i = 0; i < n; ++i) {
+       _err = 17;
+       _err = bar();
+     }
+     _err; 
+   }); /* { dg-warning "uninitialized" "warn on _err" } */
+
+   if (err == 0) return 17; 
+ }
+
+ return 18;
+}
+
diff --git a/gcc/testsuite/g++.dg/auto-init-uninit-pred-loop-1_c.cc b/gcc/testsuite/g++.dg/auto-init-uninit-pred-loop-1_c.cc
new file mode 100644
index 000000000000..82a1846c6e0b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/auto-init-uninit-pred-loop-1_c.cc
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-Wuninitialized -O2 -ftrivial-auto-var-init=zero" } */
+
+extern int bar();
+int foo(int n, int m)
+{
+ for (;;) {
+   int err = ({int _err; 
+     for (int i = 0; i < 16; ++i) {
+       if (m+i > n)
+          break;
+       _err = 17;
+       _err = bar();
+     }
+     _err; 
+   }); 
+
+   if (err == 0) return 17; }); /* { dg-warning "uninitialized" "warn on _err" } */
+ }
+
+ return 18;
+}
+
diff --git a/gcc/testsuite/g++.dg/auto-init-uninit-pred-loop_1.cc b/gcc/testsuite/g++.dg/auto-init-uninit-pred-loop_1.cc
new file mode 100644
index 000000000000..629677a60259
--- /dev/null
+++ b/gcc/testsuite/g++.dg/auto-init-uninit-pred-loop_1.cc
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-Wuninitialized -O2 -ftrivial-auto-var-init=zero" } */
+
+extern int bar();
+int foo(void)
+{
+ for (;;) {
+   int err = ({int _err; /*  { dg-bogus "uninitialized" "false warning" } */
+     for (int i = 0; i < 16; ++i) {
+       _err = 17;
+       _err = bar();
+     }
+     _err; /*  { dg-bogus "uninitialized" "false warning" } */
+   });
+
+   if (err == 0) return 17; 
+ }
+
+ return 18;
+}
+
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-1.c b/gcc/testsuite/gcc.dg/auto-init-uninit-1.c
new file mode 100644
index 000000000000..cb0e7cc62545
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-1.c
@@ -0,0 +1,30 @@
+/* Spurious uninitialized variable warnings, case 1.
+   Taken from cppfiles.c (merge_include_chains) */
+/* { dg-do compile } */
+/* { dg-options "-O -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+struct list
+{
+  struct list *next;
+  int id;
+};
+
+extern void free (void *);
+
+void remove_dupes (struct list *el)
+{
+  struct list *p, *q, *r;  /* { dg-bogus "r" "uninitialized variable warning" } */
+
+  for (p = el; p; p = p->next)
+  {
+    for (q = el; q != p; q = q->next)
+      if (q->id == p->id)
+      {
+	r->next = p->next;
+	free (p);
+	p = r;
+	break;
+      }
+    r = p;
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-11.c b/gcc/testsuite/gcc.dg/auto-init-uninit-11.c
new file mode 100644
index 000000000000..559e2d963376
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-11.c
@@ -0,0 +1,42 @@
+/* Positive test for uninitialized variables.  */
+/* { dg-do compile } */
+/* { dg-options "-O -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+int sink;
+
+void f1(int parm)	/* { dg-bogus "uninitialized" "parameter" } */
+{
+  sink = parm;		/* { dg-bogus "uninitialized" "parameter" } */
+}
+
+void f2(void)
+{
+  int x;
+  sink = x;		/* { dg-warning "is used" "unconditional" } */
+}
+
+void f3(int p)
+{
+  int x;		
+  if (p)
+    x = p;
+  sink = x;            /* { dg-warning "may be used" "conditional" } */
+}
+
+void f4(int p)
+{
+  int x;		/* { dg-bogus "uninitialized" "easy if" } */
+  if (p)
+    x = 1;
+  else
+    x = 2;
+  sink = x;
+}
+
+void f5(void)
+{
+  int x, i;		/* { dg-bogus "uninitialized" "easy loop" } */
+  for (i = 0; i < 10; ++i)
+    x = 1;
+  sink = x;
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-12.c b/gcc/testsuite/gcc.dg/auto-init-uninit-12.c
new file mode 100644
index 000000000000..acff725722dc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-12.c
@@ -0,0 +1,12 @@
+/* PR 23497 */
+/* { dg-do compile } */
+/* { dg-options "-O -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+typedef _Complex float C;
+C foo()
+{
+  C f;
+  __real__ f = 0;
+  __imag__ f = 0;
+  return f;
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-13.c b/gcc/testsuite/gcc.dg/auto-init-uninit-13.c
new file mode 100644
index 000000000000..87dd8b587e00
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-13.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-options "-O -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+typedef _Complex float C;
+C foo()
+{
+  C f;
+  __imag__ f = 0;
+  return f;	/* { dg-warning "is used" "unconditional" } */
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-14.c b/gcc/testsuite/gcc.dg/auto-init-uninit-14.c
new file mode 100644
index 000000000000..592052a9e9d0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-14.c
@@ -0,0 +1,20 @@
+/* PR 24931 */
+/* { dg-do compile } */
+/* { dg-options "-O -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+struct p {
+        short x, y;
+};
+
+struct s {
+        int i;
+        struct p p;
+};
+
+struct s f()
+{
+        struct s s;
+        s.p = (struct p){};
+        s.i = (s.p.x || s.p.y);
+        return s;
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-15.c b/gcc/testsuite/gcc.dg/auto-init-uninit-15.c
new file mode 100644
index 000000000000..121f0cff274a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-15.c
@@ -0,0 +1,26 @@
+/* PR tree-optimization/17506
+   We issue an uninitialized variable warning at a wrong location at
+   line 11, which is very confusing.  Make sure we print out a note to
+   make it less confusing.  (not xfailed alternative)
+   But it is of course ok if we warn in bar about uninitialized use
+   of j.  (not xfailed alternative)  */
+/* { dg-do compile } */
+/* { dg-options "-O1 -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+inline int
+foo (int i)
+{
+  if (i) /* { dg-warning "used uninitialized" } */
+    return 1;
+  return 0;
+}
+
+void baz (void);
+
+void
+bar (void)
+{
+  int j; /* { dg-message "note: 'j' was declared here" "" } */
+  for (; foo (j); ++j)  /* { dg-warning "'j' is used uninitialized" "" { xfail *-*-* } } */
+    baz ();
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-16.c b/gcc/testsuite/gcc.dg/auto-init-uninit-16.c
new file mode 100644
index 000000000000..0e4f336f726f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-16.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+int foo, bar;
+
+static
+void decode_reloc(int reloc, int *is_alt)
+{
+  if (reloc >= 20)
+      *is_alt = 1;
+  else if (reloc >= 10)
+      *is_alt = 0;
+}
+
+void testfunc()
+{
+  int alt_reloc;
+
+  decode_reloc(foo, &alt_reloc);
+
+  if (alt_reloc) /* { dg-warning "may be used uninitialized" } */
+    bar = 42;
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-17.c b/gcc/testsuite/gcc.dg/auto-init-uninit-17.c
new file mode 100644
index 000000000000..9eec9440c75e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-17.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+typedef _Complex float C;
+C foo(int cond)
+{
+  C f;
+  __imag__ f = 0;
+  if (cond)
+    {
+      __real__ f = 1;
+      return f;
+    }
+  return f;	/* { dg-warning "may be used" "unconditional" } */
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-18.c b/gcc/testsuite/gcc.dg/auto-init-uninit-18.c
new file mode 100644
index 000000000000..4922848f6dc7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-18.c
@@ -0,0 +1,24 @@
+/* { dg-do compile }  */
+/* { dg-options "-O -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+char *foo(int bar, char *baz)
+{
+  char *tmp;
+
+  if (bar & 3)
+    tmp = baz;
+
+  switch (bar) {
+  case 1:
+    tmp[5] = 7;    /* { dg-bogus "may be used uninitialized" } */
+    break;
+  case 2:
+    tmp[11] = 15;  /* { dg-bogus "may be used uninitialized" } */
+    break;
+  default:
+    tmp = 0;
+    break;
+  }
+
+  return tmp;      /* { dg-bogus "may be used uninitialized" } */
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-19.c b/gcc/testsuite/gcc.dg/auto-init-uninit-19.c
new file mode 100644
index 000000000000..38d27e4f9548
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-19.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O -Wuninitialized -ftrivial-auto-var-init=zero" } */
+/* { dg-additional-options "-finline-small-functions" { target avr-*-* } } */
+
+int a, l, m;
+float *b;
+float c, d, e, g, h;
+unsigned char i, k;
+void
+fn1 (int p1, float *f1, float *f2, float *f3, unsigned char *c1, float *f4,
+     unsigned char *c2, float *p10)
+{
+  if (p1 & 8)
+    b[3] = p10[a];
+  /* { dg-warning "may be used uninitialized" "" { target { { nonpic || pie_enabled } || { hppa*64*-*-* *-*-darwin* } } } .-1 } */
+}
+
+void
+fn2 ()
+{
+  float *n;
+  if (l & 6)
+    n = &c + m;
+  fn1 (l, &d, &e, &g, &i, &h, &k, n);
+  /* { dg-warning "may be used uninitialized" "" { target { ! { { nonpic || pie_enabled } || { hppa*64*-*-* *-*-darwin* } } } } .-1 } */
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-2.c b/gcc/testsuite/gcc.dg/auto-init-uninit-2.c
new file mode 100644
index 000000000000..da03bf8f6d98
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-2.c
@@ -0,0 +1,52 @@
+/* Spurious uninitialized variable warnings, case 2.
+   Taken from cpphash.c (macroexpand) */
+/* { dg-do compile } */
+/* { dg-options "-O -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+struct definition
+{
+  int nargs;
+  int rest_args;
+};
+
+struct cpp_reader;
+
+enum cpp_token
+{
+  CPP_EOF, CPP_POP, CPP_COMMA, CPP_RPAREN
+};
+
+extern enum cpp_token macarg (struct cpp_reader *, int);
+
+void
+macroexpand (struct cpp_reader *pfile, struct definition *defn)
+{
+  int nargs = defn->nargs;
+
+  if (nargs >= 0)
+    {
+      enum cpp_token token;  /* { dg-bogus "token" "uninitialized variable warning" } */
+      int i, rest_args;
+      i = 0;
+      rest_args = 0;
+      do
+	{
+	  if (rest_args)
+	    continue;
+	  if (i < nargs || (nargs == 0 && i == 0))
+	    {
+	      /* if we are working on last arg which absorbs rest of args... */
+	      if (i == nargs - 1 && defn->rest_args)
+		rest_args = 1;
+	      token = macarg (pfile, rest_args);
+	    }
+	  else
+	    token = macarg (pfile, 0);
+	  if (token == CPP_EOF || token == CPP_POP)
+	    return;
+
+	  i++;
+	}
+      while (token == CPP_COMMA);
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-20.c b/gcc/testsuite/gcc.dg/auto-init-uninit-20.c
new file mode 100644
index 000000000000..f533ce9c70e7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-20.c
@@ -0,0 +1,18 @@
+/* Spurious uninitialized variable warnings, from gdb */
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wuninitialized -ftrivial-auto-var-init=zero" } */
+struct os { struct o *o; };
+struct o { struct o *next; struct os *se; };
+void f(struct o *o){
+  struct os *s;
+  if(o) s = o->se;
+  while(o && s == o->se){
+    s++; // here `o' is non-zero and thus s is initialized
+    s == o->se  // `?' is essential, `if' does not trigger the warning
+      ? (o = o->next, o ? s = o->se : 0)
+      : 0;
+  }
+}
+
+
+
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-21.c b/gcc/testsuite/gcc.dg/auto-init-uninit-21.c
new file mode 100644
index 000000000000..6044eab27870
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-21.c
@@ -0,0 +1,33 @@
+/* PR69537, spurious warning because of a missed optimization. */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-short-enums -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+enum clnt_stat {
+ RPC_SUCCESS=0,
+ RPC_CANTENCODEARGS=1,
+};
+ 
+int do_ypcall_tr ();
+ 
+static int
+yp_master (char **outname)
+{
+  // Replacing enum clnt_stat with int avoids the warning.
+  enum clnt_stat result;
+  result = do_ypcall_tr ();
+  if (result != 0)
+    return result;
+  *outname = __builtin_strdup ("foo");
+  return 0;
+}
+ 
+int
+yp_update (void)
+{
+  char *master;
+  int r;
+  if ((r = yp_master (&master)) != 0)
+    return r;
+  __builtin_free (master); /* { dg-bogus "uninitialized" } */
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-22.c b/gcc/testsuite/gcc.dg/auto-init-uninit-22.c
new file mode 100644
index 000000000000..0200d734a2c3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-22.c
@@ -0,0 +1,69 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -Wuninitialized --param vect-max-version-for-alias-checks=20 -ftrivial-auto-var-init=zero" } */
+
+#include <stdint.h>
+
+#define A1  2896 /* (1/sqrt(2))<<12 */
+#define A2  2217
+#define A3  3784
+#define A4 -5352
+
+#define IDCT_TRANSFORM(dest,s0,s1,s2,s3,s4,s5,s6,s7,d0,d1,d2,d3,d4,d5,d6,d7,munge,src) {\
+    const int a0 = (src)[s0] + (src)[s4]; \
+    const int a1 = (src)[s0] - (src)[s4]; \
+    const int a2 = (src)[s2] + (src)[s6]; \
+    const int a3 = (A1*((src)[s2] - (src)[s6])) >> 11; \
+    const int a4 = (src)[s5] + (src)[s3]; \
+    const int a5 = (src)[s5] - (src)[s3]; \
+    const int a6 = (src)[s1] + (src)[s7]; \
+    const int a7 = (src)[s1] - (src)[s7]; \
+    const int b0 = a4 + a6; \
+    const int b1 = (A3*(a5 + a7)) >> 11; \
+    const int b2 = ((A4*a5) >> 11) - b0 + b1; \
+    const int b3 = (A1*(a6 - a4) >> 11) - b2; \
+    const int b4 = ((A2*a7) >> 11) + b3 - b1; \
+    (dest)[d0] = munge(a0+a2   +b0); \
+    (dest)[d1] = munge(a1+a3-a2+b2); \
+    (dest)[d2] = munge(a1-a3+a2+b3); \
+    (dest)[d3] = munge(a0-a2   -b4); \
+    (dest)[d4] = munge(a0-a2   +b4); \
+    (dest)[d5] = munge(a1-a3+a2-b3); \
+    (dest)[d6] = munge(a1+a3-a2-b2); \
+    (dest)[d7] = munge(a0+a2   -b0); \
+}
+
+#define MUNGE_NONE(x) (x)
+#define IDCT_COL(dest,src) IDCT_TRANSFORM(dest,0,8,16,24,32,40,48,56,0,8,16,24,32,40,48,56,MUNGE_NONE,src)
+
+#define MUNGE_ROW(x) (((x) + 0x7F)>>8)
+#define IDCT_ROW(dest,src) IDCT_TRANSFORM(dest,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,MUNGE_ROW,src)
+
+static inline void bink_idct_col(int *dest, const int32_t *src)
+{
+    if ((src[8]|src[16]|src[24]|src[32]|src[40]|src[48]|src[56])==0) {
+        dest[0]  =
+        dest[8]  =
+        dest[16] =
+        dest[24] =
+        dest[32] =
+        dest[40] =
+        dest[48] =
+        dest[56] = src[0];
+    } else {
+        IDCT_COL(dest, src);
+    }
+}
+
+int bink_idct_put_c(uint8_t *dest, int linesize, int32_t *block)
+{
+    int i;
+    int temp[64];
+    for (i = 0; i < 8; i++)
+        bink_idct_col(&temp[i], &block[i]);
+    for (i = 0; i < 8; i++) {
+        IDCT_ROW( (&dest[i*linesize]), (&temp[8*i]) );
+    }
+
+    return 0;
+}
+
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-23.c b/gcc/testsuite/gcc.dg/auto-init-uninit-23.c
new file mode 100644
index 000000000000..7dce8d052e16
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-23.c
@@ -0,0 +1,27 @@
+/* PR tree-optimization/78455 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+int ij;
+
+void
+ql (void)
+{
+  int m5 = 0;
+
+  for (;;)
+  {
+    if (0)
+      for (;;)
+      {
+        int *go;
+        int *t4 = go; /* { dg-warning "is used uninitialized" } */
+
+ l1:
+        *t4 = (*t4 != 0) ? 0 : 2;
+      }
+
+    if (ij != 0)
+      goto l1;
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-24.c b/gcc/testsuite/gcc.dg/auto-init-uninit-24.c
new file mode 100644
index 000000000000..1f6740c123ad
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-24.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-options "-O -Wmaybe-uninitialized -ftrivial-auto-var-init=zero" } */
+
+int foo (int x)
+{
+  int y;
+  if (x)
+    return *(&y + 1); /* { dg-bogus "may be used uninitialized" } */
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-25.c b/gcc/testsuite/gcc.dg/auto-init-uninit-25.c
new file mode 100644
index 000000000000..f36d95f9d279
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-25.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O -Wmaybe-uninitialized -ftrivial-auto-var-init=zero" } */
+
+extern unsigned bar (void);
+extern void quux (void);
+
+unsigned foo (unsigned v)
+{
+  unsigned u;
+  if (v != 1)
+    u = bar ();
+
+  // Prevent the "dom" pass from changing the CFG layout based on the inference
+  // 'if (v != 1) is false then (v != 2) is true'.  (Now it would have to
+  // duplicate the loop in order to do so, which is deemed expensive.)
+  for (int i = 0; i < 10; i++)
+    quux ();
+
+  if (v != 2)
+    return u;       /* { dg-warning "may be used uninitialized" } */
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-26.c b/gcc/testsuite/gcc.dg/auto-init-uninit-26.c
new file mode 100644
index 000000000000..ae97ecfa71e1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-26.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O -Wmaybe-uninitialized -ftrivial-auto-var-init=zero" } */
+
+extern unsigned bar (void);
+extern void quux (void);
+
+unsigned foo (unsigned v)
+{
+  unsigned u;
+  if (v != 100)
+    u = bar ();
+
+  // Prevent the "dom" pass from changing the CFG layout based on the inference
+  // 'if (v != 100) is false then (v < 105) is true'.  (Now it would have to
+  // duplicate the loop in order to do so, which is deemed expensive.)
+  for (int i = 0; i < 10; i++)
+    quux ();
+
+  if (v < 105) /* v == 100 falls into this range.  */
+    return u;       /* { dg-warning "may be used uninitialized" }  */
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-3.c b/gcc/testsuite/gcc.dg/auto-init-uninit-3.c
new file mode 100644
index 000000000000..7ff228501f8d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-3.c
@@ -0,0 +1,33 @@
+/* Spurious uninit variable warnings, case 3.
+   Inspired by cppexp.c (parse_charconst) */
+/* { dg-do compile } */
+/* { dg-options "-O -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+extern void error (char *);
+
+int
+parse_charconst (const char *start, const char *end)
+{
+  int c; /* { dg-bogus "c" "uninitialized variable warning" } */
+  int nchars, retval;
+
+  nchars = 0;
+  retval = 0;
+  while (start < end)
+    {
+      c = *start++;
+      if (c == '\'')
+	break;
+      nchars++;
+      retval += c;
+      retval <<= 8;
+    }
+
+  if (nchars == 0)
+    return 0;
+
+  if (c != '\'')
+    error ("malformed character constant");
+
+  return retval;
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-34.c b/gcc/testsuite/gcc.dg/auto-init-uninit-34.c
new file mode 100644
index 000000000000..98fc366c871f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-34.c
@@ -0,0 +1,58 @@
+/* PR middle-end/10138 - warn for uninitialized arrays passed as const*
+   arguments
+   Verify that passing pointers to uninitialized objects to arguments
+   to functions declared with attribute access is diagnosed where expected.
+   { dg-do compile }
+   { dg-options "-O -Wall -ftrivial-auto-var-init=zero" } */
+
+#define RW(...) __attribute__ ((access (read_write, __VA_ARGS__)))
+
+RW (1) RW (3) void
+f4pi (int*, int*, int*, int*);    // { dg-message "in a call to 'f4pi' declared with attribute 'access \\\(read_write, \[13\]\\\)'" }
+
+
+void nowarn_scalar (void)
+{
+  int i1 = 0, i2, i3 = 1, i4;
+  f4pi (&i1, &i2, &i3, &i4);
+}
+
+void warn_scalar_1 (void)
+{
+  int i1;                         // { dg-message "declared here" }
+  int i2, i3 = 1, i4;
+
+  f4pi (&i1, &i2, &i3, &i4);      // { dg-warning "'i1' may be used uninitialized" }
+}
+
+void warn_scalar_2 (void)
+{
+  int j1 = 0, j2, j4;
+  int j3;
+
+  f4pi (&j1, &j2, &j3, &j4);      // { dg-warning "'j3' may be used uninitialized" }
+}
+
+
+void nowarn_array_init (void)
+{
+  int a1[4] = { 0 }, a2[5], a3[6] = { 0 }, a4[7];
+
+  f4pi (a1, a2, a3, a4);
+}
+
+void warn_array_1 (void)
+{
+  int a1[4];                  // { dg-message "'a1' declared here" }
+  int a2[5], a3[6] = { 0 }, a4[7];
+
+  f4pi (a1, a2, a3, a4);      // { dg-warning "'a1' may be used uninitialized" }
+}
+
+void warn_array_2 (void)
+{
+  int a1[4] = { 0 }, a2[5], a4[7];
+  int a3[6];                  // { dg-message "'a3' declared here" }
+
+  f4pi (a1, a2, a3, a4);      // { dg-warning "'a3' may be used uninitialized" }
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-36.c b/gcc/testsuite/gcc.dg/auto-init-uninit-36.c
new file mode 100644
index 000000000000..64377d380ee9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-36.c
@@ -0,0 +1,238 @@
+/* PR middle-end/10138 - warn for uninitialized arrays passed as const*
+   arguments
+   Verify that passing pointers to uninitialized objects to const
+   arguments to built-ins is diagnosed where expected.
+   { dg-do compile }
+   { dg-options "-O -Wall -ftrivial-auto-var-init=zero" }
+   { dg-require-effective-target alloca } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void* alloca (size_t);
+void* malloc (size_t);
+void* realloc (void*, size_t);
+
+void* memcpy (void*, const void*, size_t);
+char* strcpy (char*, const char*);
+size_t strlen (const char*);
+
+void sink (void*);
+
+void nowarn_array_memcpy (void *d, unsigned n)
+{
+  int a[2];
+  /* Diagnose this?  */
+  memcpy (d, a, n /* Non-constant to avoid folding into MEM_REF.  */);
+}
+
+void nowarn_array_plus_cst_memcpy (void *d, unsigned n)
+{
+  int a[3];
+  /* Diagnose this?  */
+  memcpy (d, a + 1, n);
+}
+
+void nowarn_array_plus_var_memcpy (void *d, unsigned n, int i)
+{
+  int a[4];
+  /* Diagnose this?  */
+  memcpy (d, a + i, n);
+}
+
+void nowarn_array_assign_memcpy (char *d, unsigned n)
+{
+  int a[3];
+  a[1] = 3;
+  memcpy (d, a, n);
+}
+
+void nowarn_array_init_memcpy (char *d, unsigned n)
+{
+  int a[4] = { 0 };
+  memcpy (d, a, n);
+}
+
+void nowarn_array_compound_memcpy (void *d, unsigned n)
+{
+  memcpy (d, (int[2]){ 0 }, n);
+}
+
+void nowarn_struct_assign_memcpy (void *d, unsigned n)
+{
+  struct S { int a, b, c, d; } s;
+  s.b = 1;
+  s.d = 2;
+  memcpy (d, &s, n);
+}
+
+
+void nowarn_array_init_strcpy (char *d[], unsigned n)
+{
+  char a[8] = "012";
+
+  strcpy (d[0], a);
+  strcpy (d[1], a + 1);
+  strcpy (d[1], a + 2);
+  strcpy (d[1], a + 3);
+  strcpy (d[1], a + 4);
+  strcpy (d[1], a + 5);
+  strcpy (d[1], a + 6);
+  strcpy (d[1], a + 7);
+}
+
+
+void nowarn_array_assign_strcpy (char *d[], unsigned n)
+{
+  char a[8];
+  a[0] = '0';
+  a[1] = '1';
+  a[2] = '2';
+  a[3] = '\0';
+
+  strcpy (d[0], a);
+  strcpy (d[1], a + 1);
+  strcpy (d[1], a + 2);
+  strcpy (d[1], a + 3);
+}
+
+void warn_array_plus_cst_strcpy (char *d, unsigned n)
+{
+  char a[8];
+  a[0] = '1';
+  a[1] = '2';
+  a[2] = '3';
+  a[3] = '\0';
+
+  strcpy (d, a + 4);          // { dg-warning "\\\[-Wuninitialized" }
+  strcpy (d, a + 5);          // { dg-warning "\\\[-Wuninitialized" }
+  strcpy (d, a + 6);          // { dg-warning "\\\[-Wuninitialized" }
+  strcpy (d, a + 7);          // { dg-warning "\\\[-Wuninitialized" }
+}
+
+void nowarn_array_plus_var_strcpy (char *d, int i)
+{
+  char a[8];
+  a[0] = '1';
+  a[1] = '2';
+  a[2] = '3';
+  a[3] = '\0';
+
+  strcpy (d, a + i);
+}
+
+
+size_t nowarn_array_assign_strlen (const char *s)
+{
+  char a[8];
+  a[0] = s[0];
+  a[1] = s[1];
+  a[2] = s[2];
+  a[3] = s[3];
+
+  size_t n = 0;
+
+  n += strlen (a);
+  n += strlen (a + 1);
+  n += strlen (a + 2);
+  n += strlen (a + 3);
+  return n;
+}
+
+size_t warn_array_plus_cst_strlen (const char *s)
+{
+  char a[8];
+  a[0] = s[0];
+  a[1] = s[1];
+  a[2] = s[2];
+  a[3] = s[3];
+
+  return strlen (a + 4);      // { dg-warning "\\\[-Wuninitialized" }
+}
+
+size_t nowarn_array_plus_var_strlen (const char *s, int i)
+{
+  char a[8];
+  a[0] = s[0];
+  a[1] = s[1];
+  a[2] = s[2];
+  a[3] = s[3];
+
+  return strlen (a + i);
+}
+
+
+size_t nowarn_alloca_assign_strlen (int i)
+{
+  char *p = (char*)alloca (8);
+  p[i] = '\0';
+  return strlen (p);
+}
+
+size_t nowarn_alloca_escape_strlen (int i)
+{
+  char *p = (char*)alloca (8);
+  sink (p);
+  return strlen (p);
+}
+
+size_t warn_alloca_strlen (void)
+{
+  char *p = (char*)alloca (8);
+  return strlen (p);          // { dg-warning "\\\[-Wuninitialized" }
+}
+
+
+size_t nowarn_malloc_assign_strlen (int i)
+{
+  char *p = (char*)malloc (8);
+  p[i] = '\0';
+  return strlen (p);
+}
+
+size_t nowarn_malloc_escape_strlen (int i)
+{
+  char *p = (char*)malloc (8);
+  sink (p);
+  return strlen (p);
+}
+
+size_t warn_malloc_strlen (void)
+{
+  char *p = (char*)malloc (8);
+  return strlen (p);          // { dg-warning "\\\[-Wuninitialized" }
+}
+
+
+size_t nowarn_realloc_strlen (void *p)
+{
+  char *q = (char*)realloc (p, 8);
+  return strlen (q);
+}
+
+
+size_t nowarn_vla_assign_strlen (int n, int i)
+{
+  char vla[n];
+  vla[i] = '\0';
+  return strlen (vla);
+}
+
+size_t nowarn_vla_strcpy_strlen (int n, const char *s, int i)
+{
+  char vla[n];
+  strcpy (vla, s);
+  return strlen (vla + i);
+}
+
+size_t nowarn_vla_escape_strlen (int n, int i)
+{
+  char vla[n];
+  sink (vla);
+  return strlen (vla);
+}
+
+size_t warn_vla_strlen (unsigned n)
+{
+  char vla[n];
+  return strlen (vla);        // { dg-warning "\\\[-Wuninitialized" }
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-37.c b/gcc/testsuite/gcc.dg/auto-init-uninit-37.c
new file mode 100644
index 000000000000..da3c9db1840a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-37.c
@@ -0,0 +1,154 @@
+/* PR middle-end/10138 - warn for uninitialized arrays passed as const arguments
+   Verify that -Wuninitialized and -Wmaybe-uninitialized trigger (or don't)
+   when passing uninitialized variables by reference to functions declared
+   with or without attribute access and with (or without) const qualified
+   arguments of array, VLA, or pointer types.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0 -ftrivial-auto-var-init=zero" } */
+
+#define NONE    /* none */
+#define RO(...) __attribute__ ((access (read_only, __VA_ARGS__)))
+#define RW(...) __attribute__ ((access (read_write, __VA_ARGS__)))
+#define WO(...) __attribute__ ((access (write_only, __VA_ARGS__)))
+#define X(...)  __attribute__ ((access (none, __VA_ARGS__)))
+
+#define CONCAT(x, y) x ## y
+#define CAT(x, y)    CONCAT (x, y)
+#define UNIQ(pfx)    CAT (pfx, __LINE__)
+
+extern void sink (void*);
+
+
+#define T1(attr, name, type)			\
+  void UNIQ (CAT (test_, name))(void) {		\
+    extern attr void UNIQ (name)(type);		\
+    int x;					\
+    UNIQ (name)(&x);				\
+    sink (&x);					\
+  }
+
+#define T2(attr, name, types)			\
+  void UNIQ (CAT (test_, name))(void) {		\
+    extern attr void UNIQ (name)(types);	\
+    int x;					\
+    UNIQ (name)(1, &x);				\
+    sink (&x);					\
+  }
+
+
+typedef int IA_[];
+typedef const int CIA_[];
+
+T1 (NONE,   fia_,   IA_);
+T1 (NONE,   fcia_,  CIA_);    // { dg-warning "\\\[-Wmaybe-uninitialized" }
+T1 (RO (1), froia_, IA_);     // { dg-warning "\\\[-Wuninitialized" }
+T1 (RW (1), frwia_, IA_);     // { dg-warning "\\\[-Wmaybe-uninitialized" }
+T1 (WO (1), fwoia_, IA_);
+T1 (X (1),  fxia_,  IA_);
+
+
+typedef int IA1[1];
+typedef const int CIA1[1];
+
+T1 (NONE,   fia1,   IA1);
+T1 (NONE,   fcia1,  CIA1);    // { dg-warning "\\\[-Wmaybe-uninitialized" }
+T1 (RO (1), froia1, IA1);     // { dg-warning "\\\[-Wuninitialized" }
+T1 (RW (1), frwia1, IA1);     // { dg-warning "\\\[-Wmaybe-uninitialized" }
+T1 (WO (1), fwoia1, IA1);
+T1 (X (1),  fxia1,  IA1);
+
+
+#define IARS1  int[restrict static 1]
+#define CIARS1 const int[restrict static 1]
+
+T1 (NONE,   fiars1,   IARS1);
+T1 (NONE,   fciars1,  CIARS1);// { dg-warning "\\\[-Wmaybe-uninitialized" }
+T1 (RO (1), froiars1, IARS1); // { dg-warning "\\\[-Wuninitialized" }
+T1 (RW (1), frwiars1, IARS1); // { dg-warning "\\\[-Wmaybe-uninitialized" }
+T1 (WO (1), fwoiars1, IARS1);
+T1 (X (1),  fxiars1,  IARS1);
+
+
+#define IAS1  int[static 1]
+#define CIAS1 const int[static 1]
+
+T1 (NONE,   fias1,   IAS1);
+T1 (NONE,   fcias1,  CIAS1);   // { dg-warning "\\\[-Wmaybe-uninitialized" }
+T1 (RO (1), froias1, IAS1);    // { dg-warning "\\\[-Wuninitialized" }
+T1 (RW (1), frwias1, IAS1);    // { dg-warning "\\\[-Wmaybe-uninitialized" }
+T1 (WO (1), fwoias1, IAS1);
+T1 (X (1),  fxias1,  IAS1);
+
+
+#define IAX  int[*]
+#define CIAX const int[*]
+
+T1 (NONE,   fiax,   IAX);
+T1 (NONE,   fciax,  CIAX);    // { dg-warning "\\\[-Wmaybe-uninitialized" }
+T1 (RO (1), froiax, IAX);     // { dg-warning "\\\[-Wuninitialized" }
+T1 (RW (1), frwiax, IAX);     // { dg-warning "\\\[-Wmaybe-uninitialized" }
+T1 (WO (1), fwoiax, IAX);
+T1 (X (1),  fxiax,  IAX);
+
+
+#define IAN  int n, int[n]
+#define CIAN int n, const int[n]
+
+T2 (NONE,      fian,   IAN);
+T2 (NONE,      fcian,  CIAN); // { dg-warning "\\\[-Wmaybe-uninitialized" }
+T2 (RO (2, 1), froian, IAN);  // { dg-warning "\\\[-Wuninitialized" }
+T2 (RW (2, 1), frwian, IAN);  // { dg-warning "\\\[-Wmaybe-uninitialized" }
+T2 (WO (2, 1), fwoian, IAN);
+T2 (X (2, 1),  fxian,  IAN);
+
+
+typedef int* IP;
+typedef const int* CIP;
+
+T1 (NONE,   fip,   IP);
+T1 (NONE,   fcip,  CIP);     // { dg-warning "\\\[-Wmaybe-uninitialized" }
+T1 (RO (1), froip, IP);      // { dg-warning "\\\[-Wuninitialized" }
+T1 (RW (1), frwip, IP);      // { dg-warning "\\\[-Wmaybe-uninitialized" }
+T1 (WO (1), fwoip, IP);
+T1 (X (1),  fxip,  IP);
+
+
+/* Verify that the notes printed after the warning mention attribute
+   access only when the attribute is explicitly used in the declaration
+   and not otherwise.  */
+
+void test_note_cst_restrict (void)
+{
+  extern void
+    fccar (const char[restrict]);   // { dg-message "by argument 1 of type 'const char\\\[restrict]' to 'fccar'" "note" }
+
+  char a[1];                  // { dg-message "'a' declared here" "note" }
+  fccar (a);                  // { dg-warning "'a' may be used uninitialized" }
+}
+
+void test_note_vla (int n)
+{
+  extern void
+    fccvla (const char[n]);   // { dg-message "by argument 1 of type 'const char\\\[n]' to 'fccvla'" "note" }
+
+  char a[2];                  // { dg-message "'a' declared here" "note" }
+  fccvla (a);                 // { dg-warning "'a' may be used uninitialized" }
+}
+
+void test_note_ro (void)
+{
+  extern RO (1) void
+    frocar (char[restrict]);  // { dg-message "in a call to 'frocar' declared with attribute 'access \\\(read_only, 1\\\)'" "note" }
+
+  char a[3];                  // { dg-message "'a' declared here" "note" }
+  frocar (a);                 // { dg-warning "'a' is used uninitialized" }
+}
+
+void test_note_rw (void)
+{
+  extern RW (1) void
+    frwcar (char[restrict]);  // { dg-message "in a call to 'frwcar' declared with attribute 'access \\\(read_write, 1\\\)'" "note" }
+
+  char a[4];                  // { dg-message "'a' declared here" "note" }
+  frwcar (a);                 // { dg-warning "'a' may be used uninitialized" }
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-4.c b/gcc/testsuite/gcc.dg/auto-init-uninit-4.c
new file mode 100644
index 000000000000..482c837cacb2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-4.c
@@ -0,0 +1,52 @@
+/* Spurious uninit variable warnings, case 4.
+   Simplified version of cppexp.c (cpp_parse_expr).
+
+   This one is really fragile, it gets it right if you take out case
+   1, or if the structure is replaced by an int, or if the structure
+   has fewer members (!) */
+
+/* { dg-do compile } */
+/* { dg-options "-O -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+extern void abort (void);
+
+struct operation {
+    short op;
+    char rprio;
+    char flags;
+    char unsignedp;
+    long value;
+};
+
+extern struct operation cpp_lex (void);
+
+void
+cpp_parse_expr (void)
+{
+  int rprio; /* { dg-bogus "rprio" "uninitialized variable warning PR19833" } */
+  struct operation op;
+
+  for (;;)
+    {
+      op = cpp_lex ();
+
+      switch (op.op)
+	{
+	case 0:
+	  break;
+	case 1:
+	  return;
+	case 2:
+	  rprio = 1;
+	  break;
+	default:
+	  return;
+	}
+
+      if (op.op == 0)
+	return;
+
+      if (rprio != 1)
+	abort();
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-5.c b/gcc/testsuite/gcc.dg/auto-init-uninit-5.c
new file mode 100644
index 000000000000..ada81c912bdd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-5.c
@@ -0,0 +1,39 @@
+/* Spurious uninitialized-variable warnings.  */
+/* Disable jump threading, etc to test compiler analysis.  */
+/* { dg-do compile } */
+/* { dg-options "-O -Wuninitialized -fno-tree-dce -fno-tree-vrp -fno-tree-dominator-opts -ftrivial-auto-var-init=zero" } */
+
+extern void use(int);
+extern void foo(void);
+
+void
+func1(int cond)
+{
+    int x;  /* { dg-bogus "x" "uninitialized variable warning" } */
+
+    if(cond)
+	x = 1;
+
+    foo();
+
+    if(cond)
+	use(x);
+}
+
+void
+func2 (int cond)
+{
+    int x;  /* { dg-bogus "x" "uninitialized variable warning" } */
+    int flag = 0;
+
+    if(cond)
+    {
+	x = 1;
+	flag = 1;
+    }
+
+    foo();
+
+    if(flag)
+	use(x);
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-6.c b/gcc/testsuite/gcc.dg/auto-init-uninit-6.c
new file mode 100644
index 000000000000..e6cba4ef22c0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-6.c
@@ -0,0 +1,47 @@
+/* Spurious uninitialized variable warnings.
+   This one inspired by java/class.c:build_utf8_ref.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+#include <stddef.h>
+
+struct tree
+{
+    struct tree *car;
+    struct tree *cdr;
+    int type, data;
+};
+
+extern void *malloc(size_t);
+
+#define INTEGER_T 1
+#define PTR_T	  2
+
+#define APPEND(TREE, LAST, TYPE, VALUE)				\
+do {								\
+     struct tree *tmp = malloc (sizeof (struct tree));		\
+     tmp->car = 0; tmp->cdr = 0; tmp->type = TYPE;		\
+     tmp->data = VALUE;						\
+     if (TREE->car)						\
+	 LAST->cdr = tmp;					\
+     else							\
+	 TREE->car = tmp;					\
+     LAST = tmp;						\
+} while(0)
+ 
+struct tree *
+make_something(int a, int b, int c)
+{
+    struct tree *rv;
+    struct tree *field;
+
+    rv = malloc (sizeof (struct tree));
+    rv->car = 0;
+
+    APPEND(rv, field, INTEGER_T, a);  /* { dg-bogus "field" "uninitialized variable warning" } */
+    APPEND(rv, field, PTR_T, b);
+    APPEND(rv, field, INTEGER_T, c);
+
+    return rv;
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-8.c b/gcc/testsuite/gcc.dg/auto-init-uninit-8.c
new file mode 100644
index 000000000000..6c298870c679
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-8.c
@@ -0,0 +1,32 @@
+/* Uninitialized variable warning tests...
+   Inspired by part of optabs.c:expand_binop.
+   May be the same as uninit-1.c.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+#include <limits.h>
+
+void
+add_bignums (int *out, int *x, int *y)
+{
+    int p, sum;
+    int carry; /* { dg-bogus "carry" "uninitialized variable warning" } */
+
+    p = 0;
+    for (; *x; x++, y++, out++, p++)
+    {
+	if (p)
+	    sum = *x + *y + carry;
+	else
+	    sum = *x + *y;
+
+	if (sum < 0)
+	{
+	    carry = 1;
+	    sum -= INT_MAX;
+	}
+	else
+	    carry = 0;
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-9.c b/gcc/testsuite/gcc.dg/auto-init-uninit-9.c
new file mode 100644
index 000000000000..9d65493bde2a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-9.c
@@ -0,0 +1,42 @@
+/* Spurious uninitialized variable warnings.  Slight variant on the
+   documented case, inspired by reg-stack.c:record_asm_reg_life.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O -Wuninitialized -ftrivial-auto-var-init=zero" } */
+/* { dg-require-effective-target alloca } */
+
+struct foo
+{
+    int type;
+    struct foo *car;
+    struct foo *cdr;
+    char *data;
+    int data2;
+};
+
+extern void use(struct foo *);
+
+#define CLOBBER 6
+#define PARALLEL 3
+
+void
+func(struct foo *list, int count)
+{
+    int n_clobbers = 0;
+    int i;
+    struct foo **clob_list;   /* { dg-bogus "clob_list" "uninitialized variable warning" } */
+
+    if(list[0].type == PARALLEL)
+    {
+	clob_list = __builtin_alloca(count * sizeof(struct foo *));
+	
+	for(i = 1; i < count; i++)
+	{
+	    if(list[i].type == CLOBBER)
+		clob_list[n_clobbers++] = &list[i];
+	}
+    }
+
+    for(i = 0; i < n_clobbers; i++)
+	use(clob_list[i]);
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-A.c b/gcc/testsuite/gcc.dg/auto-init-uninit-A.c
new file mode 100644
index 000000000000..01fcf59f4504
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-A.c
@@ -0,0 +1,117 @@
+/* Inspired by part of java/parse.y.
+   May be a real bug in CSE. */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall -ftrivial-auto-var-init=zero" } */
+
+struct tree
+{
+    struct tree *car, *cdr, *wfl;
+    int code;
+    struct { unsigned int renp:1;
+      unsigned int rtnp:1;
+      unsigned int rpnp:1; } flags;
+};
+typedef struct tree *tree;
+#define NULL_TREE ((tree)0)
+
+/* Codes */
+enum
+{
+    CALL_EXPR, NEW_ARRAY_EXPR, NEW_CLASS_EXPR, CONVERT_EXPR,
+    ARRAY_REF, CONDITIONAL_EXPR, STRING_CST, EXPR_WITH_FILE_LOCATION
+};
+
+/* Flags */
+#define RESOLVE_EXPRESSION_NAME_P(t) ((t)->flags.renp)
+#define RESOLVE_TYPE_NAME_P(t) ((t)->flags.rtnp)
+#define RESOLVE_PACKAGE_NAME_P(t) ((t)->flags.rpnp)
+
+/* Macros */
+#define EXPR_WFL_QUALIFICATION(t) ((t)->wfl)
+#define QUAL_WFL(t) ((t)->wfl)
+#define EXPR_WFL_NODE(t) ((t)->wfl)
+#define TREE_CODE(t) ((t)->code)
+#define TREE_OPERAND(t,x) ((t)->car)
+#define CLASSTYPE_SUPER(t) ((t)->car)
+#define IDENTIFIER_LOCAL_VALUE(t) ((t)->car)
+#define TREE_CHAIN(t) ((t)->cdr)
+#define QUAL_RESOLUTION(t) ((t)->cdr)
+
+extern tree current_class, this_identifier_node;
+extern tree super_identifier_node, length_identifier_node;
+
+tree resolve_and_layout (tree, tree);
+tree lookup_field_wrapper (tree, tree);
+
+void
+qualify_ambiguous_name (id)
+     tree id;
+{
+  tree qual, qual_wfl, decl;
+  tree name;	 /* { dg-bogus "name" "uninitialized variable warning" } */
+  tree ptr_type; /* { dg-bogus "ptr_type" "uninitialized variable warning" } */
+  int again, new_array_found = 0;
+  int super_found = 0, this_found = 0;
+
+  qual = EXPR_WFL_QUALIFICATION (id);
+  do {
+    qual_wfl = QUAL_WFL (qual);
+    switch (TREE_CODE (qual_wfl))
+      {
+      case CALL_EXPR:
+	qual_wfl = TREE_OPERAND (qual_wfl, 0);
+	if (TREE_CODE (qual_wfl) != EXPR_WITH_FILE_LOCATION)
+	  {
+	    qual = EXPR_WFL_QUALIFICATION (qual_wfl);
+	    qual_wfl = QUAL_WFL (qual);
+	  }
+	break;
+      case NEW_ARRAY_EXPR:
+	qual = TREE_CHAIN (qual);
+	new_array_found = again = 1;
+	continue;
+      case NEW_CLASS_EXPR:
+      case CONVERT_EXPR:
+	qual_wfl = TREE_OPERAND (qual_wfl, 0);
+	break;
+      case ARRAY_REF:
+	while (TREE_CODE (qual_wfl) == ARRAY_REF)
+	  qual_wfl = TREE_OPERAND (qual_wfl, 0);
+	break;
+      default:
+	break;
+      }
+
+    name = EXPR_WFL_NODE (qual_wfl);
+    ptr_type = current_class;
+    again = 0;
+
+  } while (again);
+
+  /* If you put straightforward uses of name and ptr_type here
+     instead of the if-else sequence below, the warnings go away.
+     Therefore I suspect a real bug. */
+  
+  if (!this_found && !super_found && (decl = IDENTIFIER_LOCAL_VALUE (name)))
+    {
+      RESOLVE_EXPRESSION_NAME_P (qual_wfl) = 1;
+      QUAL_RESOLUTION (qual) = decl;
+    }
+  else if ((decl = lookup_field_wrapper (ptr_type, name))
+	   || (new_array_found && name == length_identifier_node))
+    {
+      RESOLVE_EXPRESSION_NAME_P (qual_wfl) = 1;
+      QUAL_RESOLUTION (qual) = (new_array_found ? NULL_TREE : decl);
+    }
+  else if ((decl = resolve_and_layout (name, NULL_TREE)))
+    {
+      RESOLVE_TYPE_NAME_P (qual_wfl) = 1;
+      QUAL_RESOLUTION (qual) = decl;
+    }
+  else if (TREE_CODE (QUAL_WFL (qual)) == CALL_EXPR
+	   || TREE_CODE (QUAL_WFL (qual)) == ARRAY_REF)
+    RESOLVE_EXPRESSION_NAME_P (qual_wfl) = 1;
+  else 
+    RESOLVE_PACKAGE_NAME_P (qual_wfl) = 1;
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-B.c b/gcc/testsuite/gcc.dg/auto-init-uninit-B.c
new file mode 100644
index 000000000000..0b2837127209
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-B.c
@@ -0,0 +1,15 @@
+/* Origin: PR c/179 from Gray Watson <gray@256.com>, adapted as a testcase
+   by Joseph Myers <jsm28@cam.ac.uk>.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wuninitialized -ftrivial-auto-var-init=zero" } */
+extern void foo (int *);
+extern void bar (int);
+
+void
+baz (void)
+{
+  int i;
+  if (i) /* { dg-warning "is used uninitialized" "uninit i warning" } */
+    bar (i);
+  foo (&i);
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-C.c b/gcc/testsuite/gcc.dg/auto-init-uninit-C.c
new file mode 100644
index 000000000000..a4aa629bf930
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-C.c
@@ -0,0 +1,21 @@
+/* Spurious uninitialized variable warning, inspired by libgcc2.c.  */
+/* { dg-do compile } */
+/* { dg-options "-O -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+/* Not all platforms support TImode integers.  */
+#if defined(__LP64__) && !defined(__hppa__)
+typedef int TItype __attribute__ ((mode (TI)));
+#else
+typedef long TItype;
+#endif
+
+
+TItype
+__subvdi3 (TItype a, TItype b)
+{
+  TItype w;
+  
+  w = a - b;
+  
+  return w;
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-H.c b/gcc/testsuite/gcc.dg/auto-init-uninit-H.c
new file mode 100644
index 000000000000..71e2e2d68d7f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-H.c
@@ -0,0 +1,33 @@
+/* PR 14204 */
+/* { dg-do compile } */
+/* { dg-options "-O -Wall -Werror -ftrivial-auto-var-init=zero" } */
+
+#if defined __alpha__
+# define ASM __asm__("$30")
+#elif defined __i386__
+# define ASM __asm__("esp")
+#elif defined (__powerpc__) || defined (__PPC__) || defined (__ppc__) || defined (_POWER)
+# define ASM __asm__("r1")
+#elif defined __s390__
+# define ASM __asm__("r15")
+#elif defined __mips
+# define ASM __asm__("$sp")
+#elif defined __sparc__
+# define ASM __asm__("sp")
+#elif defined __ia64__
+# define ASM __asm__("r12")
+#elif defined __hppa__
+# define ASM __asm__("%r30")
+#elif defined __xtensa__
+# define ASM __asm__("sp")
+#else
+/* The register name should be target-dependent so for other targets,
+   we just silence the test.  */
+# define ASM = 0
+#endif
+
+void *load_PCB (void)
+{
+  register void *sp ASM;
+  return sp;			/* { dg-bogus "uninitialized" } */
+}
diff --git a/gcc/testsuite/gcc.dg/auto-init-uninit-I.c b/gcc/testsuite/gcc.dg/auto-init-uninit-I.c
new file mode 100644
index 000000000000..09680fe9790d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/auto-init-uninit-I.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+int sys_msgctl (void)
+{
+  struct { int mode; } setbuf;
+  return setbuf.mode;  /* { dg-warning "'setbuf\.mode' is used" } */
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-1.c b/gcc/testsuite/gcc.target/aarch64/auto-init-1.c
new file mode 100644
index 000000000000..0fa470880bf5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-1.c
@@ -0,0 +1,32 @@
+/* Verify zero initialization for integer and pointer type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-rtl-expand" } */
+
+#ifndef __cplusplus
+# define bool _Bool
+#endif
+
+enum E {
+  N1 = 0,
+  N2,
+  N3
+};
+
+extern void bar (char, short, int, enum E, long, long long, int *, bool);
+
+void foo()
+{
+  char temp1;
+  short temp2;
+  int temp3;
+  enum E temp4;
+  long temp5;
+  long long temp6;
+  int *temp7;
+  bool temp8;
+
+  bar (temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8);
+  return;
+}
+
+/* { dg-final { scan-rtl-dump-times "const_int 0" 11 "expand" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-10.c b/gcc/testsuite/gcc.target/aarch64/auto-init-10.c
new file mode 100644
index 000000000000..aceceb87fbe1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-10.c
@@ -0,0 +1,18 @@
+/* Verify pattern initialization for structure type automatic variables with
+   padding.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern" } */
+
+struct test_aligned {
+        int internal1;
+        long long internal2;
+} __attribute__ ((aligned(64)));
+
+int foo ()
+{
+  struct test_aligned var;
+  return var.internal1;
+}
+
+/* { dg-final { scan-assembler-times "stp\tq0, q0," 2 } } */
+
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-11.c b/gcc/testsuite/gcc.target/aarch64/auto-init-11.c
new file mode 100644
index 000000000000..085c38629210
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-11.c
@@ -0,0 +1,27 @@
+/* Verify zero initialization for nested structure type automatic variables with
+   padding.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero" } */
+
+struct test_aligned {
+        unsigned internal1;
+        unsigned long long internal2;
+} __attribute__ ((aligned(64)));
+
+struct test_big_hole {
+        char one;
+        char two;
+        char three;
+        /* 61 byte padding hole here. */
+        struct test_aligned four;
+} __attribute__ ((aligned(64)));
+
+
+int foo ()
+{
+  struct test_big_hole var;
+  return var.four.internal1;
+}
+
+/* { dg-final { scan-assembler-times "stp\tq0, q0," 4 } } */
+
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-12.c b/gcc/testsuite/gcc.target/aarch64/auto-init-12.c
new file mode 100644
index 000000000000..5d1383b0b5c8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-12.c
@@ -0,0 +1,27 @@
+/* Verify pattern initialization for nested structure type automatic variables with
+   padding.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern" } */
+
+struct test_aligned {
+        unsigned internal1;
+        unsigned long long internal2;
+} __attribute__ ((aligned(64)));
+
+struct test_big_hole {
+        char one;
+        char two;
+        char three;
+        /* 61 byte padding hole here. */
+        struct test_aligned four;
+} __attribute__ ((aligned(64)));
+
+
+int foo ()
+{
+  struct test_big_hole var;
+  return var.four.internal1;
+}
+
+/* { dg-final { scan-assembler-times "stp\tq0, q0," 4 } } */
+
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-13.c b/gcc/testsuite/gcc.target/aarch64/auto-init-13.c
new file mode 100644
index 000000000000..3c45a6c6288d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-13.c
@@ -0,0 +1,22 @@
+/* Verify zero initialization for structure type automatic variables with
+   tail padding.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero" } */
+
+struct test_trailing_hole {
+        char *one;
+        char *two;
+        char *three;
+        char four;
+        /* "sizeof(unsigned long) - 1" byte padding hole here. */
+};
+
+int foo ()
+{
+  struct test_trailing_hole var;
+  return var.four;
+}
+
+/* { dg-final { scan-assembler-times "stp\txzr, xzr," 2 } } */
+
+
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-14.c b/gcc/testsuite/gcc.target/aarch64/auto-init-14.c
new file mode 100644
index 000000000000..bfef23d52a10
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-14.c
@@ -0,0 +1,20 @@
+/* Verify pattern initialization for structure type automatic variables with
+   tail padding.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+struct test_trailing_hole {
+        char *one;
+        char *two;
+        char *three;
+        char four;
+        /* "sizeof(unsigned long) - 1" byte padding hole here. */
+};
+
+int foo ()
+{
+  struct test_trailing_hole var;
+  return var.four;
+}
+
+/* { dg-final { scan-rtl-dump-times "0xfffffffffffffffe\\\]\\\) repeated x16" 1 "expand" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-15.c b/gcc/testsuite/gcc.target/aarch64/auto-init-15.c
new file mode 100644
index 000000000000..cb96c3a1e4b2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-15.c
@@ -0,0 +1,20 @@
+/* Verify zero initialization for structure type automatic variables with
+   padding and has explicit initialization.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero" } */
+
+struct test_trailing_hole {
+        int one;
+        int two;
+        int three;
+        char four;
+        /* "sizeof(unsigned long) - 1" byte padding hole here. */
+};
+
+int foo ()
+{
+  struct test_trailing_hole var = {.one = 1,.two = 2, .four = 'c'};
+  return var.four;
+}
+
+/* { dg-final { scan-assembler "stp\txzr, xzr," } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-16.c b/gcc/testsuite/gcc.target/aarch64/auto-init-16.c
new file mode 100644
index 000000000000..ce7c7cd31c1f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-16.c
@@ -0,0 +1,22 @@
+/* Verify pattern initialization for structure type automatic variables with
+   padding and has explicit initialization.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern" } */
+
+struct test_trailing_hole {
+        int one;
+        int two;
+        int three;
+        char four;
+        /* "sizeof(unsigned long) - 1" byte padding hole here. */
+};
+
+int foo ()
+{
+  struct test_trailing_hole var = {.one = 1,.two = 2, .four = 'c'};
+  return var.four;
+}
+
+/* { dg-final { scan-assembler "stp\txzr, xzr," } } */
+
+
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-17.c b/gcc/testsuite/gcc.target/aarch64/auto-init-17.c
new file mode 100644
index 000000000000..c81e5ff28b0b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-17.c
@@ -0,0 +1,21 @@
+/* Verify zero initialization for array type with structure element with
+   padding.  */ 
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero" } */
+
+struct test_trailing_hole {
+        int one;
+        int two;
+        int three;
+        char four;
+        /* "sizeof(unsigned long) - 1" byte padding hole here. */
+};
+
+
+int foo ()
+{
+  struct test_trailing_hole var[10]; 
+  return var[2].four;
+}
+
+/* { dg-final { scan-assembler-times "stp\tq0, q0," 5 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-18.c b/gcc/testsuite/gcc.target/aarch64/auto-init-18.c
new file mode 100644
index 000000000000..b0f3acb2f528
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-18.c
@@ -0,0 +1,22 @@
+/* Verify pattern initialization for array type with structure element with
+   padding.  */ 
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+struct test_trailing_hole {
+        int one;
+        int two;
+        int three;
+        char four;
+        /* "sizeof(unsigned long) - 1" byte padding hole here. */
+};
+
+
+int foo ()
+{
+  struct test_trailing_hole var[10]; 
+  return var[2].four;
+}
+
+/* { dg-final { scan-rtl-dump-times "0xfffffffffffffffe\\\]\\\) repeated x16" 1 "expand" } } */
+
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-19.c b/gcc/testsuite/gcc.target/aarch64/auto-init-19.c
new file mode 100644
index 000000000000..02f7edc51008
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-19.c
@@ -0,0 +1,27 @@
+/* Verify zero initialization for union type with structure field with
+   padding.  */ 
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero" } */
+
+struct test_trailing_hole {
+        int one;
+        int two;
+        int three;
+        char four;
+        /* "sizeof(unsigned long) - 1" byte padding hole here. */
+};
+
+union test_union_padding {
+  struct test_trailing_hole u1;
+  long long u2;
+};
+
+
+int foo ()
+{
+  union test_union_padding var;
+  return var.u1.four;
+}
+
+/* { dg-final { scan-assembler "stp\txzr, xzr," } } */
+
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-2.c b/gcc/testsuite/gcc.target/aarch64/auto-init-2.c
new file mode 100644
index 000000000000..2c54e6d60382
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-2.c
@@ -0,0 +1,35 @@
+/* Verify pattern initialization for integer and pointer type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-O -ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+#ifndef __cplusplus
+# define bool _Bool
+#endif
+
+enum E {
+  N1 = 0,
+  N2,
+  N3
+};
+
+extern void bar (char, short, int, enum E, long, long long, int *, bool);
+
+void foo()
+{
+  char temp1;
+  short temp2;
+  int temp3;
+  enum E temp4;
+  long temp5;
+  long long temp6;
+  int *temp7;
+  bool temp8;
+
+  bar (temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8);
+  return;
+}
+
+/* { dg-final { scan-rtl-dump-times "0xfe\\\]" 1 "expand" } } */
+/* { dg-final { scan-rtl-dump-times "0xfffffffffffffefe" 1 "expand" } } */
+/* { dg-final { scan-rtl-dump-times "0xfffffffffefefefe" 2 "expand" } } */
+/* { dg-final { scan-rtl-dump-times "0xfefefefefefefefe" 2 "expand" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-20.c b/gcc/testsuite/gcc.target/aarch64/auto-init-20.c
new file mode 100644
index 000000000000..555972f1728f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-20.c
@@ -0,0 +1,27 @@
+/* Verify pattern initialization for union type with structure field with
+   padding.  */ 
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+struct test_trailing_hole {
+        int one;
+        int two;
+        int three;
+        char four;
+        /* "sizeof(unsigned long) - 1" byte padding hole here. */
+};
+
+union test_union_padding {
+  struct test_trailing_hole u1;
+  long long u2;
+};
+
+
+int foo ()
+{
+  union test_union_padding var;
+  return var.u1.four;
+}
+
+/* { dg-final { scan-rtl-dump-times "0xfffffffffffffffe\\\]\\\) repeated x16" 1 "expand" } } */
+
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-3.c b/gcc/testsuite/gcc.target/aarch64/auto-init-3.c
new file mode 100644
index 000000000000..1adc147a3000
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-3.c
@@ -0,0 +1,17 @@
+/* Verify zero initialization for floating point type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-rtl-expand" } */
+
+long double result;
+
+long double foo()
+{
+  float temp1;
+  double temp2;
+  long double temp3;
+  
+  result = temp1 + temp2 + temp3;
+  return result;
+}
+
+/* { dg-final { scan-rtl-dump-times "const_int 0" 7 "expand" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-4.c b/gcc/testsuite/gcc.target/aarch64/auto-init-4.c
new file mode 100644
index 000000000000..9ee8335065f1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-4.c
@@ -0,0 +1,19 @@
+/* Verify pattern initialization for floating point type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-O -ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+long double result;
+
+long double foo()
+{
+  float temp1;
+  double temp2;
+  long double temp3;
+  
+  result = temp1 + temp2 + temp3;
+  return result;
+}
+
+/* { dg-final { scan-rtl-dump "fefefefe" "expand" } } */
+/* { dg-final { scan-rtl-dump "fefefefefefefefe" "expand" } } */
+/* { dg-final { scan-rtl-dump "fefefefefefefefefefefefefefefefe" "expand" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-5.c b/gcc/testsuite/gcc.target/aarch64/auto-init-5.c
new file mode 100644
index 000000000000..1874d961d5de
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-5.c
@@ -0,0 +1,19 @@
+/* Verify zero initialization for complex type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-rtl-expand" } */
+
+
+_Complex long double result;
+
+_Complex long double foo()
+{
+  _Complex float temp1;
+  _Complex double temp2;
+  _Complex long double temp3;
+
+  result = temp1 + temp2 + temp3;
+  return result;
+}
+
+/* { dg-final { scan-rtl-dump-times "const_int 0" 12 "expand" } } */
+
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-6.c b/gcc/testsuite/gcc.target/aarch64/auto-init-6.c
new file mode 100644
index 000000000000..27c16b336789
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-6.c
@@ -0,0 +1,18 @@
+/* Verify pattern initialization for complex type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+
+_Complex long double result;
+
+_Complex long double foo()
+{
+  _Complex float temp1;
+  _Complex double temp2;
+  _Complex long double temp3;
+
+  result = temp1 + temp2 + temp3;
+  return result;
+}
+
+/* { dg-final { scan-rtl-dump-times "0xfffffffffffffffe\\\]\\\) repeated x16" 3 "expand" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-7.c b/gcc/testsuite/gcc.target/aarch64/auto-init-7.c
new file mode 100644
index 000000000000..ac27fbe92f4a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-7.c
@@ -0,0 +1,32 @@
+/* Verify zero initialization for array, union, and structure type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-rtl-expand" } */
+
+struct S
+{
+  int f1;
+  float f2;
+  char f3[20];
+};
+
+union U
+{
+  char u1[5];
+  int u2;
+  float u3; 
+};
+
+double result;
+
+double foo()
+{
+  int temp1[3];
+  double temp2[3];
+  struct S temp3;
+  union U temp4;
+  
+  result = temp1[2] + temp2[1] + temp3.f2 + temp4.u3;
+  return result;
+}
+
+/* { dg-final { scan-rtl-dump-times "const_int 0" 8 "expand" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-8.c b/gcc/testsuite/gcc.target/aarch64/auto-init-8.c
new file mode 100644
index 000000000000..3943f5a4d6b2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-8.c
@@ -0,0 +1,32 @@
+/* Verify pattern initialization for array, union, and structure type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+struct S
+{
+  int f1;
+  float f2;
+  char f3[20];
+};
+
+union U
+{
+  char u1[5];
+  int u2;
+  float u3; 
+};
+
+double result;
+
+double foo()
+{
+  int temp1[3];
+  double temp2[3];
+  struct S temp3;
+  union U temp4;
+  
+  result = temp1[2] + temp2[1] + temp3.f2 + temp4.u3;
+  return result;
+}
+
+/* { dg-final { scan-rtl-dump-times "0xfffffffffffffffe\\\]\\\) repeated x16" 4 "expand" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/auto-init-9.c b/gcc/testsuite/gcc.target/aarch64/auto-init-9.c
new file mode 100644
index 000000000000..d3a88c724545
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/auto-init-9.c
@@ -0,0 +1,17 @@
+/* Verify zero initialization for structure type automatic variables with
+   padding.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero" } */
+
+struct test_aligned {
+        int internal1;
+        long long internal2;
+} __attribute__ ((aligned(64)));
+
+int foo ()
+{
+  struct test_aligned var;
+  return var.internal1;
+}
+
+/* { dg-final { scan-assembler-times "stp\tq0, q0," 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-1.c b/gcc/testsuite/gcc.target/i386/auto-init-1.c
new file mode 100644
index 000000000000..b7690df24060
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-1.c
@@ -0,0 +1,32 @@
+/* Verify zero initialization for integer and pointer type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-rtl-expand" } */
+
+#ifndef __cplusplus
+# define bool _Bool
+#endif
+
+enum E {
+  N1 = 0,
+  N2,
+  N3
+};
+
+extern void bar (char, short, int, enum E, long, long long, int *, bool);
+
+void foo()
+{
+  char temp1;
+  short temp2;
+  int temp3;
+  enum E temp4;
+  long temp5;
+  long long temp6;
+  int *temp7;
+  bool temp8;
+
+  bar (temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8);
+  return;
+}
+
+/* { dg-final { scan-rtl-dump-times "const_int 0" 10 "expand" } } */
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-10.c b/gcc/testsuite/gcc.target/i386/auto-init-10.c
new file mode 100644
index 000000000000..2dc2d035a6c9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-10.c
@@ -0,0 +1,19 @@
+/* Verify pattern initialization for structure type automatic variables with
+   padding.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+struct test_aligned {
+        int internal1;
+        long long internal2;
+} __attribute__ ((aligned(64)));
+
+int foo ()
+{
+  struct test_aligned var;
+  return var.internal1;
+}
+
+/* { dg-final { scan-rtl-dump-times "0xfefefefefefefefefefefefefefefefe" 4 "expand" } } */
+
+
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-11.c b/gcc/testsuite/gcc.target/i386/auto-init-11.c
new file mode 100644
index 000000000000..8a6d764b9fa4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-11.c
@@ -0,0 +1,30 @@
+/* Verify zero initialization for nested structure type automatic variables with
+   padding.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero" } */
+
+struct test_aligned {
+        unsigned internal1;
+        unsigned long long internal2;
+} __attribute__ ((aligned(64)));
+
+struct test_big_hole {
+        char one;
+        char two;
+        char three;
+        /* 61 byte padding hole here. */
+        struct test_aligned four;
+} __attribute__ ((aligned(64)));
+
+
+int foo ()
+{
+  struct test_big_hole var;
+  return var.four.internal1;
+}
+
+/* { dg-final { scan-assembler "movl\t\\\$0," } } */
+/* { dg-final { scan-assembler "movl\t\\\$16," } } */
+/* { dg-final { scan-assembler "rep stosq" } } */
+
+
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-12.c b/gcc/testsuite/gcc.target/i386/auto-init-12.c
new file mode 100644
index 000000000000..1ad60243aa4c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-12.c
@@ -0,0 +1,28 @@
+/* Verify pattern initialization for nested structure type automatic variables with
+   padding.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+struct test_aligned {
+        unsigned internal1;
+        unsigned long long internal2;
+} __attribute__ ((aligned(64)));
+
+struct test_big_hole {
+        char one;
+        char two;
+        char three;
+        /* 61 byte padding hole here. */
+        struct test_aligned four;
+} __attribute__ ((aligned(64)));
+
+
+int foo ()
+{
+  struct test_big_hole var;
+  return var.four.internal1;
+}
+
+/* { dg-final { scan-rtl-dump-times "0xfefefefefefefefefefefefefefefefe" 8 "expand" } } */
+
+
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-13.c b/gcc/testsuite/gcc.target/i386/auto-init-13.c
new file mode 100644
index 000000000000..e18bc3073e18
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-13.c
@@ -0,0 +1,22 @@
+/* Verify zero initialization for structure type automatic variables with
+   tail padding.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero" } */
+
+struct test_trailing_hole {
+        char *one;
+        char *two;
+        char *three;
+        char four;
+        /* "sizeof(unsigned long) - 1" byte padding hole here. */
+};
+
+int foo ()
+{
+  struct test_trailing_hole var;
+  return var.four;
+}
+
+/* { dg-final { scan-assembler-times "movq\t\\\$0," 4 } } */
+
+
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-14.c b/gcc/testsuite/gcc.target/i386/auto-init-14.c
new file mode 100644
index 000000000000..738a340efb61
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-14.c
@@ -0,0 +1,22 @@
+/* Verify pattern initialization for structure type automatic variables with
+   tail padding.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+struct test_trailing_hole {
+        char *one;
+        char *two;
+        char *three;
+        char four;
+        /* "sizeof(unsigned long) - 1" byte padding hole here. */
+};
+
+int foo ()
+{
+  struct test_trailing_hole var;
+  return var.four;
+}
+
+/* { dg-final { scan-rtl-dump-times "0xfefefefefefefefefefefefefefefefe" 2 "expand" } } */
+
+
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-15.c b/gcc/testsuite/gcc.target/i386/auto-init-15.c
new file mode 100644
index 000000000000..b5abffb0fc66
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-15.c
@@ -0,0 +1,22 @@
+/* Verify zero initialization for structure type automatic variables with
+   padding and has explicit initialization.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero" } */
+
+struct test_trailing_hole {
+        int one;
+        int two;
+        int three;
+        char four;
+        /* "sizeof(unsigned long) - 1" byte padding hole here. */
+};
+
+int foo ()
+{
+  struct test_trailing_hole var = {.one = 1,.two = 2, .four = 'c'};
+  return var.four;
+}
+
+/* { dg-final { scan-assembler-times "movq\t\\\$0," 2 } } */
+
+
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-16.c b/gcc/testsuite/gcc.target/i386/auto-init-16.c
new file mode 100644
index 000000000000..66591cff4488
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-16.c
@@ -0,0 +1,22 @@
+/* Verify pattern initialization for structure type automatic variables with
+   padding and has explicit initialization.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern" } */
+
+struct test_trailing_hole {
+        int one;
+        int two;
+        int three;
+        char four;
+        /* "sizeof(unsigned long) - 1" byte padding hole here. */
+};
+
+int foo ()
+{
+  struct test_trailing_hole var = {.one = 1,.two = 2, .four = 'c'};
+  return var.four;
+}
+
+/* { dg-final { scan-assembler-times "movq\t\\\$0," 2 } } */
+
+
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-17.c b/gcc/testsuite/gcc.target/i386/auto-init-17.c
new file mode 100644
index 000000000000..40fccadf920d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-17.c
@@ -0,0 +1,23 @@
+/* Verify zero initialization for array type with structure element with
+   padding.  */ 
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero" } */
+
+struct test_trailing_hole {
+        int one;
+        int two;
+        int three;
+        char four;
+        /* "sizeof(unsigned long) - 1" byte padding hole here. */
+};
+
+
+int foo ()
+{
+  struct test_trailing_hole var[10]; 
+  return var[2].four;
+}
+
+/* { dg-final { scan-assembler "movl\t\\\$0," } } */
+/* { dg-final { scan-assembler "movl\t\\\$20," } } */
+/* { dg-final { scan-assembler "rep stosq" } } */
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-18.c b/gcc/testsuite/gcc.target/i386/auto-init-18.c
new file mode 100644
index 000000000000..af7a6028c5ce
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-18.c
@@ -0,0 +1,21 @@
+/* Verify pattern initialization for array type with structure element with
+   padding.  */ 
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+struct test_trailing_hole {
+        int one;
+        int two;
+        int three;
+        char four;
+        /* "sizeof(unsigned long) - 1" byte padding hole here. */
+};
+
+
+int foo ()
+{
+  struct test_trailing_hole var[10]; 
+  return var[2].four;
+}
+
+/* { dg-final { scan-rtl-dump-times "0xfefefefefefefefefefefefefefefefe" 10 "expand" } } */
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-19.c b/gcc/testsuite/gcc.target/i386/auto-init-19.c
new file mode 100644
index 000000000000..31a3ee1fe971
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-19.c
@@ -0,0 +1,26 @@
+/* Verify zero initialization for union type with structure field with
+   padding.  */ 
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero" } */
+
+struct test_trailing_hole {
+        int one;
+        int two;
+        int three;
+        char four;
+        /* "sizeof(unsigned long) - 1" byte padding hole here. */
+};
+
+union test_union_padding {
+  struct test_trailing_hole u1;
+  long long u2;
+};
+
+
+int foo ()
+{
+  union test_union_padding var;
+  return var.u1.four;
+}
+
+/* { dg-final { scan-assembler-times "movq\t\\\$0," 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-2.c b/gcc/testsuite/gcc.target/i386/auto-init-2.c
new file mode 100644
index 000000000000..e76fc2565168
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-2.c
@@ -0,0 +1,36 @@
+/* Verify pattern initialization for integer and pointer type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+#ifndef __cplusplus
+# define bool _Bool
+#endif
+
+enum E {
+  N1 = 0,
+  N2,
+  N3
+};
+
+extern void bar (char, short, int, enum E, long, long long, int *, bool);
+
+void foo()
+{
+  char temp1;
+  short temp2;
+  int temp3;
+  enum E temp4;
+  long temp5;
+  long long temp6;
+  int *temp7;
+  bool temp8;
+
+  bar (temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8);
+  return;
+}
+
+/* { dg-final { scan-rtl-dump-times "0xfffffffffffffffe" 2 "expand" } } */
+/* { dg-final { scan-rtl-dump-times "0xfffffffffffffefe" 1 "expand" } } */
+/* { dg-final { scan-rtl-dump-times "0xfffffffffefefefe" 2 "expand" } } */
+/* { dg-final { scan-rtl-dump-times "0xfefefefefefefefe" 3 "expand" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-20.c b/gcc/testsuite/gcc.target/i386/auto-init-20.c
new file mode 100644
index 000000000000..bf9ea5d629a3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-20.c
@@ -0,0 +1,26 @@
+/* Verify pattern initialization for union type with structure field with
+   padding.  */ 
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+struct test_trailing_hole {
+        int one;
+        int two;
+        int three;
+        char four;
+        /* "sizeof(unsigned long) - 1" byte padding hole here. */
+};
+
+union test_union_padding {
+  struct test_trailing_hole u1;
+  long long u2;
+};
+
+
+int foo ()
+{
+  union test_union_padding var;
+  return var.u1.four;
+}
+
+/* { dg-final { scan-rtl-dump-times "0xfefefefefefefefefefefefefefefefe" 1 "expand" } } */
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-21.c b/gcc/testsuite/gcc.target/i386/auto-init-21.c
new file mode 100644
index 000000000000..1192a908f9df
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-21.c
@@ -0,0 +1,14 @@
+/* Verify zero initialization for VLA automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-rtl-expand" } */
+
+extern void bar (int);
+
+void foo(int n)
+{
+  int arr[n];
+  bar (arr[2]);
+  return;
+}
+
+/* { dg-final { scan-rtl-dump "__builtin_memset" "expand" } } */
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-22.c b/gcc/testsuite/gcc.target/i386/auto-init-22.c
new file mode 100644
index 000000000000..431d9664e94f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-22.c
@@ -0,0 +1,14 @@
+/* Verify zero initialization for VLA automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+extern void bar (int);
+
+void foo(int n)
+{
+  int arr[n];
+  bar (arr[2]);
+  return;
+}
+
+/* { dg-final { scan-rtl-dump "__builtin_memset" "expand" } } */
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-23.c b/gcc/testsuite/gcc.target/i386/auto-init-23.c
new file mode 100644
index 000000000000..72094f6bdb92
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-23.c
@@ -0,0 +1,13 @@
+/* Verify the auto initialization of nested VLA.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-rtl-expand" } */
+
+void g(void *);
+
+void foo(int a)
+{
+  int x[a][a];
+  g(x);
+}
+
+/* { dg-final { scan-rtl-dump "__builtin_memset" "expand" } } */
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-24.c b/gcc/testsuite/gcc.target/i386/auto-init-24.c
new file mode 100644
index 000000000000..5cc3a756a1c5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-24.c
@@ -0,0 +1,13 @@
+/* Verify the auto initialization of nested VLA.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+void g(void *);
+
+void foo(int a)
+{
+  int x[a][a];
+  g(x);
+}
+
+/* { dg-final { scan-rtl-dump "__builtin_memset" "expand" } } */
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-3.c b/gcc/testsuite/gcc.target/i386/auto-init-3.c
new file mode 100644
index 000000000000..130e55fe2e84
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-3.c
@@ -0,0 +1,17 @@
+/* Verify zero initialization for floating point type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-rtl-expand" } */
+
+long double result;
+
+long double foo()
+{
+  float temp1;
+  double temp2;
+  long double temp3;
+
+  result = temp1 + temp2 + temp3;
+  return result;
+}
+
+/* { dg-final { scan-rtl-dump-times "const_int 0" 3 "expand" } } */
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-4.c b/gcc/testsuite/gcc.target/i386/auto-init-4.c
new file mode 100644
index 000000000000..55125b537bbd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-4.c
@@ -0,0 +1,20 @@
+/* Verify pattern initialization for floating point type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+long double result;
+
+long double foo()
+{
+  float temp1;
+  double temp2;
+  long double temp3;
+
+  result = temp1 + temp2 + temp3;
+  return result;
+}
+
+/* { dg-final { scan-rtl-dump-times "0xfffffffffefefefe" 1 "expand" } } */
+/* { dg-final { scan-rtl-dump-times "\\\[0xfefefefefefefefe\\\]" 1 "expand" } } */
+/* { dg-final { scan-rtl-dump-times "0xfefefefefefefefefefefefefefefefe" 1 "expand" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-5.c b/gcc/testsuite/gcc.target/i386/auto-init-5.c
new file mode 100644
index 000000000000..06c15bf935b2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-5.c
@@ -0,0 +1,20 @@
+/* Verify zero initialization for complex type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-rtl-expand" } */
+
+
+_Complex long double result;
+
+_Complex long double foo()
+{
+  _Complex float temp1;
+  _Complex double temp2;
+  _Complex long double temp3;
+
+  result = temp1 + temp2 + temp3;
+  return result;
+}
+
+/* { dg-final { scan-rtl-dump-times "const_int 0" 4 "expand" } } */
+
+
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-6.c b/gcc/testsuite/gcc.target/i386/auto-init-6.c
new file mode 100644
index 000000000000..80bd56bcb9a7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-6.c
@@ -0,0 +1,19 @@
+/* Verify pattern initialization for complex type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+
+_Complex long double result;
+
+_Complex long double foo()
+{
+  _Complex float temp1;
+  _Complex double temp2;
+  _Complex long double temp3;
+
+  result = temp1 + temp2 + temp3;
+  return result;
+}
+
+/* { dg-final { scan-rtl-dump-times "\\\[0xfefefefefefefefe\\\]" 1 "expand" } } */
+/* { dg-final { scan-rtl-dump-times "0xfefefefefefefefefefefefefefefefe" 3 "expand" } } */
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-7.c b/gcc/testsuite/gcc.target/i386/auto-init-7.c
new file mode 100644
index 000000000000..e91a024631da
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-7.c
@@ -0,0 +1,32 @@
+/* Verify zero initialization for array, union, and structure type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero -fdump-rtl-expand" } */
+
+struct S
+{
+  int f1;
+  float f2;
+  char f3[20];
+};
+
+union U
+{
+  char u1[5];
+  int u2;
+  float u3; 
+};
+
+double result;
+
+double foo()
+{
+  int temp1[3];
+  double temp2[3];
+  struct S temp3;
+  union U temp4;
+  
+  result = temp1[2] + temp2[1] + temp3.f2 + temp4.u3;
+  return result;
+}
+
+/* { dg-final { scan-rtl-dump-times "const_int 0" 7 "expand" } } */
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-8.c b/gcc/testsuite/gcc.target/i386/auto-init-8.c
new file mode 100644
index 000000000000..55e55be3bdd2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-8.c
@@ -0,0 +1,35 @@
+/* Verify pattern initialization for array, union, and structure type automatic variables.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=pattern -fdump-rtl-expand" } */
+
+struct S
+{
+  int f1;
+  float f2;
+  char f3[20];
+};
+
+union U
+{
+  char u1[5];
+  int u2;
+  float u3; 
+};
+
+double result;
+
+double foo()
+{
+  int temp1[3];
+  double temp2[3];
+  struct S temp3;
+  union U temp4;
+  
+  result = temp1[2] + temp2[1] + temp3.f2 + temp4.u3;
+  return result;
+}
+
+/* { dg-final { scan-rtl-dump-times "0xfffffffffefefefe" 1 "expand" } } */
+/* { dg-final { scan-rtl-dump-times "\\\[0xfefefefefefefefe\\\]" 3 "expand" } } */
+/* { dg-final { scan-rtl-dump-times "0xfefefefefefefefefefefefefefefefe" 3 "expand" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/auto-init-9.c b/gcc/testsuite/gcc.target/i386/auto-init-9.c
new file mode 100644
index 000000000000..b4069797d939
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/auto-init-9.c
@@ -0,0 +1,19 @@
+/* Verify zero initialization for structure type automatic variables with
+   padding.  */
+/* { dg-do compile } */
+/* { dg-options "-ftrivial-auto-var-init=zero" } */
+
+struct test_aligned {
+        int internal1;
+        long long internal2;
+} __attribute__ ((aligned(64)));
+
+int foo ()
+{
+  struct test_aligned var;
+  return var.internal1;
+}
+
+/* { dg-final { scan-assembler-times "movq\t\\\$0," 8 } } */
+
+
diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c
index c73e1cbdda6b..f6dcd142954e 100644
--- a/gcc/tree-cfg.c
+++ b/gcc/tree-cfg.c
@@ -3453,31 +3453,92 @@ verify_gimple_call (gcall *stmt)
 	}
     }
 
+  /* For a call to .DEFERRED_INIT, we should guarantee that the 1st and the 3rd
+     argument is consistent, i.e.:
+     1st argument: SIZE of the DECL;
+     3rd argument: IS_VLA, 0 NO, 1 YES;
+
+     if IS_VLA is false, the 1st argument should be a constant and the same as
+     the size of the LHS.  */
+  if (gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
+    {
+      tree size_of_arg0 = gimple_call_arg (stmt, 0);
+      tree size_of_lhs = TYPE_SIZE_UNIT (TREE_TYPE (lhs));
+      tree is_vla_node = gimple_call_arg (stmt, 2);
+      bool is_vla = (bool) TREE_INT_CST_LOW (is_vla_node);
+
+      if (TREE_CODE (lhs) == SSA_NAME)
+       lhs = SSA_NAME_VAR (lhs);
+
+      poly_uint64 size_from_arg0, size_from_lhs;
+      bool is_constant_size_arg0 = poly_int_tree_p (size_of_arg0,
+						    &size_from_arg0);
+      bool is_constant_size_lhs = poly_int_tree_p (size_of_lhs,
+						   &size_from_lhs);
+      if (!is_vla)
+	{
+	  if (!is_constant_size_arg0)
+	    {
+	      error ("%<DEFFERED_INIT%> calls for non-VLA should have "
+		     "constant size for the first argument");
+	      return true;
+	    }
+	  else if (!is_constant_size_lhs)
+	    {
+	      error ("%<DEFFERED_INIT%> calls for non-VLA should have "
+		     "constant size for the LHS ");
+	      return true;
+	    }
+	  else if (maybe_ne (size_from_arg0, size_from_lhs))
+	    {
+	      error ("%<DEFFERED_INIT%> calls for non-VLA should have same "
+		     "constant size for the first argument and LHS");
+	      return true;
+	    }
+	}
+    }
+
+  /* We will not verify the arguments for the calls to .DEFERRED_INIT.
+     Such call is not a real call, just a placeholder for a later
+     initialization during expand phase.
+     This is mainly to avoid assertion failure for the following
+     case:
+
+     uni_var = .DEFERRED_INIT (var_size, INIT_TYPE, is_vla);
+     foo (&uni_var);
+
+     in the above, the uninitialized auto variable "uni_var" is
+     addressable, therefore should not be in registers, resulting
+     the assertion failure in the following argument verification.  */
+  if (gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
+    return false;
+
   /* ???  The C frontend passes unpromoted arguments in case it
      didn't see a function declaration before the call.  So for now
      leave the call arguments mostly unverified.  Once we gimplify
      unit-at-a-time we have a chance to fix this.  */
 
-  for (i = 0; i < gimple_call_num_args (stmt); ++i)
-    {
-      tree arg = gimple_call_arg (stmt, i);
-      if ((is_gimple_reg_type (TREE_TYPE (arg))
-	   && !is_gimple_val (arg))
-	  || (!is_gimple_reg_type (TREE_TYPE (arg))
-	      && !is_gimple_lvalue (arg)))
-	{
-	  error ("invalid argument to gimple call");
-	  debug_generic_expr (arg);
-	  return true;
-	}
-      if (!is_gimple_reg (arg))
-	{
-	  if (TREE_CODE (arg) == WITH_SIZE_EXPR)
-	    arg = TREE_OPERAND (arg, 0);
-	  if (verify_types_in_gimple_reference (arg, false))
+  else 
+    for (i = 0; i < gimple_call_num_args (stmt); ++i)
+      {
+	tree arg = gimple_call_arg (stmt, i);
+	if ((is_gimple_reg_type (TREE_TYPE (arg))
+	     && !is_gimple_val (arg))
+	    || (!is_gimple_reg_type (TREE_TYPE (arg))
+		&& !is_gimple_lvalue (arg)))
+	  {
+	    error ("invalid argument to gimple call");
+	    debug_generic_expr (arg);
 	    return true;
-	}
-    }
+	  }
+	if (!is_gimple_reg (arg))
+	  {
+	    if (TREE_CODE (arg) == WITH_SIZE_EXPR)
+	      arg = TREE_OPERAND (arg, 0);
+	    if (verify_types_in_gimple_reference (arg, false))
+	      return true;
+	  }
+      }
 
   return false;
 }
diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
index c05d22f3e8f1..35051d7c6b96 100644
--- a/gcc/tree-sra.c
+++ b/gcc/tree-sra.c
@@ -384,6 +384,13 @@ static struct
 
   /* Numbber of components created when splitting aggregate parameters.  */
   int param_reductions_created;
+
+  /* Number of deferred_init calls that are modified.  */
+  int deferred_init;
+
+  /* Number of deferred_init calls that are created by
+     generate_subtree_deferred_init.  */
+  int subtree_deferred_init;
 } sra_stats;
 
 static void
@@ -4096,6 +4103,110 @@ get_repl_default_def_ssa_name (struct access *racc, tree reg_type)
   return get_or_create_ssa_default_def (cfun, racc->replacement_decl);
 }
 
+
+/* Generate statements to call .DEFERRED_INIT to initialize scalar replacements
+   of accesses within a subtree ACCESS; all its children, siblings and their
+   children are to be processed.
+   GSI is a statement iterator used to place the new statements.  */
+static void
+generate_subtree_deferred_init (struct access *access,
+				tree init_type,
+				tree is_vla,
+				gimple_stmt_iterator *gsi,
+				location_t loc)
+{
+  do
+    {
+      if (access->grp_to_be_replaced)
+	{
+	  tree repl = get_access_replacement (access);
+	  gimple *call
+	    = gimple_build_call_internal (IFN_DEFERRED_INIT, 3,
+					  TYPE_SIZE_UNIT (TREE_TYPE (repl)),
+					  init_type, is_vla);
+	  gimple_call_set_lhs (call, repl);
+	  gsi_insert_before (gsi, call, GSI_SAME_STMT);
+	  update_stmt (call);
+	  gimple_set_location (call, loc);
+	  sra_stats.subtree_deferred_init++;
+	}
+      else if (access->grp_to_be_debug_replaced)
+	{
+	  tree drepl = get_access_replacement (access);
+	  tree call = build_call_expr_internal_loc
+		     (UNKNOWN_LOCATION, IFN_DEFERRED_INIT,
+		      TREE_TYPE (drepl), 3,
+		      TYPE_SIZE_UNIT (TREE_TYPE (drepl)),
+		      init_type, is_vla);
+	  gdebug *ds = gimple_build_debug_bind (drepl, call,
+						gsi_stmt (*gsi));
+	  gsi_insert_before (gsi, ds, GSI_SAME_STMT);
+	}
+      if (access->first_child)
+	generate_subtree_deferred_init (access->first_child, init_type,
+					is_vla, gsi, loc);
+
+      access = access ->next_sibling;
+    }
+  while (access);
+}
+
+/* For a call to .DEFERRED_INIT:
+   var = .DEFERRED_INIT (size_of_var, init_type, is_vla);
+   examine the LHS variable VAR and replace it with a scalar replacement if
+   there is one, also replace the RHS call to a call to .DEFERRED_INIT of
+   the corresponding scalar relacement variable.  Examine the subtree and
+   do the scalar replacements in the subtree too.  STMT is the call, GSI is
+   the statment iterator to place newly created statement.  */
+
+static enum assignment_mod_result
+sra_modify_deferred_init (gimple *stmt, gimple_stmt_iterator *gsi)
+{
+  tree lhs = gimple_call_lhs (stmt);
+  tree init_type = gimple_call_arg (stmt, 1);
+  tree is_vla = gimple_call_arg (stmt, 2);
+
+  struct access *lhs_access = get_access_for_expr (lhs);
+  if (!lhs_access)
+    return SRA_AM_NONE;
+
+  location_t loc = gimple_location (stmt);
+
+  if (lhs_access->grp_to_be_replaced)
+    {
+      tree lhs_repl = get_access_replacement (lhs_access);
+      gimple_call_set_lhs (stmt, lhs_repl);
+      tree arg0_repl = TYPE_SIZE_UNIT (TREE_TYPE (lhs_repl));
+      gimple_call_set_arg (stmt, 0, arg0_repl);
+      sra_stats.deferred_init++;
+    }
+  else if (lhs_access->grp_to_be_debug_replaced)
+    {
+      tree lhs_drepl = get_access_replacement (lhs_access);
+      tree call = build_call_expr_internal_loc
+		  (UNKNOWN_LOCATION, IFN_DEFERRED_INIT,
+		  TREE_TYPE (lhs_drepl), 3,
+		  TYPE_SIZE_UNIT (TREE_TYPE (lhs_drepl)),
+		  init_type, is_vla);
+      gdebug *ds = gimple_build_debug_bind (lhs_drepl, call,
+					    gsi_stmt (*gsi));
+      gsi_insert_before (gsi, ds, GSI_SAME_STMT);
+    }
+
+  if (lhs_access->first_child)
+    generate_subtree_deferred_init (lhs_access->first_child,
+				    init_type, is_vla, gsi, loc);
+  if (lhs_access->grp_covered)
+    {
+      unlink_stmt_vdef (stmt);
+      gsi_remove (gsi, true);
+      release_defs (stmt);
+      return SRA_AM_REMOVED;
+    }
+
+  return SRA_AM_MODIFIED;
+}
+
 /* Examine both sides of the assignment statement pointed to by STMT, replace
    them with a scalare replacement if there is one and generate copying of
    replacements if scalarized aggregates have been used in the assignment.  GSI
@@ -4460,17 +4571,27 @@ sra_modify_function_body (void)
 	      break;
 
 	    case GIMPLE_CALL:
-	      /* Operands must be processed before the lhs.  */
-	      for (i = 0; i < gimple_call_num_args (stmt); i++)
+	      /* Handle calls to .DEFERRED_INIT specially.  */
+	      if (gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
 		{
-		  t = gimple_call_arg_ptr (stmt, i);
-		  modified |= sra_modify_expr (t, &gsi, false);
+		  assign_result = sra_modify_deferred_init (stmt, &gsi);
+		  modified |= assign_result == SRA_AM_MODIFIED;
+		  deleted = assign_result == SRA_AM_REMOVED;
 		}
-
-	      if (gimple_call_lhs (stmt))
+	      else
 		{
-		  t = gimple_call_lhs_ptr (stmt);
-		  modified |= sra_modify_expr (t, &gsi, true);
+		  /* Operands must be processed before the lhs.  */
+		  for (i = 0; i < gimple_call_num_args (stmt); i++)
+		    {
+		      t = gimple_call_arg_ptr (stmt, i);
+		      modified |= sra_modify_expr (t, &gsi, false);
+		    }
+
+	      	  if (gimple_call_lhs (stmt))
+		    {
+		      t = gimple_call_lhs_ptr (stmt);
+		      modified |= sra_modify_expr (t, &gsi, true);
+		    }
 		}
 	      break;
 
diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
index 7163438e23d3..68ed16326f33 100644
--- a/gcc/tree-ssa-structalias.c
+++ b/gcc/tree-ssa-structalias.c
@@ -4893,6 +4893,9 @@ find_func_aliases_for_call (struct function *fn, gcall *t)
       && find_func_aliases_for_builtin_call (fn, t))
     return;
 
+  if (gimple_call_internal_p (t, IFN_DEFERRED_INIT))
+    return;
+
   fi = get_fi_for_callee (t);
   if (!in_ipa_mode
       || (fi->decl && fndecl && !fi->is_fn_info))
diff --git a/gcc/tree-ssa-uninit.c b/gcc/tree-ssa-uninit.c
index 99442d7f975c..a155b25c5c23 100644
--- a/gcc/tree-ssa-uninit.c
+++ b/gcc/tree-ssa-uninit.c
@@ -152,6 +152,42 @@ warn_uninit (enum opt_code wc, tree t, tree expr, tree var,
   if (is_gimple_assign (context)
       && gimple_assign_rhs_code (context) == COMPLEX_EXPR)
     return;
+
+  /* Ignore REALPART_EXPR or IMAGPART_EXPR if its operand is a call to
+     .DEFERRED_INIT.  This is for handling the following case correctly:
+
+  1 typedef _Complex float C;
+  2 C foo (int cond)
+  3 {
+  4   C f;
+  5   __imag__ f = 0;
+  6   if (cond)
+  7     {
+  8       __real__ f = 1;
+  9       return f;
+ 10     }
+ 11   return f;
+ 12 }
+
+    with -ftrivial-auto-var-init, compiler will insert the following
+    artificial initialization at line 4:
+  f = .DEFERRED_INIT (f, 2);
+  _1 = REALPART_EXPR <f>;
+
+    without the following special handling, _1 = REALPART_EXPR <f> will
+    be treated as the uninitialized use point, which is incorrect. (the
+    real uninitialized use point is at line 11).  */
+  if (is_gimple_assign (context)
+      && (gimple_assign_rhs_code (context) == REALPART_EXPR
+	  || gimple_assign_rhs_code (context) == IMAGPART_EXPR))
+    {
+      tree v = gimple_assign_rhs1 (context);
+      if (TREE_CODE (TREE_OPERAND (v, 0)) == SSA_NAME
+	  && gimple_call_internal_p (SSA_NAME_DEF_STMT (TREE_OPERAND (v, 0)),
+				     IFN_DEFERRED_INIT))
+	return;
+    }
+
   if (!has_undefined_value_p (t))
     return;
 
@@ -227,6 +263,11 @@ check_defs (ao_ref *ref, tree vdef, void *data_)
   check_defs_data *data = (check_defs_data *)data_;
   gimple *def_stmt = SSA_NAME_DEF_STMT (vdef);
 
+  /* Ignore the vdef if the definition statement is a call
+     to .DEFERRED_INIT function.  */
+  if (gimple_call_internal_p (def_stmt, IFN_DEFERRED_INIT))
+    return false;
+
   /* The ASAN_MARK intrinsic doesn't modify the variable.  */
   if (is_gimple_call (def_stmt)
       && gimple_call_internal_p (def_stmt, IFN_ASAN_MARK))
@@ -642,6 +683,12 @@ warn_uninitialized_vars (bool wmaybe_uninit)
 	  ssa_op_iter op_iter;
 	  tree use;
 
+	  /* The call is an artificial use, will not provide meaningful
+	     error message.  If the result of the call is used somewhere
+	     else, we warn there instead.  */
+	  if (gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
+	    continue;
+
 	  if (is_gimple_debug (stmt))
 	    continue;
 
diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c
index 4cc400d3c2eb..0fba404babe9 100644
--- a/gcc/tree-ssa.c
+++ b/gcc/tree-ssa.c
@@ -1325,6 +1325,46 @@ ssa_undefined_value_p (tree t, bool partial)
   if (gimple_nop_p (def_stmt))
     return true;
 
+  /* The value is undefined if the definition statement is a call
+     to .DEFERRED_INIT function.  */
+  if (gimple_call_internal_p (def_stmt, IFN_DEFERRED_INIT))
+    return true;
+
+  /* The value is partially undefined if the definition statement is
+     a REALPART_EXPR or IMAGPART_EXPR and its operand is defined by
+     the call to .DEFERRED_INIT function.  This is for handling the
+     following case:
+
+  1 typedef _Complex float C;
+  2 C foo (int cond)
+  3 {
+  4   C f;
+  5   __imag__ f = 0;
+  6   if (cond)
+  7     {
+  8       __real__ f = 1;
+  9       return f;
+ 10     }
+ 11   return f;
+ 12 }
+
+    with -ftrivial-auto-var-init, compiler will insert the following
+    artificial initialization:
+  f = .DEFERRED_INIT (f, 2);
+  _1 = REALPART_EXPR <f>;
+
+    we should treat the definition _1 = REALPART_EXPR <f> as undefined.  */
+  if (partial && is_gimple_assign (def_stmt)
+      && (gimple_assign_rhs_code (def_stmt) == REALPART_EXPR
+	  || gimple_assign_rhs_code (def_stmt) == IMAGPART_EXPR))
+    {
+      tree real_imag_part = TREE_OPERAND (gimple_assign_rhs1 (def_stmt), 0);
+      if (TREE_CODE (real_imag_part) == SSA_NAME
+	 && gimple_call_internal_p (SSA_NAME_DEF_STMT (real_imag_part),
+				    IFN_DEFERRED_INIT))
+	return true;
+    }
+
   /* Check if the complex was not only partially defined.  */
   if (partial && is_gimple_assign (def_stmt)
       && gimple_assign_rhs_code (def_stmt) == COMPLEX_EXPR)
-- 
2.27.0


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-07 17:38 [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc Qing Zhao
@ 2021-07-08 13:29 ` Martin Jambor
  2021-07-08 15:00   ` Qing Zhao
  2021-07-08 21:10   ` Qing Zhao
  2021-07-12 17:56 ` Kees Cook
  1 sibling, 2 replies; 25+ messages in thread
From: Martin Jambor @ 2021-07-08 13:29 UTC (permalink / raw)
  To: Qing Zhao, Richard Biener, Richard Sandiford, kees cook
  Cc: gcc-patches Qing Zhao via

Hi,

On Wed, Jul 07 2021, Qing Zhao via Gcc-patches wrote:
> Hi, 
>
> This is the 4th version of the patch for the new security feature for GCC.

I have been following the threads about this feature only very lightly,
so please accept my apologies if my comments are about something which
has been already discussed, but...

[...]

> diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
> index c05d22f3e8f1..35051d7c6b96 100644
> --- a/gcc/tree-sra.c
> +++ b/gcc/tree-sra.c
> @@ -384,6 +384,13 @@ static struct
>  
>    /* Numbber of components created when splitting aggregate parameters.  */
>    int param_reductions_created;
> +
> +  /* Number of deferred_init calls that are modified.  */
> +  int deferred_init;
> +
> +  /* Number of deferred_init calls that are created by
> +     generate_subtree_deferred_init.  */
> +  int subtree_deferred_init;
>  } sra_stats;
>  
>  static void
> @@ -4096,6 +4103,110 @@ get_repl_default_def_ssa_name (struct access *racc, tree reg_type)
>    return get_or_create_ssa_default_def (cfun, racc->replacement_decl);
>  }
>  
> +
> +/* Generate statements to call .DEFERRED_INIT to initialize scalar replacements
> +   of accesses within a subtree ACCESS; all its children, siblings and their
> +   children are to be processed.
> +   GSI is a statement iterator used to place the new statements.  */
> +static void
> +generate_subtree_deferred_init (struct access *access,
> +				tree init_type,
> +				tree is_vla,
> +				gimple_stmt_iterator *gsi,
> +				location_t loc)
> +{
> +  do
> +    {
> +      if (access->grp_to_be_replaced)
> +	{
> +	  tree repl = get_access_replacement (access);
> +	  gimple *call
> +	    = gimple_build_call_internal (IFN_DEFERRED_INIT, 3,
> +					  TYPE_SIZE_UNIT (TREE_TYPE (repl)),
> +					  init_type, is_vla);
> +	  gimple_call_set_lhs (call, repl);
> +	  gsi_insert_before (gsi, call, GSI_SAME_STMT);
> +	  update_stmt (call);
> +	  gimple_set_location (call, loc);
> +	  sra_stats.subtree_deferred_init++;
> +	}
> +      else if (access->grp_to_be_debug_replaced)
> +	{
> +	  tree drepl = get_access_replacement (access);
> +	  tree call = build_call_expr_internal_loc
> +		     (UNKNOWN_LOCATION, IFN_DEFERRED_INIT,
> +		      TREE_TYPE (drepl), 3,
> +		      TYPE_SIZE_UNIT (TREE_TYPE (drepl)),
> +		      init_type, is_vla);
> +	  gdebug *ds = gimple_build_debug_bind (drepl, call,
> +						gsi_stmt (*gsi));
> +	  gsi_insert_before (gsi, ds, GSI_SAME_STMT);

Is handling of grp_to_be_debug_replaced accesses necessary here?  If so,
why?  grp_to_be_debug_replaced accesses are there only to facilitate
debug information about a part of an aggregate decl is that is likely
going to be entirely removed - so that debuggers can sometimes show to
users information about what they would contain had they not removed.
It seems strange you need to mark them as uninitialized because they
should not have any consumers.  (But perhaps it is also harmless.)

On a related note, if the intent of the feature is for optimizers to
behave (almost?) as if it was not taking place, I believe you need to
handle specially, and probably just ignore, calls to IFN_DEFERRED_INIT
in scan_function in tree-sra.c.  Otherwise the generated SRA access
structures will have extra write flags turned on in them and that will
lead to different behavior of the pass.

Martin



> +	}
> +      if (access->first_child)
> +	generate_subtree_deferred_init (access->first_child, init_type,
> +					is_vla, gsi, loc);
> +
> +      access = access ->next_sibling;
> +    }
> +  while (access);
> +}
> +
> +/* For a call to .DEFERRED_INIT:
> +   var = .DEFERRED_INIT (size_of_var, init_type, is_vla);
> +   examine the LHS variable VAR and replace it with a scalar replacement if
> +   there is one, also replace the RHS call to a call to .DEFERRED_INIT of
> +   the corresponding scalar relacement variable.  Examine the subtree and
> +   do the scalar replacements in the subtree too.  STMT is the call, GSI is
> +   the statment iterator to place newly created statement.  */
> +
> +static enum assignment_mod_result
> +sra_modify_deferred_init (gimple *stmt, gimple_stmt_iterator *gsi)
> +{
> +  tree lhs = gimple_call_lhs (stmt);
> +  tree init_type = gimple_call_arg (stmt, 1);
> +  tree is_vla = gimple_call_arg (stmt, 2);
> +
> +  struct access *lhs_access = get_access_for_expr (lhs);
> +  if (!lhs_access)
> +    return SRA_AM_NONE;
> +
> +  location_t loc = gimple_location (stmt);
> +
> +  if (lhs_access->grp_to_be_replaced)
> +    {
> +      tree lhs_repl = get_access_replacement (lhs_access);
> +      gimple_call_set_lhs (stmt, lhs_repl);
> +      tree arg0_repl = TYPE_SIZE_UNIT (TREE_TYPE (lhs_repl));
> +      gimple_call_set_arg (stmt, 0, arg0_repl);
> +      sra_stats.deferred_init++;
> +    }
> +  else if (lhs_access->grp_to_be_debug_replaced)
> +    {
> +      tree lhs_drepl = get_access_replacement (lhs_access);
> +      tree call = build_call_expr_internal_loc
> +		  (UNKNOWN_LOCATION, IFN_DEFERRED_INIT,
> +		  TREE_TYPE (lhs_drepl), 3,
> +		  TYPE_SIZE_UNIT (TREE_TYPE (lhs_drepl)),
> +		  init_type, is_vla);
> +      gdebug *ds = gimple_build_debug_bind (lhs_drepl, call,
> +					    gsi_stmt (*gsi));
> +      gsi_insert_before (gsi, ds, GSI_SAME_STMT);
> +    }
> +
> +  if (lhs_access->first_child)
> +    generate_subtree_deferred_init (lhs_access->first_child,
> +				    init_type, is_vla, gsi, loc);
> +  if (lhs_access->grp_covered)
> +    {
> +      unlink_stmt_vdef (stmt);
> +      gsi_remove (gsi, true);
> +      release_defs (stmt);
> +      return SRA_AM_REMOVED;
> +    }
> +
> +  return SRA_AM_MODIFIED;
> +}
> +
>  /* Examine both sides of the assignment statement pointed to by STMT, replace
>     them with a scalare replacement if there is one and generate copying of
>     replacements if scalarized aggregates have been used in the assignment.  GSI
> @@ -4460,17 +4571,27 @@ sra_modify_function_body (void)
>  	      break;
>  
>  	    case GIMPLE_CALL:
> -	      /* Operands must be processed before the lhs.  */
> -	      for (i = 0; i < gimple_call_num_args (stmt); i++)
> +	      /* Handle calls to .DEFERRED_INIT specially.  */
> +	      if (gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
>  		{
> -		  t = gimple_call_arg_ptr (stmt, i);
> -		  modified |= sra_modify_expr (t, &gsi, false);
> +		  assign_result = sra_modify_deferred_init (stmt, &gsi);
> +		  modified |= assign_result == SRA_AM_MODIFIED;
> +		  deleted = assign_result == SRA_AM_REMOVED;
>  		}
> -
> -	      if (gimple_call_lhs (stmt))
> +	      else
>  		{
> -		  t = gimple_call_lhs_ptr (stmt);
> -		  modified |= sra_modify_expr (t, &gsi, true);
> +		  /* Operands must be processed before the lhs.  */
> +		  for (i = 0; i < gimple_call_num_args (stmt); i++)
> +		    {
> +		      t = gimple_call_arg_ptr (stmt, i);
> +		      modified |= sra_modify_expr (t, &gsi, false);
> +		    }
> +
> +	      	  if (gimple_call_lhs (stmt))
> +		    {
> +		      t = gimple_call_lhs_ptr (stmt);
> +		      modified |= sra_modify_expr (t, &gsi, true);
> +		    }
>  		}
>  	      break;
>  

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-08 13:29 ` Martin Jambor
@ 2021-07-08 15:00   ` Qing Zhao
  2021-07-08 21:10   ` Qing Zhao
  1 sibling, 0 replies; 25+ messages in thread
From: Qing Zhao @ 2021-07-08 15:00 UTC (permalink / raw)
  To: Martin Jambor, Richard Sandiford
  Cc: Richard Biener, kees cook, gcc-patches Qing Zhao via

Hi, Martin,

Thank you for the review and comment.

On Jul 8, 2021, at 8:29 AM, Martin Jambor <mjambor@suse.cz<mailto:mjambor@suse.cz>> wrote:

diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
index c05d22f3e8f1..35051d7c6b96 100644
--- a/gcc/tree-sra.c
+++ b/gcc/tree-sra.c
@@ -384,6 +384,13 @@ static struct

  /* Numbber of components created when splitting aggregate parameters.  */
  int param_reductions_created;
+
+  /* Number of deferred_init calls that are modified.  */
+  int deferred_init;
+
+  /* Number of deferred_init calls that are created by
+     generate_subtree_deferred_init.  */
+  int subtree_deferred_init;
} sra_stats;

static void
@@ -4096,6 +4103,110 @@ get_repl_default_def_ssa_name (struct access *racc, tree reg_type)
  return get_or_create_ssa_default_def (cfun, racc->replacement_decl);
}

+
+/* Generate statements to call .DEFERRED_INIT to initialize scalar replacements
+   of accesses within a subtree ACCESS; all its children, siblings and their
+   children are to be processed.
+   GSI is a statement iterator used to place the new statements.  */
+static void
+generate_subtree_deferred_init (struct access *access,
+ tree init_type,
+ tree is_vla,
+ gimple_stmt_iterator *gsi,
+ location_t loc)
+{
+  do
+    {
+      if (access->grp_to_be_replaced)
+ {
+  tree repl = get_access_replacement (access);
+  gimple *call
+    = gimple_build_call_internal (IFN_DEFERRED_INIT, 3,
+  TYPE_SIZE_UNIT (TREE_TYPE (repl)),
+  init_type, is_vla);
+  gimple_call_set_lhs (call, repl);
+  gsi_insert_before (gsi, call, GSI_SAME_STMT);
+  update_stmt (call);
+  gimple_set_location (call, loc);
+  sra_stats.subtree_deferred_init++;
+ }
+      else if (access->grp_to_be_debug_replaced)
+ {
+  tree drepl = get_access_replacement (access);
+  tree call = build_call_expr_internal_loc
+     (UNKNOWN_LOCATION, IFN_DEFERRED_INIT,
+      TREE_TYPE (drepl), 3,
+      TYPE_SIZE_UNIT (TREE_TYPE (drepl)),
+      init_type, is_vla);
+  gdebug *ds = gimple_build_debug_bind (drepl, call,
+ gsi_stmt (*gsi));
+  gsi_insert_before (gsi, ds, GSI_SAME_STMT);

Is handling of grp_to_be_debug_replaced accesses necessary here?  If so,
why?  grp_to_be_debug_replaced accesses are there only to facilitate
debug information about a part of an aggregate decl is that is likely
going to be entirely removed - so that debuggers can sometimes show to
users information about what they would contain had they not removed.
It seems strange you need to mark them as uninitialized because they
should not have any consumers.  (But perhaps it is also harmless.)

This part has been discussed during the 2nd version of the patch, but I think that more discussion might be necessary.

In the previous discussion, Richard Sandiford mentioned: (https://gcc.gnu.org/pipermail/gcc-patches/2021-April/568620.html):

=====

I guess the thing we need to decide here is whether -ftrivial-auto-var-init
should affect debug-only constructs too.  If it doesn't, exmaining removed
components in a debugger might show uninitialised values in cases where
the user was expecting initialised ones.  There would be no security
concern, but it might be surprising.

I think in principle the DRHS can contain a call to DEFERRED_INIT.
Doing that would probably require further handling elsewhere though.

=====

I am still not very confident now for this part of the change.

My questions:

1. If we don’t handle grp_to_be_debug_replaced at all, what will happen?  ( the user of the debugger will see uninitialized values in
the removed part of the aggregate?  Or something else?)
2. On the other hand, if we handle grp_to_be_debug_replaced as the current patch, what will the user of the debugger see?



On a related note, if the intent of the feature is for optimizers to
behave (almost?) as if it was not taking place,

What’s you mean by “it” here?
I believe you need to
handle specially, and probably just ignore, calls to IFN_DEFERRED_INIT
in scan_function in tree-sra.c.

Do you mean to let tree-sra phase ignore IFN_DEFERRED_INIT calls completely?

My major purpose of change tree-sra.c phase is:

Change:

tmp = .DEFERRED_INIT (24, 2, 0)

To

tmp1 = .DEFERRED_INIT (8, 2, 0);
tmp2 = .DEFERRED_INIT (8, 2, 0);
tmp3 = .DEFERRED_INIT (8, 2, 0);

Doing this is to reduce the stack usage.

 Otherwise the generated SRA access
structures will have extra write flags turned on in them and that will
lead to different behavior of the pass.

Could you please explain this more?

thanks.

Qing

Martin



+ }
+      if (access->first_child)
+ generate_subtree_deferred_init (access->first_child, init_type,
+ is_vla, gsi, loc);
+
+      access = access ->next_sibling;
+    }
+  while (access);
+}
+
+/* For a call to .DEFERRED_INIT:
+   var = .DEFERRED_INIT (size_of_var, init_type, is_vla);
+   examine the LHS variable VAR and replace it with a scalar replacement if
+   there is one, also replace the RHS call to a call to .DEFERRED_INIT of
+   the corresponding scalar relacement variable.  Examine the subtree and
+   do the scalar replacements in the subtree too.  STMT is the call, GSI is
+   the statment iterator to place newly created statement.  */
+
+static enum assignment_mod_result
+sra_modify_deferred_init (gimple *stmt, gimple_stmt_iterator *gsi)
+{
+  tree lhs = gimple_call_lhs (stmt);
+  tree init_type = gimple_call_arg (stmt, 1);
+  tree is_vla = gimple_call_arg (stmt, 2);
+
+  struct access *lhs_access = get_access_for_expr (lhs);
+  if (!lhs_access)
+    return SRA_AM_NONE;
+
+  location_t loc = gimple_location (stmt);
+
+  if (lhs_access->grp_to_be_replaced)
+    {
+      tree lhs_repl = get_access_replacement (lhs_access);
+      gimple_call_set_lhs (stmt, lhs_repl);
+      tree arg0_repl = TYPE_SIZE_UNIT (TREE_TYPE (lhs_repl));
+      gimple_call_set_arg (stmt, 0, arg0_repl);
+      sra_stats.deferred_init++;
+    }
+  else if (lhs_access->grp_to_be_debug_replaced)
+    {
+      tree lhs_drepl = get_access_replacement (lhs_access);
+      tree call = build_call_expr_internal_loc
+  (UNKNOWN_LOCATION, IFN_DEFERRED_INIT,
+  TREE_TYPE (lhs_drepl), 3,
+  TYPE_SIZE_UNIT (TREE_TYPE (lhs_drepl)),
+  init_type, is_vla);
+      gdebug *ds = gimple_build_debug_bind (lhs_drepl, call,
+    gsi_stmt (*gsi));
+      gsi_insert_before (gsi, ds, GSI_SAME_STMT);
+    }
+
+  if (lhs_access->first_child)
+    generate_subtree_deferred_init (lhs_access->first_child,
+    init_type, is_vla, gsi, loc);
+  if (lhs_access->grp_covered)
+    {
+      unlink_stmt_vdef (stmt);
+      gsi_remove (gsi, true);
+      release_defs (stmt);
+      return SRA_AM_REMOVED;
+    }
+
+  return SRA_AM_MODIFIED;
+}
+
/* Examine both sides of the assignment statement pointed to by STMT, replace
   them with a scalare replacement if there is one and generate copying of
   replacements if scalarized aggregates have been used in the assignment.  GSI
@@ -4460,17 +4571,27 @@ sra_modify_function_body (void)
     break;

   case GIMPLE_CALL:
-      /* Operands must be processed before the lhs.  */
-      for (i = 0; i < gimple_call_num_args (stmt); i++)
+      /* Handle calls to .DEFERRED_INIT specially.  */
+      if (gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
{
-  t = gimple_call_arg_ptr (stmt, i);
-  modified |= sra_modify_expr (t, &gsi, false);
+  assign_result = sra_modify_deferred_init (stmt, &gsi);
+  modified |= assign_result == SRA_AM_MODIFIED;
+  deleted = assign_result == SRA_AM_REMOVED;
}
-
-      if (gimple_call_lhs (stmt))
+      else
{
-  t = gimple_call_lhs_ptr (stmt);
-  modified |= sra_modify_expr (t, &gsi, true);
+  /* Operands must be processed before the lhs.  */
+  for (i = 0; i < gimple_call_num_args (stmt); i++)
+    {
+      t = gimple_call_arg_ptr (stmt, i);
+      modified |= sra_modify_expr (t, &gsi, false);
+    }
+
+        if (gimple_call_lhs (stmt))
+    {
+      t = gimple_call_lhs_ptr (stmt);
+      modified |= sra_modify_expr (t, &gsi, true);
+    }
}
     break;



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-08 13:29 ` Martin Jambor
  2021-07-08 15:00   ` Qing Zhao
@ 2021-07-08 21:10   ` Qing Zhao
  2021-07-09 16:18     ` Martin Jambor
  1 sibling, 1 reply; 25+ messages in thread
From: Qing Zhao @ 2021-07-08 21:10 UTC (permalink / raw)
  To: Martin Jambor
  Cc: Richard Biener, Richard Sandiford, kees cook, gcc-patches Qing Zhao via

(Resend this email since the previous one didn’t quote, I changed one setting in my mail client, hopefully that can fix this issue).

Hi, Martin,

Thank you for the review and comment.

> On Jul 8, 2021, at 8:29 AM, Martin Jambor <mjambor@suse.cz> wrote:
>> diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
>> index c05d22f3e8f1..35051d7c6b96 100644
>> --- a/gcc/tree-sra.c
>> +++ b/gcc/tree-sra.c
>> @@ -384,6 +384,13 @@ static struct
>> 
>>   /* Numbber of components created when splitting aggregate parameters.  */
>>   int param_reductions_created;
>> +
>> +  /* Number of deferred_init calls that are modified.  */
>> +  int deferred_init;
>> +
>> +  /* Number of deferred_init calls that are created by
>> +     generate_subtree_deferred_init.  */
>> +  int subtree_deferred_init;
>> } sra_stats;
>> 
>> static void
>> @@ -4096,6 +4103,110 @@ get_repl_default_def_ssa_name (struct access *racc, tree reg_type)
>>   return get_or_create_ssa_default_def (cfun, racc->replacement_decl);
>> }
>> 
>> +
>> +/* Generate statements to call .DEFERRED_INIT to initialize scalar replacements
>> +   of accesses within a subtree ACCESS; all its children, siblings and their
>> +   children are to be processed.
>> +   GSI is a statement iterator used to place the new statements.  */
>> +static void
>> +generate_subtree_deferred_init (struct access *access,
>> +				tree init_type,
>> +				tree is_vla,
>> +				gimple_stmt_iterator *gsi,
>> +				location_t loc)
>> +{
>> +  do
>> +    {
>> +      if (access->grp_to_be_replaced)
>> +	{
>> +	  tree repl = get_access_replacement (access);
>> +	  gimple *call
>> +	    = gimple_build_call_internal (IFN_DEFERRED_INIT, 3,
>> +					  TYPE_SIZE_UNIT (TREE_TYPE (repl)),
>> +					  init_type, is_vla);
>> +	  gimple_call_set_lhs (call, repl);
>> +	  gsi_insert_before (gsi, call, GSI_SAME_STMT);
>> +	  update_stmt (call);
>> +	  gimple_set_location (call, loc);
>> +	  sra_stats.subtree_deferred_init++;
>> +	}
>> +      else if (access->grp_to_be_debug_replaced)
>> +	{
>> +	  tree drepl = get_access_replacement (access);
>> +	  tree call = build_call_expr_internal_loc
>> +		     (UNKNOWN_LOCATION, IFN_DEFERRED_INIT,
>> +		      TREE_TYPE (drepl), 3,
>> +		      TYPE_SIZE_UNIT (TREE_TYPE (drepl)),
>> +		      init_type, is_vla);
>> +	  gdebug *ds = gimple_build_debug_bind (drepl, call,
>> +						gsi_stmt (*gsi));
>> +	  gsi_insert_before (gsi, ds, GSI_SAME_STMT);
> 
> Is handling of grp_to_be_debug_replaced accesses necessary here?  If so,
> why?  grp_to_be_debug_replaced accesses are there only to facilitate
> debug information about a part of an aggregate decl is that is likely
> going to be entirely removed - so that debuggers can sometimes show to
> users information about what they would contain had they not removed.
> It seems strange you need to mark them as uninitialized because they
> should not have any consumers.  (But perhaps it is also harmless.)

This part has been discussed during the 2nd version of the patch, but I think that more discussion might be necessary.

In the previous discussion, Richard Sandiford mentioned: (https://gcc.gnu.org/pipermail/gcc-patches/2021-April/568620.html):

=====

I guess the thing we need to decide here is whether -ftrivial-auto-var-init
should affect debug-only constructs too.  If it doesn't, exmaining removed
components in a debugger might show uninitialised values in cases where
the user was expecting initialised ones.  There would be no security
concern, but it might be surprising.

I think in principle the DRHS can contain a call to DEFERRED_INIT.
Doing that would probably require further handling elsewhere though.

=====

I am still not very confident now for this part of the change.

My questions:

1. If we don’t handle grp_to_be_debug_replaced at all, what will happen?  ( the user of the debugger will see uninitialized values in
the removed part of the aggregate?  Or something else?)
2. On the other hand, if we handle grp_to_be_debug_replaced as the current patch, what will the user of the debugger see?

> 
> On a related note, if the intent of the feature is for optimizers to
> behave (almost?) as if it was not taking place,

What’s you mean by “it” here?

> I believe you need to
> handle specially, and probably just ignore, calls to IFN_DEFERRED_INIT
> in scan_function in tree-sra.c.


Do you mean to let tree-sra phase ignore IFN_DEFERRED_INIT calls completely?

My major purpose of change tree-sra.c phase is:

Change:

tmp = .DEFERRED_INIT (24, 2, 0)

To

tmp1 = .DEFERRED_INIT (8, 2, 0);
tmp2 = .DEFERRED_INIT (8, 2, 0);
tmp3 = .DEFERRED_INIT (8, 2, 0);

Doing this is to reduce the stack usage.


>  Otherwise the generated SRA access
> structures will have extra write flags turned on in them and that will
> lead to different behavior of the pass.

Could you please explain this more?

Thanks.

Qing


> 
> Martin
> 
> 


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-08 21:10   ` Qing Zhao
@ 2021-07-09 16:18     ` Martin Jambor
  2021-07-09 18:52       ` Qing Zhao
  2021-07-12  7:51       ` Richard Sandiford
  0 siblings, 2 replies; 25+ messages in thread
From: Martin Jambor @ 2021-07-09 16:18 UTC (permalink / raw)
  To: Qing Zhao
  Cc: Richard Biener, Richard Sandiford, kees cook, gcc-patches Qing Zhao via

Hi,

On Thu, Jul 08 2021, Qing Zhao wrote:
> (Resend this email since the previous one didn’t quote, I changed one
> setting in my mail client, hopefully that can fix this issue).
>
> Hi, Martin,
>
> Thank you for the review and comment.
>
>> On Jul 8, 2021, at 8:29 AM, Martin Jambor <mjambor@suse.cz> wrote:
>>> diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
>>> index c05d22f3e8f1..35051d7c6b96 100644
>>> --- a/gcc/tree-sra.c
>>> +++ b/gcc/tree-sra.c
>>> @@ -384,6 +384,13 @@ static struct
>>> 
>>>   /* Numbber of components created when splitting aggregate parameters.  */
>>>   int param_reductions_created;
>>> +
>>> +  /* Number of deferred_init calls that are modified.  */
>>> +  int deferred_init;
>>> +
>>> +  /* Number of deferred_init calls that are created by
>>> +     generate_subtree_deferred_init.  */
>>> +  int subtree_deferred_init;
>>> } sra_stats;
>>> 
>>> static void
>>> @@ -4096,6 +4103,110 @@ get_repl_default_def_ssa_name (struct access *racc, tree reg_type)
>>>   return get_or_create_ssa_default_def (cfun, racc->replacement_decl);
>>> }
>>> 
>>> +
>>> +/* Generate statements to call .DEFERRED_INIT to initialize scalar replacements
>>> +   of accesses within a subtree ACCESS; all its children, siblings and their
>>> +   children are to be processed.
>>> +   GSI is a statement iterator used to place the new statements.  */
>>> +static void
>>> +generate_subtree_deferred_init (struct access *access,
>>> +				tree init_type,
>>> +				tree is_vla,
>>> +				gimple_stmt_iterator *gsi,
>>> +				location_t loc)
>>> +{
>>> +  do
>>> +    {
>>> +      if (access->grp_to_be_replaced)
>>> +	{
>>> +	  tree repl = get_access_replacement (access);
>>> +	  gimple *call
>>> +	    = gimple_build_call_internal (IFN_DEFERRED_INIT, 3,
>>> +					  TYPE_SIZE_UNIT (TREE_TYPE (repl)),
>>> +					  init_type, is_vla);
>>> +	  gimple_call_set_lhs (call, repl);
>>> +	  gsi_insert_before (gsi, call, GSI_SAME_STMT);
>>> +	  update_stmt (call);
>>> +	  gimple_set_location (call, loc);
>>> +	  sra_stats.subtree_deferred_init++;
>>> +	}
>>> +      else if (access->grp_to_be_debug_replaced)
>>> +	{
>>> +	  tree drepl = get_access_replacement (access);
>>> +	  tree call = build_call_expr_internal_loc
>>> +		     (UNKNOWN_LOCATION, IFN_DEFERRED_INIT,
>>> +		      TREE_TYPE (drepl), 3,
>>> +		      TYPE_SIZE_UNIT (TREE_TYPE (drepl)),
>>> +		      init_type, is_vla);
>>> +	  gdebug *ds = gimple_build_debug_bind (drepl, call,
>>> +						gsi_stmt (*gsi));
>>> +	  gsi_insert_before (gsi, ds, GSI_SAME_STMT);
>> 
>> Is handling of grp_to_be_debug_replaced accesses necessary here?  If so,
>> why?  grp_to_be_debug_replaced accesses are there only to facilitate
>> debug information about a part of an aggregate decl is that is likely
>> going to be entirely removed - so that debuggers can sometimes show to
>> users information about what they would contain had they not removed.
>> It seems strange you need to mark them as uninitialized because they
>> should not have any consumers.  (But perhaps it is also harmless.)
>
> This part has been discussed during the 2nd version of the patch, but
> I think that more discussion might be necessary.
>
> In the previous discussion, Richard Sandiford mentioned:
> (https://gcc.gnu.org/pipermail/gcc-patches/2021-April/568620.html):
>
> =====
>
> I guess the thing we need to decide here is whether -ftrivial-auto-var-init
> should affect debug-only constructs too.  If it doesn't, exmaining removed
> components in a debugger might show uninitialised values in cases where
> the user was expecting initialised ones.  There would be no security
> concern, but it might be surprising.
>
> I think in principle the DRHS can contain a call to DEFERRED_INIT.
> Doing that would probably require further handling elsewhere though.
>
> =====
>
> I am still not very confident now for this part of the change.

I see.  I still tend to think that with or without the generation of
gimple_build_debug_binds, the debugger would still not display any value
for the component in question.  Without it there would be no information
about the component at a any place in code affected by this, with it the
component would be explicitely uninitialized.  But OK.

>
> My questions:
>
> 1. If we don’t handle grp_to_be_debug_replaced at all, what will
> happen?  ( the user of the debugger will see uninitialized values in
> the removed part of the aggregate?  Or something else?)

Well, can you try?  :-) I think the debugger would not have anything to
display.

> 2. On the other hand, if we handle grp_to_be_debug_replaced as the
> current patch, what will the user of the debugger see?

I don't know.  It would be interesting to know if the generated DWARF is
different at all.

>
>> 
>> On a related note, if the intent of the feature is for optimizers to
>> behave (almost?) as if it was not taking place,
>
> What’s you mean by “it” here?

-ftrivial-auto-var-init (the feature)

>
>> I believe you need to
>> handle specially, and probably just ignore, calls to IFN_DEFERRED_INIT
>> in scan_function in tree-sra.c.
>
>
> Do you mean to let tree-sra phase ignore IFN_DEFERRED_INIT calls completely?
>

I have thought about it a bit more and actually looked at the decision
making code and I realized I was wrong because initializations of
aggregates from a call, which IIUC DEFERRED_INIT is, would not result in
setting neither grp_assignment_write nor grp_scalar_write being set and
only these two affect the transformation decisions.

Moreover,  ignoring DEFERRED_INIT calls in scan_function might cause
a need for special-casing elsewhere and so it is not worth it.  Sorry.

But presence of calls to DEFERRED_INIT still might cause total
scalarization to not happen.  I believe you do not want to set a bit in
cannot_scalarize_away_bitmap in build_access_from_expr function if the
statement in question is a call to DEFERRED_INIT.  You should be able to
modify gcc/testsuite/gcc.dg/tree-ssa/sra-12.c to get a simple testcase,
I think.

Martin

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-09 16:18     ` Martin Jambor
@ 2021-07-09 18:52       ` Qing Zhao
  2021-07-12  7:51       ` Richard Sandiford
  1 sibling, 0 replies; 25+ messages in thread
From: Qing Zhao @ 2021-07-09 18:52 UTC (permalink / raw)
  To: Martin Jambor
  Cc: Richard Biener, Richard Sandiford, kees cook, gcc-patches Qing Zhao via

Hi,

> On Jul 9, 2021, at 11:18 AM, Martin Jambor <mjambor@suse.cz> wrote:
>> 
>>> On Jul 8, 2021, at 8:29 AM, Martin Jambor <mjambor@suse.cz> wrote:
>>>> diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
>>>> index c05d22f3e8f1..35051d7c6b96 100644
>>>> --- a/gcc/tree-sra.c
>>>> +++ b/gcc/tree-sra.c
>>>> @@ -384,6 +384,13 @@ static struct
>>>> 
>>>>  /* Numbber of components created when splitting aggregate parameters.  */
>>>>  int param_reductions_created;
>>>> +
>>>> +  /* Number of deferred_init calls that are modified.  */
>>>> +  int deferred_init;
>>>> +
>>>> +  /* Number of deferred_init calls that are created by
>>>> +     generate_subtree_deferred_init.  */
>>>> +  int subtree_deferred_init;
>>>> } sra_stats;
>>>> 
>>>> static void
>>>> @@ -4096,6 +4103,110 @@ get_repl_default_def_ssa_name (struct access *racc, tree reg_type)
>>>>  return get_or_create_ssa_default_def (cfun, racc->replacement_decl);
>>>> }
>>>> 
>>>> +
>>>> +/* Generate statements to call .DEFERRED_INIT to initialize scalar replacements
>>>> +   of accesses within a subtree ACCESS; all its children, siblings and their
>>>> +   children are to be processed.
>>>> +   GSI is a statement iterator used to place the new statements.  */
>>>> +static void
>>>> +generate_subtree_deferred_init (struct access *access,
>>>> +				tree init_type,
>>>> +				tree is_vla,
>>>> +				gimple_stmt_iterator *gsi,
>>>> +				location_t loc)
>>>> +{
>>>> +  do
>>>> +    {
>>>> +      if (access->grp_to_be_replaced)
>>>> +	{
>>>> +	  tree repl = get_access_replacement (access);
>>>> +	  gimple *call
>>>> +	    = gimple_build_call_internal (IFN_DEFERRED_INIT, 3,
>>>> +					  TYPE_SIZE_UNIT (TREE_TYPE (repl)),
>>>> +					  init_type, is_vla);
>>>> +	  gimple_call_set_lhs (call, repl);
>>>> +	  gsi_insert_before (gsi, call, GSI_SAME_STMT);
>>>> +	  update_stmt (call);
>>>> +	  gimple_set_location (call, loc);
>>>> +	  sra_stats.subtree_deferred_init++;
>>>> +	}
>>>> +      else if (access->grp_to_be_debug_replaced)
>>>> +	{
>>>> +	  tree drepl = get_access_replacement (access);
>>>> +	  tree call = build_call_expr_internal_loc
>>>> +		     (UNKNOWN_LOCATION, IFN_DEFERRED_INIT,
>>>> +		      TREE_TYPE (drepl), 3,
>>>> +		      TYPE_SIZE_UNIT (TREE_TYPE (drepl)),
>>>> +		      init_type, is_vla);
>>>> +	  gdebug *ds = gimple_build_debug_bind (drepl, call,
>>>> +						gsi_stmt (*gsi));
>>>> +	  gsi_insert_before (gsi, ds, GSI_SAME_STMT);
>>> 
>>> Is handling of grp_to_be_debug_replaced accesses necessary here?  If so,
>>> why?  grp_to_be_debug_replaced accesses are there only to facilitate
>>> debug information about a part of an aggregate decl is that is likely
>>> going to be entirely removed - so that debuggers can sometimes show to
>>> users information about what they would contain had they not removed.
>>> It seems strange you need to mark them as uninitialized because they
>>> should not have any consumers.  (But perhaps it is also harmless.)
>> 
>> This part has been discussed during the 2nd version of the patch, but
>> I think that more discussion might be necessary.
>> 
>> In the previous discussion, Richard Sandiford mentioned:
>> (https://gcc.gnu.org/pipermail/gcc-patches/2021-April/568620.html):
>> 
>> =====
>> 
>> I guess the thing we need to decide here is whether -ftrivial-auto-var-init
>> should affect debug-only constructs too.  If it doesn't, exmaining removed
>> components in a debugger might show uninitialised values in cases where
>> the user was expecting initialised ones.  There would be no security
>> concern, but it might be surprising.
>> 
>> I think in principle the DRHS can contain a call to DEFERRED_INIT.
>> Doing that would probably require further handling elsewhere though.
>> 
>> =====
>> 
>> I am still not very confident now for this part of the change.
> 
> I see.  I still tend to think that with or without the generation of
> gimple_build_debug_binds, the debugger would still not display any value
> for the component in question.  Without it there would be no information
> about the component at a any place in code affected by this, with it the
> component would be explicitely uninitialized.  But OK.

So, my current change for access->grp_to_be_debug_replaced is good?

Do I need to modify any other code in addition to this in order to let debugger work correctly?

Or deleting this part of code might be simple and better?

>> 
>> My questions:
>> 
>> 1. If we don’t handle grp_to_be_debug_replaced at all, what will
>> happen?  ( the user of the debugger will see uninitialized values in
>> the removed part of the aggregate?  Or something else?)
> 
> Well, can you try?  :-) I think the debugger would not have anything to
> display.
I will try to come up with a small example on this.
> 
>> 2. On the other hand, if we handle grp_to_be_debug_replaced as the
>> current patch, what will the user of the debugger see?
> 
> I don't know.  It would be interesting to know if the generated DWARF is
> different at all.

Okay, I will study this a little bit.
> 
>> 
>>> 
>>> On a related note, if the intent of the feature is for optimizers to
>>> behave (almost?) as if it was not taking place,
>> 
>> What’s you mean by “it” here?
> 
> -ftrivial-auto-var-init (the feature)
Okay.

> 
>> 
>>> I believe you need to
>>> handle specially, and probably just ignore, calls to IFN_DEFERRED_INIT
>>> in scan_function in tree-sra.c.
>> 
>> 
>> Do you mean to let tree-sra phase ignore IFN_DEFERRED_INIT calls completely?
>> 
> 
> I have thought about it a bit more and actually looked at the decision
> making code and I realized I was wrong because initializations of
> aggregates from a call, which IIUC DEFERRED_INIT is, would not result in
> setting neither grp_assignment_write nor grp_scalar_write being set and
> only these two affect the transformation decisions.
> 
> Moreover,  ignoring DEFERRED_INIT calls in scan_function might cause
> a need for special-casing elsewhere and so it is not worth it.  Sorry.

Okay.

> 
> But presence of calls to DEFERRED_INIT still might cause total
> scalarization to not happen.  I believe you do not want to set a bit in
> cannot_scalarize_away_bitmap in build_access_from_expr function if the
> statement in question is a call to DEFERRED_INIT.  You should be able to
> modify gcc/testsuite/gcc.dg/tree-ssa/sra-12.c to get a simple testcase,
Okay, will study this a little bit more.
Might have more questions on this.

Thanks.

Qing
> I think.
> 
> Martin


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-09 16:18     ` Martin Jambor
  2021-07-09 18:52       ` Qing Zhao
@ 2021-07-12  7:51       ` Richard Sandiford
  2021-07-12 15:31         ` Qing Zhao
  1 sibling, 1 reply; 25+ messages in thread
From: Richard Sandiford @ 2021-07-12  7:51 UTC (permalink / raw)
  To: Martin Jambor
  Cc: Qing Zhao, Richard Biener, kees cook, gcc-patches Qing Zhao via

Martin Jambor <mjambor@suse.cz> writes:
> On Thu, Jul 08 2021, Qing Zhao wrote:
>> (Resend this email since the previous one didn’t quote, I changed one
>> setting in my mail client, hopefully that can fix this issue).
>>
>> Hi, Martin,
>>
>> Thank you for the review and comment.
>>
>>> On Jul 8, 2021, at 8:29 AM, Martin Jambor <mjambor@suse.cz> wrote:
>>>> diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
>>>> index c05d22f3e8f1..35051d7c6b96 100644
>>>> --- a/gcc/tree-sra.c
>>>> +++ b/gcc/tree-sra.c
>>>> @@ -384,6 +384,13 @@ static struct
>>>> 
>>>>   /* Numbber of components created when splitting aggregate parameters.  */
>>>>   int param_reductions_created;
>>>> +
>>>> +  /* Number of deferred_init calls that are modified.  */
>>>> +  int deferred_init;
>>>> +
>>>> +  /* Number of deferred_init calls that are created by
>>>> +     generate_subtree_deferred_init.  */
>>>> +  int subtree_deferred_init;
>>>> } sra_stats;
>>>> 
>>>> static void
>>>> @@ -4096,6 +4103,110 @@ get_repl_default_def_ssa_name (struct access *racc, tree reg_type)
>>>>   return get_or_create_ssa_default_def (cfun, racc->replacement_decl);
>>>> }
>>>> 
>>>> +
>>>> +/* Generate statements to call .DEFERRED_INIT to initialize scalar replacements
>>>> +   of accesses within a subtree ACCESS; all its children, siblings and their
>>>> +   children are to be processed.
>>>> +   GSI is a statement iterator used to place the new statements.  */
>>>> +static void
>>>> +generate_subtree_deferred_init (struct access *access,
>>>> +				tree init_type,
>>>> +				tree is_vla,
>>>> +				gimple_stmt_iterator *gsi,
>>>> +				location_t loc)
>>>> +{
>>>> +  do
>>>> +    {
>>>> +      if (access->grp_to_be_replaced)
>>>> +	{
>>>> +	  tree repl = get_access_replacement (access);
>>>> +	  gimple *call
>>>> +	    = gimple_build_call_internal (IFN_DEFERRED_INIT, 3,
>>>> +					  TYPE_SIZE_UNIT (TREE_TYPE (repl)),
>>>> +					  init_type, is_vla);
>>>> +	  gimple_call_set_lhs (call, repl);
>>>> +	  gsi_insert_before (gsi, call, GSI_SAME_STMT);
>>>> +	  update_stmt (call);
>>>> +	  gimple_set_location (call, loc);
>>>> +	  sra_stats.subtree_deferred_init++;
>>>> +	}
>>>> +      else if (access->grp_to_be_debug_replaced)
>>>> +	{
>>>> +	  tree drepl = get_access_replacement (access);
>>>> +	  tree call = build_call_expr_internal_loc
>>>> +		     (UNKNOWN_LOCATION, IFN_DEFERRED_INIT,
>>>> +		      TREE_TYPE (drepl), 3,
>>>> +		      TYPE_SIZE_UNIT (TREE_TYPE (drepl)),
>>>> +		      init_type, is_vla);
>>>> +	  gdebug *ds = gimple_build_debug_bind (drepl, call,
>>>> +						gsi_stmt (*gsi));
>>>> +	  gsi_insert_before (gsi, ds, GSI_SAME_STMT);
>>> 
>>> Is handling of grp_to_be_debug_replaced accesses necessary here?  If so,
>>> why?  grp_to_be_debug_replaced accesses are there only to facilitate
>>> debug information about a part of an aggregate decl is that is likely
>>> going to be entirely removed - so that debuggers can sometimes show to
>>> users information about what they would contain had they not removed.
>>> It seems strange you need to mark them as uninitialized because they
>>> should not have any consumers.  (But perhaps it is also harmless.)
>>
>> This part has been discussed during the 2nd version of the patch, but
>> I think that more discussion might be necessary.
>>
>> In the previous discussion, Richard Sandiford mentioned:
>> (https://gcc.gnu.org/pipermail/gcc-patches/2021-April/568620.html):
>>
>> =====
>>
>> I guess the thing we need to decide here is whether -ftrivial-auto-var-init
>> should affect debug-only constructs too.  If it doesn't, exmaining removed
>> components in a debugger might show uninitialised values in cases where
>> the user was expecting initialised ones.  There would be no security
>> concern, but it might be surprising.
>>
>> I think in principle the DRHS can contain a call to DEFERRED_INIT.
>> Doing that would probably require further handling elsewhere though.
>>
>> =====
>>
>> I am still not very confident now for this part of the change.
>
> I see.  I still tend to think that with or without the generation of
> gimple_build_debug_binds, the debugger would still not display any value
> for the component in question.  Without it there would be no information
> about the component at a any place in code affected by this, with it the
> component would be explicitely uninitialized.  But OK.

FTR, I don't have a strong opinion here.  You know the code better
than I do, so if you think not generating debug binds is better then
let's do that.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-12  7:51       ` Richard Sandiford
@ 2021-07-12 15:31         ` Qing Zhao
  2021-07-12 17:06           ` Martin Jambor
  0 siblings, 1 reply; 25+ messages in thread
From: Qing Zhao @ 2021-07-12 15:31 UTC (permalink / raw)
  To: Richard Sandiford, Martin Jambor
  Cc: Richard Biener, kees cook, gcc-patches Qing Zhao via



> On Jul 12, 2021, at 2:51 AM, Richard Sandiford <richard.sandiford@arm.com> wrote:
> 
> Martin Jambor <mjambor@suse.cz> writes:
>> On Thu, Jul 08 2021, Qing Zhao wrote:
>>> (Resend this email since the previous one didn’t quote, I changed one
>>> setting in my mail client, hopefully that can fix this issue).
>>> 
>>> Hi, Martin,
>>> 
>>> Thank you for the review and comment.
>>> 
>>>> On Jul 8, 2021, at 8:29 AM, Martin Jambor <mjambor@suse.cz> wrote:
>>>>> diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
>>>>> index c05d22f3e8f1..35051d7c6b96 100644
>>>>> --- a/gcc/tree-sra.c
>>>>> +++ b/gcc/tree-sra.c
>>>>> @@ -384,6 +384,13 @@ static struct
>>>>> 
>>>>>  /* Numbber of components created when splitting aggregate parameters.  */
>>>>>  int param_reductions_created;
>>>>> +
>>>>> +  /* Number of deferred_init calls that are modified.  */
>>>>> +  int deferred_init;
>>>>> +
>>>>> +  /* Number of deferred_init calls that are created by
>>>>> +     generate_subtree_deferred_init.  */
>>>>> +  int subtree_deferred_init;
>>>>> } sra_stats;
>>>>> 
>>>>> static void
>>>>> @@ -4096,6 +4103,110 @@ get_repl_default_def_ssa_name (struct access *racc, tree reg_type)
>>>>>  return get_or_create_ssa_default_def (cfun, racc->replacement_decl);
>>>>> }
>>>>> 
>>>>> +
>>>>> +/* Generate statements to call .DEFERRED_INIT to initialize scalar replacements
>>>>> +   of accesses within a subtree ACCESS; all its children, siblings and their
>>>>> +   children are to be processed.
>>>>> +   GSI is a statement iterator used to place the new statements.  */
>>>>> +static void
>>>>> +generate_subtree_deferred_init (struct access *access,
>>>>> +				tree init_type,
>>>>> +				tree is_vla,
>>>>> +				gimple_stmt_iterator *gsi,
>>>>> +				location_t loc)
>>>>> +{
>>>>> +  do
>>>>> +    {
>>>>> +      if (access->grp_to_be_replaced)
>>>>> +	{
>>>>> +	  tree repl = get_access_replacement (access);
>>>>> +	  gimple *call
>>>>> +	    = gimple_build_call_internal (IFN_DEFERRED_INIT, 3,
>>>>> +					  TYPE_SIZE_UNIT (TREE_TYPE (repl)),
>>>>> +					  init_type, is_vla);
>>>>> +	  gimple_call_set_lhs (call, repl);
>>>>> +	  gsi_insert_before (gsi, call, GSI_SAME_STMT);
>>>>> +	  update_stmt (call);
>>>>> +	  gimple_set_location (call, loc);
>>>>> +	  sra_stats.subtree_deferred_init++;
>>>>> +	}
>>>>> +      else if (access->grp_to_be_debug_replaced)
>>>>> +	{
>>>>> +	  tree drepl = get_access_replacement (access);
>>>>> +	  tree call = build_call_expr_internal_loc
>>>>> +		     (UNKNOWN_LOCATION, IFN_DEFERRED_INIT,
>>>>> +		      TREE_TYPE (drepl), 3,
>>>>> +		      TYPE_SIZE_UNIT (TREE_TYPE (drepl)),
>>>>> +		      init_type, is_vla);
>>>>> +	  gdebug *ds = gimple_build_debug_bind (drepl, call,
>>>>> +						gsi_stmt (*gsi));
>>>>> +	  gsi_insert_before (gsi, ds, GSI_SAME_STMT);
>>>> 
>>>> Is handling of grp_to_be_debug_replaced accesses necessary here?  If so,
>>>> why?  grp_to_be_debug_replaced accesses are there only to facilitate
>>>> debug information about a part of an aggregate decl is that is likely
>>>> going to be entirely removed - so that debuggers can sometimes show to
>>>> users information about what they would contain had they not removed.
>>>> It seems strange you need to mark them as uninitialized because they
>>>> should not have any consumers.  (But perhaps it is also harmless.)
>>> 
>>> This part has been discussed during the 2nd version of the patch, but
>>> I think that more discussion might be necessary.
>>> 
>>> In the previous discussion, Richard Sandiford mentioned:
>>> (https://gcc.gnu.org/pipermail/gcc-patches/2021-April/568620.html):
>>> 
>>> =====
>>> 
>>> I guess the thing we need to decide here is whether -ftrivial-auto-var-init
>>> should affect debug-only constructs too.  If it doesn't, exmaining removed
>>> components in a debugger might show uninitialised values in cases where
>>> the user was expecting initialised ones.  There would be no security
>>> concern, but it might be surprising.
>>> 
>>> I think in principle the DRHS can contain a call to DEFERRED_INIT.
>>> Doing that would probably require further handling elsewhere though.
>>> 
>>> =====
>>> 
>>> I am still not very confident now for this part of the change.
>> 
>> I see.  I still tend to think that with or without the generation of
>> gimple_build_debug_binds, the debugger would still not display any value
>> for the component in question.  Without it there would be no information
>> about the component at a any place in code affected by this, with it the
>> component would be explicitely uninitialized.  But OK.
> 
> FTR, I don't have a strong opinion here.  You know the code better
> than I do, so if you think not generating debug binds is better then
> let's do that.

I am okay with not generating debug binds here. 

Then I will just delete the part of code that guarded with  if (access->grp_to_be_debug_replaced)?

Martin, please confirm on this.

Thanks.

Qing
> Thanks,
> Richard


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-12 15:31         ` Qing Zhao
@ 2021-07-12 17:06           ` Martin Jambor
  2021-07-12 18:13             ` Qing Zhao
  0 siblings, 1 reply; 25+ messages in thread
From: Martin Jambor @ 2021-07-12 17:06 UTC (permalink / raw)
  To: Qing Zhao, Richard Sandiford
  Cc: Richard Biener, kees cook, gcc-patches Qing Zhao via

[-- Attachment #1: Type: text/plain, Size: 7307 bytes --]

Hi,

On Mon, Jul 12 2021, Qing Zhao wrote:
>> On Jul 12, 2021, at 2:51 AM, Richard Sandiford <richard.sandiford@arm.com> wrote:
>>
>> Martin Jambor <mjambor@suse.cz> writes:
>>> On Thu, Jul 08 2021, Qing Zhao wrote:
>>>> (Resend this email since the previous one didn’t quote, I changed one
>>>> setting in my mail client, hopefully that can fix this issue).
>>>>
>>>> Hi, Martin,
>>>>
>>>> Thank you for the review and comment.
>>>>
>>>>> On Jul 8, 2021, at 8:29 AM, Martin Jambor <mjambor@suse.cz> wrote:
>>>>>> diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
>>>>>> index c05d22f3e8f1..35051d7c6b96 100644
>>>>>> --- a/gcc/tree-sra.c
>>>>>> +++ b/gcc/tree-sra.c
>>>>>> @@ -384,6 +384,13 @@ static struct
>>>>>>
>>>>>>  /* Numbber of components created when splitting aggregate parameters.  */
>>>>>>  int param_reductions_created;
>>>>>> +
>>>>>> +  /* Number of deferred_init calls that are modified.  */
>>>>>> +  int deferred_init;
>>>>>> +
>>>>>> +  /* Number of deferred_init calls that are created by
>>>>>> +     generate_subtree_deferred_init.  */
>>>>>> +  int subtree_deferred_init;
>>>>>> } sra_stats;
>>>>>>
>>>>>> static void
>>>>>> @@ -4096,6 +4103,110 @@ get_repl_default_def_ssa_name (struct access *racc, tree reg_type)
>>>>>>  return get_or_create_ssa_default_def (cfun, racc->replacement_decl);
>>>>>> }
>>>>>>
>>>>>> +
>>>>>> +/* Generate statements to call .DEFERRED_INIT to initialize scalar replacements
>>>>>> +   of accesses within a subtree ACCESS; all its children, siblings and their
>>>>>> +   children are to be processed.
>>>>>> +   GSI is a statement iterator used to place the new statements.  */
>>>>>> +static void
>>>>>> +generate_subtree_deferred_init (struct access *access,
>>>>>> +				tree init_type,
>>>>>> +				tree is_vla,
>>>>>> +				gimple_stmt_iterator *gsi,
>>>>>> +				location_t loc)
>>>>>> +{
>>>>>> +  do
>>>>>> +    {
>>>>>> +      if (access->grp_to_be_replaced)
>>>>>> +	{
>>>>>> +	  tree repl = get_access_replacement (access);
>>>>>> +	  gimple *call
>>>>>> +	    = gimple_build_call_internal (IFN_DEFERRED_INIT, 3,
>>>>>> +					  TYPE_SIZE_UNIT (TREE_TYPE (repl)),
>>>>>> +					  init_type, is_vla);
>>>>>> +	  gimple_call_set_lhs (call, repl);
>>>>>> +	  gsi_insert_before (gsi, call, GSI_SAME_STMT);
>>>>>> +	  update_stmt (call);
>>>>>> +	  gimple_set_location (call, loc);
>>>>>> +	  sra_stats.subtree_deferred_init++;
>>>>>> +	}
>>>>>> +      else if (access->grp_to_be_debug_replaced)
>>>>>> +	{
>>>>>> +	  tree drepl = get_access_replacement (access);
>>>>>> +	  tree call = build_call_expr_internal_loc
>>>>>> +		     (UNKNOWN_LOCATION, IFN_DEFERRED_INIT,
>>>>>> +		      TREE_TYPE (drepl), 3,
>>>>>> +		      TYPE_SIZE_UNIT (TREE_TYPE (drepl)),
>>>>>> +		      init_type, is_vla);
>>>>>> +	  gdebug *ds = gimple_build_debug_bind (drepl, call,
>>>>>> +						gsi_stmt (*gsi));
>>>>>> +	  gsi_insert_before (gsi, ds, GSI_SAME_STMT);
>>>>>
>>>>> Is handling of grp_to_be_debug_replaced accesses necessary here?  If so,
>>>>> why?  grp_to_be_debug_replaced accesses are there only to facilitate
>>>>> debug information about a part of an aggregate decl is that is likely
>>>>> going to be entirely removed - so that debuggers can sometimes show to
>>>>> users information about what they would contain had they not removed.
>>>>> It seems strange you need to mark them as uninitialized because they
>>>>> should not have any consumers.  (But perhaps it is also harmless.)
>>>>
>>>> This part has been discussed during the 2nd version of the patch, but
>>>> I think that more discussion might be necessary.
>>>>
>>>> In the previous discussion, Richard Sandiford mentioned:
>>>> (https://gcc.gnu.org/pipermail/gcc-patches/2021-April/568620.html):
>>>>
>>>> =====
>>>>
>>>> I guess the thing we need to decide here is whether -ftrivial-auto-var-init
>>>> should affect debug-only constructs too.  If it doesn't, exmaining removed
>>>> components in a debugger might show uninitialised values in cases where
>>>> the user was expecting initialised ones.  There would be no security
>>>> concern, but it might be surprising.
>>>>
>>>> I think in principle the DRHS can contain a call to DEFERRED_INIT.
>>>> Doing that would probably require further handling elsewhere though.
>>>>
>>>> =====
>>>>
>>>> I am still not very confident now for this part of the change.
>>>
>>> I see.  I still tend to think that with or without the generation of
>>> gimple_build_debug_binds, the debugger would still not display any value
>>> for the component in question.  Without it there would be no information
>>> about the component at a any place in code affected by this, with it the
>>> component would be explicitely uninitialized.  But OK.
>>
>> FTR, I don't have a strong opinion here.  You know the code better
>> than I do, so if you think not generating debug binds is better then
>> let's do that.

I am very flattered that you think I understand debug_binds well :-)  (I don't)

>
> I am okay with not generating debug binds here.
>
> Then I will just delete the part of code that guarded with  if (access->grp_to_be_debug_replaced)?
>

But I have done some simple experiments and reached the conclusion the
code is never executed and wrong.  It can get executed if you change the
if statement in build_access_from_expr like I explained in my previous
email:

  static bool
  build_access_from_expr (tree expr, gimple *stmt, bool write)
  {
    struct access *access;

    access = build_access_from_expr_1 (expr, stmt, write);
    if (access)
      {
        /* This means the aggregate is accesses as a whole in a way other than an
           assign statement and thus cannot be removed even if we had a scalar
           replacement for everything.  */
        if (cannot_scalarize_away_bitmap
            && !gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
          bitmap_set_bit (cannot_scalarize_away_bitmap, DECL_UID (access->base));
        return true;
      }
    return false;
  }

But then it ICEs when the operand scanner attempts to grok the debug bind:

  test.c:43:1: internal compiler error: in get_expr_operands, at tree-ssa-operands.c:945
     43 | }

Gdb reveals the statement is what I suspected:

  #2  0x000000000156eea6 in operands_scanner::parse_ssa_operands (this=0x7fffffffdad0)
      at /home/mjambor/gcc/mine/src/gcc/tree-ssa-operands.c:973
  973             get_expr_operands (gimple_debug_bind_get_value_ptr (stmt),
  (gdb) pgg stmt
  # DEBUG s2D.1971 => .DEFERRED_INIT (4, 1, 0)

It turns out debug binds cannot have CALL_EXPRs as operands.

So yes, just do not attempt to handle grp_to_be_debug_replaced accesses
in generate_subtree_deferred_init.  In fact, if you do that (+ the
modification described above) the results seem to be rather good on the
attached testcase (which I derived from gcc.dg/guality/pr59776.c), at
least on GIMPLE and at least when compiled with -O1 -g.  I did not
actually attempt to look at the generated dwarf.  I have made a simple
attempt to print uninitialized (and unused) SRAed structure in gdb with
and without -ftrivial-auto-var-init=pattern, but in both cases it just
said it was optimized out.

Martin


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: sra-uninit-test.c --]
[-- Type: text/x-csrc, Size: 533 bytes --]

#include "nop.h"

struct S { float f, g; };

__attribute__((noipa)) void
foo (struct S *p, int flag)
{
  struct S s1, s2;
  if (flag)
    {
      s1 = *p;
      s2 = s1;
    }
  else
    {
      s2 = s1;
    }

  *(int *) &s2.f = 0;
  asm volatile (NOP : : : "memory");
  asm volatile (NOP : : : "memory");
  s2 = s1;
  asm volatile (NOP : : : "memory");
  asm volatile (NOP : : : "memory");
}

int __attribute__((noipa))
getint(void)
{
  return 1;
}

int
main ()
{
  struct S x = { 5.0f, 6.0f };
  foo (&x, getint());
  return 0;
}

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-07 17:38 [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc Qing Zhao
  2021-07-08 13:29 ` Martin Jambor
@ 2021-07-12 17:56 ` Kees Cook
  2021-07-12 20:28   ` Qing Zhao
  1 sibling, 1 reply; 25+ messages in thread
From: Kees Cook @ 2021-07-12 17:56 UTC (permalink / raw)
  To: Qing Zhao; +Cc: Richard Biener, Richard Sandiford, gcc-patches Qing Zhao via

On Wed, Jul 07, 2021 at 05:38:02PM +0000, Qing Zhao wrote:
> Hi, 
> 
> This is the 4th version of the patch for the new security feature for GCC.
> 
> I have tested it with bootstrap on both x86 and aarch64, regression testing on both x86 and aarch64.
> Also compile and run CPU2017, without any issue.
> 
> Please take a look and let me know your comments and suggestions.

Thanks for the update!

It looks like padding initialization has regressed to where things where
in version 1[1] (it was, however, working in version 2[2]). I'm seeing
these failures again in the kernel self-test:

test_stackinit: small_hole_static_all FAIL (uninit bytes: 3)
test_stackinit: big_hole_static_all FAIL (uninit bytes: 61)
test_stackinit: trailing_hole_static_all FAIL (uninit bytes: 7)
test_stackinit: small_hole_dynamic_all FAIL (uninit bytes: 3)
test_stackinit: big_hole_dynamic_all FAIL (uninit bytes: 61)
test_stackinit: trailing_hole_dynamic_all FAIL (uninit bytes: 7)

In looking at the gcc test cases, I think the wrong thing is
being checked: we want to verify the padding itself. For example,
in auto-init-17.c, the actual bytes after "four" need to be checked,
rather than "four" itself. For example, something like this:

struct test_trailing_hole {
        int one;
        int two;
        int three;
        char four;
        /* "sizeof(unsigned long) - 1" byte padding hole here. */
};
        
#define offsetofend(STRUCT, MEMBER) \
  (__builtin_offsetof(STRUCT, MEMBER) + sizeof((((STRUCT *)0)->MEMBER)))

int foo ()
{ 
  struct test_trailing_hole var[10];
  unsigned char *ptr = (unsigned char *)&var[2];
  int i;

  for (i = 0; i < sizeof(var[2]) - offsetofend(typeof(var[2]), four); i++) {
    if (ptr[i] != 0)
      return 1;
  } 
  return 0;
}

But this isn't actually sufficient because they may _accidentally_
be zero already. The kernel tests specifically make sure to fill the
about-to-be-used stack with 0xff before calling a function like foo()
above.

(And as an aside, it seems like naming the test cases with some details
about what is being tested in the filename would be nice -- it was
a little weird having to dig through their numeric names to find the
padding tests.)

Otherwise, this looks like it's coming along; I remain very excited!
Thank you for sticking with it. :)

-Kees

[1] https://gcc.gnu.org/pipermail/gcc-patches/2021-February/565840.html
[2] https://gcc.gnu.org/pipermail/gcc-patches/2021-April/567754.html

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-12 17:06           ` Martin Jambor
@ 2021-07-12 18:13             ` Qing Zhao
  0 siblings, 0 replies; 25+ messages in thread
From: Qing Zhao @ 2021-07-12 18:13 UTC (permalink / raw)
  To: Martin Jambor
  Cc: Richard Sandiford, Richard Biener, kees cook, gcc-patches Qing Zhao via

Hi, Martin,

Thanks a lot for your experiments and examples, they are really helpful.

So, based on your study, I will delete the code that handle 
grp_to_be_debug_replaced accesses in generate_subtree_deferred_init.

Let me know if you have further comments on this.

Qing


> On Jul 12, 2021, at 12:06 PM, Martin Jambor <mjambor@suse.cz> wrote:
> 
> Hi,
> 
> On Mon, Jul 12 2021, Qing Zhao wrote:
>>> On Jul 12, 2021, at 2:51 AM, Richard Sandiford <richard.sandiford@arm.com> wrote:
>>> 
>>> Martin Jambor <mjambor@suse.cz> writes:
>>>> On Thu, Jul 08 2021, Qing Zhao wrote:
>>>>> (Resend this email since the previous one didn’t quote, I changed one
>>>>> setting in my mail client, hopefully that can fix this issue).
>>>>> 
>>>>> Hi, Martin,
>>>>> 
>>>>> Thank you for the review and comment.
>>>>> 
>>>>>> On Jul 8, 2021, at 8:29 AM, Martin Jambor <mjambor@suse.cz> wrote:
>>>>>>> diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
>>>>>>> index c05d22f3e8f1..35051d7c6b96 100644
>>>>>>> --- a/gcc/tree-sra.c
>>>>>>> +++ b/gcc/tree-sra.c
>>>>>>> @@ -384,6 +384,13 @@ static struct
>>>>>>> 
>>>>>>> /* Numbber of components created when splitting aggregate parameters.  */
>>>>>>> int param_reductions_created;
>>>>>>> +
>>>>>>> +  /* Number of deferred_init calls that are modified.  */
>>>>>>> +  int deferred_init;
>>>>>>> +
>>>>>>> +  /* Number of deferred_init calls that are created by
>>>>>>> +     generate_subtree_deferred_init.  */
>>>>>>> +  int subtree_deferred_init;
>>>>>>> } sra_stats;
>>>>>>> 
>>>>>>> static void
>>>>>>> @@ -4096,6 +4103,110 @@ get_repl_default_def_ssa_name (struct access *racc, tree reg_type)
>>>>>>> return get_or_create_ssa_default_def (cfun, racc->replacement_decl);
>>>>>>> }
>>>>>>> 
>>>>>>> +
>>>>>>> +/* Generate statements to call .DEFERRED_INIT to initialize scalar replacements
>>>>>>> +   of accesses within a subtree ACCESS; all its children, siblings and their
>>>>>>> +   children are to be processed.
>>>>>>> +   GSI is a statement iterator used to place the new statements.  */
>>>>>>> +static void
>>>>>>> +generate_subtree_deferred_init (struct access *access,
>>>>>>> +				tree init_type,
>>>>>>> +				tree is_vla,
>>>>>>> +				gimple_stmt_iterator *gsi,
>>>>>>> +				location_t loc)
>>>>>>> +{
>>>>>>> +  do
>>>>>>> +    {
>>>>>>> +      if (access->grp_to_be_replaced)
>>>>>>> +	{
>>>>>>> +	  tree repl = get_access_replacement (access);
>>>>>>> +	  gimple *call
>>>>>>> +	    = gimple_build_call_internal (IFN_DEFERRED_INIT, 3,
>>>>>>> +					  TYPE_SIZE_UNIT (TREE_TYPE (repl)),
>>>>>>> +					  init_type, is_vla);
>>>>>>> +	  gimple_call_set_lhs (call, repl);
>>>>>>> +	  gsi_insert_before (gsi, call, GSI_SAME_STMT);
>>>>>>> +	  update_stmt (call);
>>>>>>> +	  gimple_set_location (call, loc);
>>>>>>> +	  sra_stats.subtree_deferred_init++;
>>>>>>> +	}
>>>>>>> +      else if (access->grp_to_be_debug_replaced)
>>>>>>> +	{
>>>>>>> +	  tree drepl = get_access_replacement (access);
>>>>>>> +	  tree call = build_call_expr_internal_loc
>>>>>>> +		     (UNKNOWN_LOCATION, IFN_DEFERRED_INIT,
>>>>>>> +		      TREE_TYPE (drepl), 3,
>>>>>>> +		      TYPE_SIZE_UNIT (TREE_TYPE (drepl)),
>>>>>>> +		      init_type, is_vla);
>>>>>>> +	  gdebug *ds = gimple_build_debug_bind (drepl, call,
>>>>>>> +						gsi_stmt (*gsi));
>>>>>>> +	  gsi_insert_before (gsi, ds, GSI_SAME_STMT);
>>>>>> 
>>>>>> Is handling of grp_to_be_debug_replaced accesses necessary here?  If so,
>>>>>> why?  grp_to_be_debug_replaced accesses are there only to facilitate
>>>>>> debug information about a part of an aggregate decl is that is likely
>>>>>> going to be entirely removed - so that debuggers can sometimes show to
>>>>>> users information about what they would contain had they not removed.
>>>>>> It seems strange you need to mark them as uninitialized because they
>>>>>> should not have any consumers.  (But perhaps it is also harmless.)
>>>>> 
>>>>> This part has been discussed during the 2nd version of the patch, but
>>>>> I think that more discussion might be necessary.
>>>>> 
>>>>> In the previous discussion, Richard Sandiford mentioned:
>>>>> (https://gcc.gnu.org/pipermail/gcc-patches/2021-April/568620.html):
>>>>> 
>>>>> =====
>>>>> 
>>>>> I guess the thing we need to decide here is whether -ftrivial-auto-var-init
>>>>> should affect debug-only constructs too.  If it doesn't, exmaining removed
>>>>> components in a debugger might show uninitialised values in cases where
>>>>> the user was expecting initialised ones.  There would be no security
>>>>> concern, but it might be surprising.
>>>>> 
>>>>> I think in principle the DRHS can contain a call to DEFERRED_INIT.
>>>>> Doing that would probably require further handling elsewhere though.
>>>>> 
>>>>> =====
>>>>> 
>>>>> I am still not very confident now for this part of the change.
>>>> 
>>>> I see.  I still tend to think that with or without the generation of
>>>> gimple_build_debug_binds, the debugger would still not display any value
>>>> for the component in question.  Without it there would be no information
>>>> about the component at a any place in code affected by this, with it the
>>>> component would be explicitely uninitialized.  But OK.
>>> 
>>> FTR, I don't have a strong opinion here.  You know the code better
>>> than I do, so if you think not generating debug binds is better then
>>> let's do that.
> 
> I am very flattered that you think I understand debug_binds well :-)  (I don't)
> 
>> 
>> I am okay with not generating debug binds here.
>> 
>> Then I will just delete the part of code that guarded with  if (access->grp_to_be_debug_replaced)?
>> 
> 
> But I have done some simple experiments and reached the conclusion the
> code is never executed and wrong.  It can get executed if you change the
> if statement in build_access_from_expr like I explained in my previous
> email:
> 
>  static bool
>  build_access_from_expr (tree expr, gimple *stmt, bool write)
>  {
>    struct access *access;
> 
>    access = build_access_from_expr_1 (expr, stmt, write);
>    if (access)
>      {
>        /* This means the aggregate is accesses as a whole in a way other than an
>           assign statement and thus cannot be removed even if we had a scalar
>           replacement for everything.  */
>        if (cannot_scalarize_away_bitmap
>            && !gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
>          bitmap_set_bit (cannot_scalarize_away_bitmap, DECL_UID (access->base));
>        return true;
>      }
>    return false;
>  }
> 
> But then it ICEs when the operand scanner attempts to grok the debug bind:
> 
>  test.c:43:1: internal compiler error: in get_expr_operands, at tree-ssa-operands.c:945
>     43 | }
> 
> Gdb reveals the statement is what I suspected:
> 
>  #2  0x000000000156eea6 in operands_scanner::parse_ssa_operands (this=0x7fffffffdad0)
>      at /home/mjambor/gcc/mine/src/gcc/tree-ssa-operands.c:973
>  973             get_expr_operands (gimple_debug_bind_get_value_ptr (stmt),
>  (gdb) pgg stmt
>  # DEBUG s2D.1971 => .DEFERRED_INIT (4, 1, 0)
> 
> It turns out debug binds cannot have CALL_EXPRs as operands.
> 
> So yes, just do not attempt to handle grp_to_be_debug_replaced accesses
> in generate_subtree_deferred_init.  In fact, if you do that (+ the
> modification described above) the results seem to be rather good on the
> attached testcase (which I derived from gcc.dg/guality/pr59776.c), at
> least on GIMPLE and at least when compiled with -O1 -g.  I did not
> actually attempt to look at the generated dwarf.  I have made a simple
> attempt to print uninitialized (and unused) SRAed structure in gdb with
> and without -ftrivial-auto-var-init=pattern, but in both cases it just
> said it was optimized out.
> 
> Martin
> 
> #include "nop.h"
> 
> struct S { float f, g; };
> 
> __attribute__((noipa)) void
> foo (struct S *p, int flag)
> {
>  struct S s1, s2;
>  if (flag)
>    {
>      s1 = *p;
>      s2 = s1;
>    }
>  else
>    {
>      s2 = s1;
>    }
> 
>  *(int *) &s2.f = 0;
>  asm volatile (NOP : : : "memory");
>  asm volatile (NOP : : : "memory");
>  s2 = s1;
>  asm volatile (NOP : : : "memory");
>  asm volatile (NOP : : : "memory");
> }
> 
> int __attribute__((noipa))
> getint(void)
> {
>  return 1;
> }
> 
> int
> main ()
> {
>  struct S x = { 5.0f, 6.0f };
>  foo (&x, getint());
>  return 0;
> }


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-12 17:56 ` Kees Cook
@ 2021-07-12 20:28   ` Qing Zhao
  2021-07-13 21:29     ` Kees Cook
  0 siblings, 1 reply; 25+ messages in thread
From: Qing Zhao @ 2021-07-12 20:28 UTC (permalink / raw)
  To: Kees Cook; +Cc: Richard Biener, Richard Sandiford, gcc-patches Qing Zhao via

Hi, Kees,

Thanks a lot for your testing on kernel testing cases. 

I have some question in below:

> On Jul 12, 2021, at 12:56 PM, Kees Cook <keescook@chromium.org> wrote:
> 
> On Wed, Jul 07, 2021 at 05:38:02PM +0000, Qing Zhao wrote:
>> Hi, 
>> 
>> This is the 4th version of the patch for the new security feature for GCC.
>> 
>> I have tested it with bootstrap on both x86 and aarch64, regression testing on both x86 and aarch64.
>> Also compile and run CPU2017, without any issue.
>> 
>> Please take a look and let me know your comments and suggestions.
> 
> Thanks for the update!
> 
> It looks like padding initialization has regressed to where things where
> in version 1[1] (it was, however, working in version 2[2]). I'm seeing
> these failures again in the kernel self-test:
> 
> test_stackinit: small_hole_static_all FAIL (uninit bytes: 3)
> test_stackinit: big_hole_static_all FAIL (uninit bytes: 61)
> test_stackinit: trailing_hole_static_all FAIL (uninit bytes: 7)
> test_stackinit: small_hole_dynamic_all FAIL (uninit bytes: 3)
> test_stackinit: big_hole_dynamic_all FAIL (uninit bytes: 61)
> test_stackinit: trailing_hole_dynamic_all FAIL (uninit bytes: 7)
 
Are the above failures for -ftrivial-auto-var-init=zero or -ftrivial-auto-var-init=pattern?  Or both?

For the current implementation, I believe that all paddings should be initialized with this option, 
for -ftrivial-auto-var-init=zero, the padding will be initialized to zero as before, however, for
-ftrivial-auto-var-init=pattern, the padding will be initialized to 0xFE byte-repeatable patterns.

> 
> In looking at the gcc test cases, I think the wrong thing is
> being checked: we want to verify the padding itself. For example,
> in auto-init-17.c, the actual bytes after "four" need to be checked,
> rather than "four" itself.

******For the current auto-init-17.c

  1 /* Verify zero initialization for array type with structure element with
  2    padding.  */
  3 /* { dg-do compile } */
  4 /* { dg-options "-ftrivial-auto-var-init=zero" } */
  5 
  6 struct test_trailing_hole {
  7         int one;
  8         int two;
  9         int three;
 10         char four;
 11         /* "sizeof(unsigned long) - 1" byte padding hole here. */
 12 };
 13 
 14 
 15 int foo ()
 16 {
 17   struct test_trailing_hole var[10];
 18   return var[2].four;
 19 }
 20 
 21 /* { dg-final { scan-assembler "movl\t\\\$0," } } */
 22 /* { dg-final { scan-assembler "movl\t\\\$20," } } */
 23 /* { dg-final { scan-assembler "rep stosq" } } */
~  
******We have the assembly as: (-ftrivial-auto-var-init=zero)

        .file   "auto-init-17.c"
        .text
        .globl  foo
        .type   foo, @function
foo:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $40, %rsp
        leaq    -160(%rbp), %rax
        movq    %rax, %rsi
        movl    $0, %eax
        movl    $20, %edx
        movq    %rsi, %rdi
        movq    %rdx, %rcx
        rep stosq
        movzbl  -116(%rbp), %eax
        movsbl  %al, %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   foo, .-foo
        .section        .note.GNU-stack,"",@progbits

From the above, we can see,  “zero” will be used to initialize 8 * 20 = 16 * 10 bytes of memory starting from the beginning of “var”, that include all the padding holes inside
This array of structure. 

I didn’t see issue with padding initialization here.

Do I miss anything here?

For pattern initialization, since we currently initialize all paddings with 0xFE byte-repeatable patterns, if the testing case still assumes zero initialization, then the testing cases
Need to be updated with this fact.

> For example, something like this:
> 
> struct test_trailing_hole {
>        int one;
>        int two;
>        int three;
>        char four;
>        /* "sizeof(unsigned long) - 1" byte padding hole here. */
> };
> 
> #define offsetofend(STRUCT, MEMBER) \
>  (__builtin_offsetof(STRUCT, MEMBER) + sizeof((((STRUCT *)0)->MEMBER)))
> 
> int foo ()
> { 
>  struct test_trailing_hole var[10];
>  unsigned char *ptr = (unsigned char *)&var[2];
>  int i;
> 
>  for (i = 0; i < sizeof(var[2]) - offsetofend(typeof(var[2]), four); i++) {
>    if (ptr[i] != 0)
>      return 1;
>  } 
>  return 0;
> }
> 
> But this isn't actually sufficient because they may _accidentally_
> be zero already. The kernel tests specifically make sure to fill the
> about-to-be-used stack with 0xff before calling a function like foo()
> above.
> 
> (And as an aside, it seems like naming the test cases with some details
> about what is being tested in the filename would be nice -- it was
> a little weird having to dig through their numeric names to find the
> padding tests.)

Yes, I will fix the testing names to more reflect the testing details. 

thanks.

Qing
> 
> Otherwise, this looks like it's coming along; I remain very excited!
> Thank you for sticking with it. :)
> 
> -Kees
> 
> [1] https://gcc.gnu.org/pipermail/gcc-patches/2021-February/565840.html
> [2] https://gcc.gnu.org/pipermail/gcc-patches/2021-April/567754.html
> 
> -- 
> Kees Cook


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-12 20:28   ` Qing Zhao
@ 2021-07-13 21:29     ` Kees Cook
  2021-07-13 23:09       ` Kees Cook
  2021-07-13 23:16       ` Qing Zhao
  0 siblings, 2 replies; 25+ messages in thread
From: Kees Cook @ 2021-07-13 21:29 UTC (permalink / raw)
  To: Qing Zhao; +Cc: Richard Biener, Richard Sandiford, gcc-patches Qing Zhao via

[-- Attachment #1: Type: text/plain, Size: 5029 bytes --]

On Mon, Jul 12, 2021 at 08:28:55PM +0000, Qing Zhao wrote:
> > On Jul 12, 2021, at 12:56 PM, Kees Cook <keescook@chromium.org> wrote:
> > On Wed, Jul 07, 2021 at 05:38:02PM +0000, Qing Zhao wrote:
> >> This is the 4th version of the patch for the new security feature for GCC.
> > 
> > It looks like padding initialization has regressed to where things where
> > in version 1[1] (it was, however, working in version 2[2]). I'm seeing
> > these failures again in the kernel self-test:
> > 
> > test_stackinit: small_hole_static_all FAIL (uninit bytes: 3)
> > test_stackinit: big_hole_static_all FAIL (uninit bytes: 61)
> > test_stackinit: trailing_hole_static_all FAIL (uninit bytes: 7)
> > test_stackinit: small_hole_dynamic_all FAIL (uninit bytes: 3)
> > test_stackinit: big_hole_dynamic_all FAIL (uninit bytes: 61)
> > test_stackinit: trailing_hole_dynamic_all FAIL (uninit bytes: 7)
>  
> Are the above failures for -ftrivial-auto-var-init=zero or -ftrivial-auto-var-init=pattern?  Or both?

Yes, I was only testing =zero (the kernel test handles =pattern as well:
it doesn't explicitly test for 0x00). I've verified with =pattern now,
too.

> For the current implementation, I believe that all paddings should be initialized with this option, 
> for -ftrivial-auto-var-init=zero, the padding will be initialized to zero as before, however, for
> -ftrivial-auto-var-init=pattern, the padding will be initialized to 0xFE byte-repeatable patterns.

I've double-checked that I'm using the right gcc, with the flag.

> > 
> > In looking at the gcc test cases, I think the wrong thing is
> > being checked: we want to verify the padding itself. For example,
> > in auto-init-17.c, the actual bytes after "four" need to be checked,
> > rather than "four" itself.
> 
> ******For the current auto-init-17.c
> 
>   1 /* Verify zero initialization for array type with structure element with
>   2    padding.  */
>   3 /* { dg-do compile } */
>   4 /* { dg-options "-ftrivial-auto-var-init=zero" } */
>   5 
>   6 struct test_trailing_hole {
>   7         int one;
>   8         int two;
>   9         int three;
>  10         char four;
>  11         /* "sizeof(unsigned long) - 1" byte padding hole here. */
>  12 };
>  13 
>  14 
>  15 int foo ()
>  16 {
>  17   struct test_trailing_hole var[10];
>  18   return var[2].four;
>  19 }
>  20 
>  21 /* { dg-final { scan-assembler "movl\t\\\$0," } } */
>  22 /* { dg-final { scan-assembler "movl\t\\\$20," } } */
>  23 /* { dg-final { scan-assembler "rep stosq" } } */
> ~  
> ******We have the assembly as: (-ftrivial-auto-var-init=zero)
> 
>         .file   "auto-init-17.c"
>         .text
>         .globl  foo
>         .type   foo, @function
> foo:
> .LFB0:
>         .cfi_startproc
>         pushq   %rbp
>         .cfi_def_cfa_offset 16
>         .cfi_offset 6, -16
>         movq    %rsp, %rbp
>         .cfi_def_cfa_register 6
>         subq    $40, %rsp
>         leaq    -160(%rbp), %rax
>         movq    %rax, %rsi
>         movl    $0, %eax
>         movl    $20, %edx
>         movq    %rsi, %rdi
>         movq    %rdx, %rcx
>         rep stosq
>         movzbl  -116(%rbp), %eax
>         movsbl  %al, %eax
>         leave
>         .cfi_def_cfa 7, 8
>         ret
>         .cfi_endproc
> .LFE0:
>         .size   foo, .-foo
>         .section        .note.GNU-stack,"",@progbits
> 
> From the above, we can see,  “zero” will be used to initialize 8 * 20 = 16 * 10 bytes of memory starting from the beginning of “var”, that include all the padding holes inside
> This array of structure. 
> 
> I didn’t see issue with padding initialization here.

Hm, agreed -- this test does do the right thing.

> > But this isn't actually sufficient because they may _accidentally_
> > be zero already. The kernel tests specifically make sure to fill the
> > about-to-be-used stack with 0xff before calling a function like foo()
> > above.

I've extracted the kernel test to build for userspace, and it behaves
the same way. See attached "stackinit.c".

$ gcc-build/auto-var-init.4/installed/bin/gcc -O2 -Wall -o stackinit stackinit.c
$ ./stackinit 2>&1 | grep failures:
stackinit: failures: 23
$ gcc-build/auto-var-init.4/installed/bin/gcc -O2 -Wall -ftrivial-auto-var-init=zero -o stackinit stackinit.c
stackinit.c: In function ‘__leaf_switch_none’:
stackinit.c:326:26: warning: statement will never be executed
[-Wswitch-unreachable]
  326 |                 uint64_t var;
      |                          ^~~
$ ./stackinit 2>&1 | grep failures:
stackinit: failures: 6

Same failures as seen in the kernel test (and an expected warning
about the initialization that will never happen for a pre-case switch
statement).

> > 
> > (And as an aside, it seems like naming the test cases with some details
> > about what is being tested in the filename would be nice -- it was
> > a little weird having to dig through their numeric names to find the
> > padding tests.)
> 
> Yes, I will fix the testing names to more reflect the testing details. 

Great!

-- 
Kees Cook

[-- Attachment #2: stackinit.c --]
[-- Type: text/x-csrc, Size: 12869 bytes --]

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Test cases for compiler-based stack variable zeroing via
 * -ftrivial-auto-var-init={zero,pattern} or CONFIG_GCC_PLUGIN_STRUCTLEAK*.
 *
 * Build example:
 * gcc -O2 -Wall -ftrivial-auto-var-init=zero -o stackinit stackinit.c
 */

/* Userspace headers. */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>

/* Linux kernel-isms */
#define KBUILD_MODNAME		"stackinit"
#define pr_fmt(fmt)		KBUILD_MODNAME ": " fmt
#define pr_err(fmt, ...)	fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warn(fmt, ...)	fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__)
#define pr_info(fmt, ...)	fprintf(stdout, pr_fmt(fmt), ##__VA_ARGS__)
#define __init			/**/
#define __user			/**/
#define noinline		__attribute__((__noinline__))
#define __aligned(x)		__attribute__((__aligned__(x)))
#ifdef __clang__
# define __compiletime_error(message) /**/
#else
# define __compiletime_error(message) __attribute__((__error__(message)))
#endif
#define __compiletime_assert(condition, msg, prefix, suffix)		\
	do {								\
		extern void prefix ## suffix(void) __compiletime_error(msg); \
		if (!(condition))					\
			prefix ## suffix();				\
	} while (0)
#define _compiletime_assert(condition, msg, prefix, suffix) \
	__compiletime_assert(condition, msg, prefix, suffix)
#define compiletime_assert(condition, msg) \
	_compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
#define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
#define BUILD_BUG_ON(condition) \
	BUILD_BUG_ON_MSG(condition, "BUILD_BUG_ON failed: " #condition)
typedef uint8_t			u8;
typedef uint16_t		u16;
typedef uint32_t		u32;
typedef uint64_t		u64;


/* Exfiltration buffer. */
#define MAX_VAR_SIZE	128
static u8 check_buf[MAX_VAR_SIZE];

/* Character array to trigger stack protector in all functions. */
#define VAR_BUFFER	 32

/* Volatile mask to convince compiler to copy memory with 0xff. */
static volatile u8 forced_mask = 0xff;

/* Location and size tracking to validate fill and test are colocated. */
static void *fill_start, *target_start;
static size_t fill_size, target_size;

static bool range_contains(char *haystack_start, size_t haystack_size,
			   char *needle_start, size_t needle_size)
{
	if (needle_start >= haystack_start &&
	    needle_start + needle_size <= haystack_start + haystack_size)
		return true;
	return false;
}

#define DO_NOTHING_TYPE_SCALAR(var_type)	var_type
#define DO_NOTHING_TYPE_STRING(var_type)	void
#define DO_NOTHING_TYPE_STRUCT(var_type)	void

#define DO_NOTHING_RETURN_SCALAR(ptr)		*(ptr)
#define DO_NOTHING_RETURN_STRING(ptr)		/**/
#define DO_NOTHING_RETURN_STRUCT(ptr)		/**/

#define DO_NOTHING_CALL_SCALAR(var, name)			\
		(var) = do_nothing_ ## name(&(var))
#define DO_NOTHING_CALL_STRING(var, name)			\
		do_nothing_ ## name(var)
#define DO_NOTHING_CALL_STRUCT(var, name)			\
		do_nothing_ ## name(&(var))

#define FETCH_ARG_SCALAR(var)		&var
#define FETCH_ARG_STRING(var)		var
#define FETCH_ARG_STRUCT(var)		&var

#define FILL_SIZE_STRING		16

#define INIT_CLONE_SCALAR		/**/
#define INIT_CLONE_STRING		[FILL_SIZE_STRING]
#define INIT_CLONE_STRUCT		/**/

#define INIT_SCALAR_none		/**/
#define INIT_SCALAR_zero		= 0

#define INIT_STRING_none		[FILL_SIZE_STRING] /**/
#define INIT_STRING_zero		[FILL_SIZE_STRING] = { }

#define INIT_STRUCT_none		/**/
#define INIT_STRUCT_zero		= { }
#define INIT_STRUCT_static_partial	= { .two = 0, }
#define INIT_STRUCT_static_all		= { .one = arg->one,		\
					    .two = arg->two,		\
					    .three = arg->three,	\
					    .four = arg->four,		\
					}
#define INIT_STRUCT_dynamic_partial	= { .two = arg->two, }
#define INIT_STRUCT_dynamic_all		= { .one = arg->one,		\
					    .two = arg->two,		\
					    .three = arg->three,	\
					    .four = arg->four,		\
					}
#define INIT_STRUCT_runtime_partial	;				\
					var.two = 0
#define INIT_STRUCT_runtime_all		;				\
					var.one = 0;			\
					var.two = 0;			\
					var.three = 0;			\
					memset(&var.four, 0,		\
					       sizeof(var.four))

/*
 * @name: unique string name for the test
 * @var_type: type to be tested for zeroing initialization
 * @which: is this a SCALAR, STRING, or STRUCT type?
 * @init_level: what kind of initialization is performed
 * @xfail: is this test expected to fail?
 */
#define DEFINE_TEST_DRIVER(name, var_type, which, xfail)	\
/* Returns 0 on success, 1 on failure. */			\
static noinline __init int test_ ## name (void)			\
{								\
	var_type zero INIT_CLONE_ ## which;			\
	int ignored;						\
	u8 sum = 0, i;						\
								\
	/* Notice when a new test is larger than expected. */	\
	BUILD_BUG_ON(sizeof(zero) > MAX_VAR_SIZE);		\
								\
	/* Fill clone type with zero for per-field init. */	\
	memset(&zero, 0x00, sizeof(zero));			\
	/* Clear entire check buffer for 0xFF overlap test. */	\
	memset(check_buf, 0x00, sizeof(check_buf));		\
	/* Fill stack with 0xFF. */				\
	ignored = leaf_ ##name((unsigned long)&ignored, 1,	\
				FETCH_ARG_ ## which(zero));	\
	/* Verify all bytes overwritten with 0xFF. */		\
	for (sum = 0, i = 0; i < target_size; i++)		\
		sum += (check_buf[i] != 0xFF);			\
	if (sum) {						\
		pr_err(#name ": leaf fill was not 0xFF!?\n");	\
		return 1;					\
	}							\
	/* Clear entire check buffer for later bit tests. */	\
	memset(check_buf, 0x00, sizeof(check_buf));		\
	/* Extract stack-defined variable contents. */		\
	ignored = leaf_ ##name((unsigned long)&ignored, 0,	\
				FETCH_ARG_ ## which(zero));	\
								\
	/* Validate that compiler lined up fill and target. */	\
	if (!range_contains(fill_start, fill_size,		\
			    target_start, target_size)) {	\
		pr_err(#name ": stack fill missed target!?\n");	\
		pr_err(#name ": fill %zu wide\n", fill_size);	\
		pr_err(#name ": target offset by %d\n",	\
			(int)((ssize_t)(uintptr_t)fill_start -	\
			(ssize_t)(uintptr_t)target_start));	\
		return 1;					\
	}							\
								\
	/* Look for any bytes still 0xFF in check region. */	\
	for (sum = 0, i = 0; i < target_size; i++)		\
		sum += (check_buf[i] == 0xFF);			\
								\
	if (sum == 0) {						\
		pr_info(#name " ok\n");				\
		return 0;					\
	} else {						\
		pr_warn(#name " %sFAIL (uninit bytes: %d)\n",	\
			(xfail) ? "X" : "", sum);		\
		return (xfail) ? 0 : 1;				\
	}							\
}
#define DEFINE_TEST(name, var_type, which, init_level)		\
/* no-op to force compiler into ignoring "uninitialized" vars */\
static noinline __init DO_NOTHING_TYPE_ ## which(var_type)	\
do_nothing_ ## name(var_type *ptr)				\
{								\
	/* Will always be true, but compiler doesn't know. */	\
	if ((unsigned long)ptr > 0x2)				\
		return DO_NOTHING_RETURN_ ## which(ptr);	\
	else							\
		return DO_NOTHING_RETURN_ ## which(ptr + 1);	\
}								\
static noinline __init int leaf_ ## name(unsigned long sp,	\
					 bool fill,		\
					 var_type *arg)		\
{								\
	char buf[VAR_BUFFER];					\
	var_type var INIT_ ## which ## _ ## init_level;		\
								\
	target_start = &var;					\
	target_size = sizeof(var);				\
	/*							\
	 * Keep this buffer around to make sure we've got a	\
	 * stack frame of SOME kind...				\
	 */							\
	memset(buf, (char)(sp & 0xff), sizeof(buf));		\
	/* Fill variable with 0xFF. */				\
	if (fill) {						\
		fill_start = &var;				\
		fill_size = sizeof(var);			\
		memset(fill_start,				\
		       (char)((sp & 0xff) | forced_mask),	\
		       fill_size);				\
	}							\
								\
	/* Silence "never initialized" warnings. */		\
	DO_NOTHING_CALL_ ## which(var, name);			\
								\
	/* Exfiltrate "var". */					\
	memcpy(check_buf, target_start, target_size);		\
								\
	return (int)buf[0] | (int)buf[sizeof(buf) - 1];		\
}								\
DEFINE_TEST_DRIVER(name, var_type, which, 0)

/* Structure with no padding. */
struct test_packed {
	unsigned long one;
	unsigned long two;
	unsigned long three;
	unsigned long four;
};

/* Simple structure with padding likely to be covered by compiler. */
struct test_small_hole {
	size_t one;
	char two;
	/* 3 byte padding hole here. */
	int three;
	unsigned long four;
};

/* Try to trigger unhandled padding in a structure. */
struct test_aligned {
	u32 internal1;
	u64 internal2;
} __aligned(64);

struct test_big_hole {
	u8 one;
	u8 two;
	u8 three;
	/* 61 byte padding hole here. */
	struct test_aligned four;
} __aligned(64);

struct test_trailing_hole {
	char *one;
	char *two;
	char *three;
	char four;
	/* "sizeof(unsigned long) - 1" byte padding hole here. */
};

/* Test if STRUCTLEAK is clearing structs with __user fields. */
struct test_user {
	u8 one;
	unsigned long two;
	char __user *three;
	unsigned long four;
};

#define DEFINE_SCALAR_TEST(name, init)				\
		DEFINE_TEST(name ## _ ## init, name, SCALAR, init)

#define DEFINE_SCALAR_TESTS(init)				\
		DEFINE_SCALAR_TEST(u8, init);			\
		DEFINE_SCALAR_TEST(u16, init);			\
		DEFINE_SCALAR_TEST(u32, init);			\
		DEFINE_SCALAR_TEST(u64, init);			\
		DEFINE_TEST(char_array_ ## init, unsigned char, STRING, init)

#define DEFINE_STRUCT_TEST(name, init)				\
		DEFINE_TEST(name ## _ ## init,			\
			    struct test_ ## name, STRUCT, init)

#define DEFINE_STRUCT_TESTS(init)				\
		DEFINE_STRUCT_TEST(small_hole, init);		\
		DEFINE_STRUCT_TEST(big_hole, init);		\
		DEFINE_STRUCT_TEST(trailing_hole, init);	\
		DEFINE_STRUCT_TEST(packed, init)

/* These should be fully initialized all the time! */
DEFINE_SCALAR_TESTS(zero);
DEFINE_STRUCT_TESTS(zero);
/* Static initialization: padding may be left uninitialized. */
DEFINE_STRUCT_TESTS(static_partial);
DEFINE_STRUCT_TESTS(static_all);
/* Dynamic initialization: padding may be left uninitialized. */
DEFINE_STRUCT_TESTS(dynamic_partial);
DEFINE_STRUCT_TESTS(dynamic_all);
/* Runtime initialization: padding may be left uninitialized. */
DEFINE_STRUCT_TESTS(runtime_partial);
DEFINE_STRUCT_TESTS(runtime_all);
/* No initialization without compiler instrumentation. */
DEFINE_SCALAR_TESTS(none);
DEFINE_STRUCT_TESTS(none);
DEFINE_TEST(user, struct test_user, STRUCT, none);

/*
 * Check two uses through a variable declaration outside either path,
 * which was noticed as a special case in porting earlier stack init
 * compiler logic.
 */
static int noinline __leaf_switch_none(int path, bool fill)
{
	switch (path) {
		uint64_t var;

	case 1:
		target_start = &var;
		target_size = sizeof(var);
		if (fill) {
			fill_start = &var;
			fill_size = sizeof(var);

			memset(fill_start, forced_mask | 0x55, fill_size);
		}
		memcpy(check_buf, target_start, target_size);
		break;
	case 2:
		target_start = &var;
		target_size = sizeof(var);
		if (fill) {
			fill_start = &var;
			fill_size = sizeof(var);

			memset(fill_start, forced_mask | 0xaa, fill_size);
		}
		memcpy(check_buf, target_start, target_size);
		break;
	default:
		var = 5;
		return var & forced_mask;
	}
	return 0;
}

static noinline __init int leaf_switch_1_none(unsigned long sp, bool fill,
					      uint64_t *arg)
{
	return __leaf_switch_none(1, fill);
}

static noinline __init int leaf_switch_2_none(unsigned long sp, bool fill,
					      uint64_t *arg)
{
	return __leaf_switch_none(2, fill);
}

/*
 * These are expected to fail for most configurations because neither
 * GCC nor Clang have a way to perform initialization of variables in
 * non-code areas (i.e. in a switch statement before the first "case").
 * https://bugs.llvm.org/show_bug.cgi?id=44916
 */
DEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, 1);
DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, 1);

static int __init test_stackinit_init(void)
{
	unsigned int failures = 0;

#define test_scalars(init)	do {				\
		failures += test_u8_ ## init ();		\
		failures += test_u16_ ## init ();		\
		failures += test_u32_ ## init ();		\
		failures += test_u64_ ## init ();		\
		failures += test_char_array_ ## init ();	\
	} while (0)

#define test_structs(init)	do {				\
		failures += test_small_hole_ ## init ();	\
		failures += test_big_hole_ ## init ();		\
		failures += test_trailing_hole_ ## init ();	\
		failures += test_packed_ ## init ();		\
	} while (0)

	/* These are explicitly initialized and should always pass. */
	test_scalars(zero);
	test_structs(zero);
	/* Padding here appears to be accidentally always initialized? */
	test_structs(dynamic_partial);
	/* Padding initialization depends on compiler behaviors. */
	test_structs(static_partial);
	test_structs(static_all);
	test_structs(dynamic_all);
	test_structs(runtime_partial);
	test_structs(runtime_all);

	/* STRUCTLEAK_BYREF_ALL should cover everything from here down. */
	test_scalars(none);
	failures += test_switch_1_none();
	failures += test_switch_2_none();

	/* STRUCTLEAK_BYREF should cover from here down. */
	test_structs(none);

	/* STRUCTLEAK will only cover this. */
	failures += test_user();

	if (failures == 0)
		pr_info("all tests passed!\n");
	else
		pr_err("failures: %u\n", failures);

	return failures ? -EINVAL : 0;
}

int main(void)
{
	return test_stackinit_init();
}

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-13 21:29     ` Kees Cook
@ 2021-07-13 23:09       ` Kees Cook
  2021-07-13 23:16       ` Qing Zhao
  1 sibling, 0 replies; 25+ messages in thread
From: Kees Cook @ 2021-07-13 23:09 UTC (permalink / raw)
  To: Qing Zhao; +Cc: Richard Biener, Richard Sandiford, gcc-patches Qing Zhao via

[-- Attachment #1: Type: text/plain, Size: 3382 bytes --]

On Tue, Jul 13, 2021 at 02:29:33PM -0700, Kees Cook wrote:
> I've extracted the kernel test to build for userspace, and it behaves
> the same way. See attached "stackinit.c".

I've adjusted this slightly (the "static" tests weren't testing the
correct thing, but the results remained the same). Here's what I see.

This is the variable on the stack:

struct test_small_hole {
        size_t                     one;                  /*     0     8 */
        char                       two;                  /*     8     1 */

        /* XXX 3 bytes hole, try to pack */

        int                        three;                /*    12     4 */
        long unsigned int          four;                 /*    16     8 */

        /* size: 24, cachelines: 1, members: 4 */
        /* sum members: 21, holes: 1, sum holes: 3 */
        /* last cacheline: 24 bytes */
};

The above is 0x18 in size.

0000000000405370 <leaf_small_hole_static_all.constprop.0>:
  405370:       40 0f b6 c7             movzbl %dil,%eax
  405374:       41 89 f1                mov    %esi,%r9d
  405377:       48 8d 74 24 b8          lea    -0x48(%rsp),%rsi

-0x48(%rsp) is the location of the variable.

  40537c:       44 0f be c7             movsbl %dil,%r8d
  405380:       48 b9 01 01 01 01 01    movabs $0x101010101010101,%rcx
  405387:       01 01 01 
  40538a:       49 89 c2                mov    %rax,%r10
  40538d:       48 c7 44 24 b8 00 00    movq   $0x0,-0x48(%rsp)
  405394:       00 00 

8 byte move of 0 to -0x48(%rsp) through -0x41

  405396:       48 f7 e1                mul    %rcx
  405399:       c6 44 24 c0 00          movb   $0x0,-0x40(%rsp)

1 byte move of 0 to -0x40(%rsp)

  40539e:       4c 0f af d1             imul   %rcx,%r10
  4053a2:       c7 44 24 c4 00 00 00    movl   $0x0,-0x3c(%rsp)
  4053a9:       00 

4 byte move of 0 to -0x3c through -0x39 (note that -0x3f, -0x3e,
-0x3d is _not_ written, which maps to the 3 byte struct hole).

  4053aa:       48 c7 44 24 c8 00 00    movq   $0x0,-0x38(%rsp)
  4053b1:       00 00 

8 byte move of 0 to -0x38(%rsp) through -0x31.

  4053b3:       48 89 35 d6 9c 00 00    mov    %rsi,0x9cd6(%rip) # 40f090 <target_start>

variable address is saved to global.

  4053ba:       4c 01 d2                add    %r10,%rdx
  4053bd:       48 89 44 24 d8          mov    %rax,-0x28(%rsp)
  4053c2:       48 c7 05 b3 9c 00 00    movq   $0x18,0x9cb3(%rip) # 40f080 <target_size>
  4053c9:       18 00 00 00 

variable size is saved to global.

  4053cd:       48 89 54 24 e0          mov    %rdx,-0x20(%rsp)
  4053d2:       48 89 44 24 e8          mov    %rax,-0x18(%rsp)
  4053d7:       48 89 54 24 f0          mov    %rdx,-0x10(%rsp)
  4053dc:       45 84 c9                test   %r9b,%r9b
  4053df:       75 1d                   jne    4053fe <leaf_small_hole_static_all.constprop.0+0x8e>
  4053e1:       48 8b 44 24 c8          mov    -0x38(%rsp),%rax
  4053e6:       66 0f 6f 44 24 b8       movdqa -0x48(%rsp),%xmm0
  4053ec:       48 89 05 bd 9c 00 00    mov    %rax,0x9cbd(%rip) # 40f0b0 <check_buf+0x10>
  4053f3:       44 89 c0                mov    %r8d,%eax
  4053f6:       0f 29 05 a3 9c 00 00    movaps %xmm0,0x9ca3(%rip) # 40f0a0 <check_buf>

Here's the unrolled memcpy (8 bytes and 16 bytes) to the global buffer,
taking the "uninitialized" padding with it.

  4053fd:       c3                      ret    

-- 
Kees Cook

[-- Attachment #2: stackinit.c --]
[-- Type: text/x-csrc, Size: 12763 bytes --]

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Test cases for compiler-based stack variable zeroing via
 * -ftrivial-auto-var-init={zero,pattern} or CONFIG_GCC_PLUGIN_STRUCTLEAK*.
 *
 * Build example:
 * gcc -O2 -Wall -ftrivial-auto-var-init=zero -o stackinit stackinit.c
 */

/* Userspace headers. */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>

/* Linux kernel-isms */
#define KBUILD_MODNAME		"stackinit"
#define pr_fmt(fmt)		KBUILD_MODNAME ": " fmt
#define pr_err(fmt, ...)	fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warn(fmt, ...)	fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__)
#define pr_info(fmt, ...)	fprintf(stdout, pr_fmt(fmt), ##__VA_ARGS__)
#define __init			/**/
#define __user			/**/
#define noinline		__attribute__((__noinline__))
#define __aligned(x)		__attribute__((__aligned__(x)))
#ifdef __clang__
# define __compiletime_error(message) /**/
#else
# define __compiletime_error(message) __attribute__((__error__(message)))
#endif
#define __compiletime_assert(condition, msg, prefix, suffix)		\
	do {								\
		extern void prefix ## suffix(void) __compiletime_error(msg); \
		if (!(condition))					\
			prefix ## suffix();				\
	} while (0)
#define _compiletime_assert(condition, msg, prefix, suffix) \
	__compiletime_assert(condition, msg, prefix, suffix)
#define compiletime_assert(condition, msg) \
	_compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
#define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
#define BUILD_BUG_ON(condition) \
	BUILD_BUG_ON_MSG(condition, "BUILD_BUG_ON failed: " #condition)
typedef uint8_t			u8;
typedef uint16_t		u16;
typedef uint32_t		u32;
typedef uint64_t		u64;


/* Exfiltration buffer. */
#define MAX_VAR_SIZE	128
static u8 check_buf[MAX_VAR_SIZE];

/* Character array to trigger stack protector in all functions. */
#define VAR_BUFFER	 32

/* Volatile mask to convince compiler to copy memory with 0xff. */
static volatile u8 forced_mask = 0xff;

/* Location and size tracking to validate fill and test are colocated. */
static void *fill_start, *target_start;
static size_t fill_size, target_size;

static bool range_contains(char *haystack_start, size_t haystack_size,
			   char *needle_start, size_t needle_size)
{
	if (needle_start >= haystack_start &&
	    needle_start + needle_size <= haystack_start + haystack_size)
		return true;
	return false;
}

#define DO_NOTHING_TYPE_SCALAR(var_type)	var_type
#define DO_NOTHING_TYPE_STRING(var_type)	void
#define DO_NOTHING_TYPE_STRUCT(var_type)	void

#define DO_NOTHING_RETURN_SCALAR(ptr)		*(ptr)
#define DO_NOTHING_RETURN_STRING(ptr)		/**/
#define DO_NOTHING_RETURN_STRUCT(ptr)		/**/

#define DO_NOTHING_CALL_SCALAR(var, name)			\
		(var) = do_nothing_ ## name(&(var))
#define DO_NOTHING_CALL_STRING(var, name)			\
		do_nothing_ ## name(var)
#define DO_NOTHING_CALL_STRUCT(var, name)			\
		do_nothing_ ## name(&(var))

#define FETCH_ARG_SCALAR(var)		&var
#define FETCH_ARG_STRING(var)		var
#define FETCH_ARG_STRUCT(var)		&var

#define FILL_SIZE_STRING		16

#define INIT_CLONE_SCALAR		/**/
#define INIT_CLONE_STRING		[FILL_SIZE_STRING]
#define INIT_CLONE_STRUCT		/**/

#define INIT_SCALAR_none		/**/
#define INIT_SCALAR_zero		= 0

#define INIT_STRING_none		[FILL_SIZE_STRING] /**/
#define INIT_STRING_zero		[FILL_SIZE_STRING] = { }

#define INIT_STRUCT_none		/**/
#define INIT_STRUCT_zero		= { }
#define INIT_STRUCT_static_partial	= { .two = 0, }
#define INIT_STRUCT_static_all		= { .one = 0,			\
					    .two = 0,			\
					    .three = 0,			\
					    .four = 0,			\
					}
#define INIT_STRUCT_dynamic_partial	= { .two = arg->two, }
#define INIT_STRUCT_dynamic_all		= { .one = arg->one,		\
					    .two = arg->two,		\
					    .three = arg->three,	\
					    .four = arg->four,		\
					}
#define INIT_STRUCT_runtime_partial	;				\
					var.two = 0
#define INIT_STRUCT_runtime_all		;				\
					var.one = 0;			\
					var.two = 0;			\
					var.three = 0;			\
					memset(&var.four, 0,		\
					       sizeof(var.four))

/*
 * @name: unique string name for the test
 * @var_type: type to be tested for zeroing initialization
 * @which: is this a SCALAR, STRING, or STRUCT type?
 * @init_level: what kind of initialization is performed
 * @xfail: is this test expected to fail?
 */
#define DEFINE_TEST_DRIVER(name, var_type, which, xfail)	\
/* Returns 0 on success, 1 on failure. */			\
static noinline __init int test_ ## name (void)			\
{								\
	var_type zero INIT_CLONE_ ## which;			\
	int ignored;						\
	u8 sum = 0, i;						\
								\
	/* Notice when a new test is larger than expected. */	\
	BUILD_BUG_ON(sizeof(zero) > MAX_VAR_SIZE);		\
								\
	/* Fill clone type with zero for per-field init. */	\
	memset(&zero, 0x00, sizeof(zero));			\
	/* Clear entire check buffer for 0xFF overlap test. */	\
	memset(check_buf, 0x00, sizeof(check_buf));		\
	/* Fill stack with 0xFF. */				\
	ignored = leaf_ ##name((unsigned long)&ignored, 1,	\
				FETCH_ARG_ ## which(zero));	\
	/* Verify all bytes overwritten with 0xFF. */		\
	for (sum = 0, i = 0; i < target_size; i++)		\
		sum += (check_buf[i] != 0xFF);			\
	if (sum) {						\
		pr_err(#name ": leaf fill was not 0xFF!?\n");	\
		return 1;					\
	}							\
	/* Clear entire check buffer for later bit tests. */	\
	memset(check_buf, 0x00, sizeof(check_buf));		\
	/* Extract stack-defined variable contents. */		\
	ignored = leaf_ ##name((unsigned long)&ignored, 0,	\
				FETCH_ARG_ ## which(zero));	\
								\
	/* Validate that compiler lined up fill and target. */	\
	if (!range_contains(fill_start, fill_size,		\
			    target_start, target_size)) {	\
		pr_err(#name ": stack fill missed target!?\n");	\
		pr_err(#name ": fill %zu wide\n", fill_size);	\
		pr_err(#name ": target offset by %d\n",	\
			(int)((ssize_t)(uintptr_t)fill_start -	\
			(ssize_t)(uintptr_t)target_start));	\
		return 1;					\
	}							\
								\
	/* Look for any bytes still 0xFF in check region. */	\
	for (sum = 0, i = 0; i < target_size; i++)		\
		sum += (check_buf[i] == 0xFF);			\
								\
	if (sum == 0) {						\
		pr_info(#name " ok\n");				\
		return 0;					\
	} else {						\
		pr_warn(#name " %sFAIL (uninit bytes: %d)\n",	\
			(xfail) ? "X" : "", sum);		\
		return (xfail) ? 0 : 1;				\
	}							\
}
#define DEFINE_TEST(name, var_type, which, init_level)		\
/* no-op to force compiler into ignoring "uninitialized" vars */\
static noinline __init DO_NOTHING_TYPE_ ## which(var_type)	\
do_nothing_ ## name(var_type *ptr)				\
{								\
	/* Will always be true, but compiler doesn't know. */	\
	if ((unsigned long)ptr > 0x2)				\
		return DO_NOTHING_RETURN_ ## which(ptr);	\
	else							\
		return DO_NOTHING_RETURN_ ## which(ptr + 1);	\
}								\
static noinline __init int leaf_ ## name(unsigned long sp,	\
					 bool fill,		\
					 var_type *arg)		\
{								\
	char buf[VAR_BUFFER];					\
	var_type var INIT_ ## which ## _ ## init_level;		\
								\
	target_start = &var;					\
	target_size = sizeof(var);				\
	/*							\
	 * Keep this buffer around to make sure we've got a	\
	 * stack frame of SOME kind...				\
	 */							\
	memset(buf, (char)(sp & 0xff), sizeof(buf));		\
	/* Fill variable with 0xFF. */				\
	if (fill) {						\
		fill_start = &var;				\
		fill_size = sizeof(var);			\
		memset(fill_start,				\
		       (char)((sp & 0xff) | forced_mask),	\
		       fill_size);				\
	}							\
								\
	/* Silence "never initialized" warnings. */		\
	DO_NOTHING_CALL_ ## which(var, name);			\
								\
	/* Exfiltrate "var". */					\
	memcpy(check_buf, target_start, target_size);		\
								\
	return (int)buf[0] | (int)buf[sizeof(buf) - 1];		\
}								\
DEFINE_TEST_DRIVER(name, var_type, which, 0)

/* Structure with no padding. */
struct test_packed {
	unsigned long one;
	unsigned long two;
	unsigned long three;
	unsigned long four;
};

/* Simple structure with padding likely to be covered by compiler. */
struct test_small_hole {
	size_t one;
	char two;
	/* 3 byte padding hole here. */
	int three;
	unsigned long four;
};

/* Trigger unhandled padding in a structure. */
struct test_big_hole {
	u8 one;
	u8 two;
	u8 three;
	/* 61 byte padding hole here. */
	u8 four __aligned(64);
} __aligned(64);

struct test_trailing_hole {
	char *one;
	char *two;
	char *three;
	char four;
	/* "sizeof(unsigned long) - 1" byte padding hole here. */
};

/* Test if STRUCTLEAK is clearing structs with __user fields. */
struct test_user {
	u8 one;
	unsigned long two;
	char __user *three;
	unsigned long four;
};

#define DEFINE_SCALAR_TEST(name, init)				\
		DEFINE_TEST(name ## _ ## init, name, SCALAR, init)

#define DEFINE_SCALAR_TESTS(init)				\
		DEFINE_SCALAR_TEST(u8, init);			\
		DEFINE_SCALAR_TEST(u16, init);			\
		DEFINE_SCALAR_TEST(u32, init);			\
		DEFINE_SCALAR_TEST(u64, init);			\
		DEFINE_TEST(char_array_ ## init, unsigned char, STRING, init)

#define DEFINE_STRUCT_TEST(name, init)				\
		DEFINE_TEST(name ## _ ## init,			\
			    struct test_ ## name, STRUCT, init)

#define DEFINE_STRUCT_TESTS(init)				\
		DEFINE_STRUCT_TEST(small_hole, init);		\
		DEFINE_STRUCT_TEST(big_hole, init);		\
		DEFINE_STRUCT_TEST(trailing_hole, init);	\
		DEFINE_STRUCT_TEST(packed, init)


/* These should be fully initialized all the time! */
DEFINE_SCALAR_TESTS(zero);
DEFINE_STRUCT_TESTS(zero);
/* Static initialization: padding may be left uninitialized. */
DEFINE_STRUCT_TESTS(static_partial);
DEFINE_STRUCT_TESTS(static_all);
/* Dynamic initialization: padding may be left uninitialized. */
DEFINE_STRUCT_TESTS(dynamic_partial);
DEFINE_STRUCT_TESTS(dynamic_all);
/* Runtime initialization: padding may be left uninitialized. */
DEFINE_STRUCT_TESTS(runtime_partial);
DEFINE_STRUCT_TESTS(runtime_all);
/* No initialization without compiler instrumentation. */
DEFINE_SCALAR_TESTS(none);
DEFINE_STRUCT_TESTS(none);
DEFINE_TEST(user, struct test_user, STRUCT, none);

/*
 * Check two uses through a variable declaration outside either path,
 * which was noticed as a special case in porting earlier stack init
 * compiler logic.
 */
static int noinline __leaf_switch_none(int path, bool fill)
{
	switch (path) {
		uint64_t var;

	case 1:
		target_start = &var;
		target_size = sizeof(var);
		if (fill) {
			fill_start = &var;
			fill_size = sizeof(var);

			memset(fill_start, forced_mask | 0x55, fill_size);
		}
		memcpy(check_buf, target_start, target_size);
		break;
	case 2:
		target_start = &var;
		target_size = sizeof(var);
		if (fill) {
			fill_start = &var;
			fill_size = sizeof(var);

			memset(fill_start, forced_mask | 0xaa, fill_size);
		}
		memcpy(check_buf, target_start, target_size);
		break;
	default:
		var = 5;
		return var & forced_mask;
	}
	return 0;
}

static noinline __init int leaf_switch_1_none(unsigned long sp, bool fill,
					      uint64_t *arg)
{
	return __leaf_switch_none(1, fill);
}

static noinline __init int leaf_switch_2_none(unsigned long sp, bool fill,
					      uint64_t *arg)
{
	return __leaf_switch_none(2, fill);
}

/*
 * These are expected to fail for most configurations because neither
 * GCC nor Clang have a way to perform initialization of variables in
 * non-code areas (i.e. in a switch statement before the first "case").
 * https://bugs.llvm.org/show_bug.cgi?id=44916
 */
DEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, 1);
DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, 1);

static int __init test_stackinit_init(void)
{
	unsigned int failures = 0;

#define test_scalars(init)	do {				\
		failures += test_u8_ ## init ();		\
		failures += test_u16_ ## init ();		\
		failures += test_u32_ ## init ();		\
		failures += test_u64_ ## init ();		\
		failures += test_char_array_ ## init ();	\
	} while (0)

#define test_structs(init)	do {				\
		failures += test_small_hole_ ## init ();	\
		failures += test_big_hole_ ## init ();		\
		failures += test_trailing_hole_ ## init ();	\
		failures += test_packed_ ## init ();		\
	} while (0)


	/* These are explicitly initialized and should always pass. */
	test_scalars(zero);
	test_structs(zero);
	/* Padding here appears to be accidentally always initialized? */
	test_structs(dynamic_partial);
	/* Padding initialization depends on compiler behaviors. */
	test_structs(static_partial);
	test_structs(static_all);
	test_structs(dynamic_all);
	test_structs(runtime_partial);
	test_structs(runtime_all);

	/* STRUCTLEAK_BYREF_ALL should cover everything from here down. */
	test_scalars(none);
	failures += test_switch_1_none();
	failures += test_switch_2_none();

	/* STRUCTLEAK_BYREF should cover from here down. */
	test_structs(none);

	/* STRUCTLEAK will only cover this. */
	failures += test_user();

	if (failures == 0)
		pr_info("all tests passed!\n");
	else
		pr_err("failures: %u\n", failures);

	return failures ? -EINVAL : 0;
}

int main(void)
{
	return test_stackinit_init();
}

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-13 21:29     ` Kees Cook
  2021-07-13 23:09       ` Kees Cook
@ 2021-07-13 23:16       ` Qing Zhao
  2021-07-14  2:42         ` Kees Cook
  2021-07-14  7:14         ` Richard Biener
  1 sibling, 2 replies; 25+ messages in thread
From: Qing Zhao @ 2021-07-13 23:16 UTC (permalink / raw)
  To: Kees Cook, Richard Biener; +Cc: Richard Sandiford, gcc-patches Qing Zhao via

Hi, Kees,

I took a look at the kernel testing case you attached in the previous email, and found the testing failed with the following case:

#define INIT_STRUCT_static_all          = { .one = arg->one,            \
                                            .two = arg->two,            \
                                            .three = arg->three,        \
                                            .four = arg->four,          \
                                        }

i.e, when the structure type auto variable has been explicitly initialized in the source code.  -ftrivial-auto-var-init in the 4th version
does not initialize the paddings for such variables.  

But in the previous version of the patches ( 2 or 3), -ftrivial-auto-var-init initializes the paddings for such variables.

I intended to remove this part of the code from the 4th version of the patch since the implementation for initializing such paddings is completely different from 
the initializing of the whole structure as a whole with memset in this version of the implementation. 

If we really need this functionality, I will add another separate patch for this additional functionality, but not with this patch.

Richard, what’s your comment and suggestions on this?

Thanks.

Qing

> On Jul 13, 2021, at 4:29 PM, Kees Cook <keescook@chromium.org> wrote:
> 
> On Mon, Jul 12, 2021 at 08:28:55PM +0000, Qing Zhao wrote:
>>> On Jul 12, 2021, at 12:56 PM, Kees Cook <keescook@chromium.org> wrote:
>>> On Wed, Jul 07, 2021 at 05:38:02PM +0000, Qing Zhao wrote:
>>>> This is the 4th version of the patch for the new security feature for GCC.
>>> 
>>> It looks like padding initialization has regressed to where things where
>>> in version 1[1] (it was, however, working in version 2[2]). I'm seeing
>>> these failures again in the kernel self-test:
>>> 
>>> test_stackinit: small_hole_static_all FAIL (uninit bytes: 3)
>>> test_stackinit: big_hole_static_all FAIL (uninit bytes: 61)
>>> test_stackinit: trailing_hole_static_all FAIL (uninit bytes: 7)
>>> test_stackinit: small_hole_dynamic_all FAIL (uninit bytes: 3)
>>> test_stackinit: big_hole_dynamic_all FAIL (uninit bytes: 61)
>>> test_stackinit: trailing_hole_dynamic_all FAIL (uninit bytes: 7)
>> 
>> Are the above failures for -ftrivial-auto-var-init=zero or -ftrivial-auto-var-init=pattern?  Or both?
> 
> Yes, I was only testing =zero (the kernel test handles =pattern as well:
> it doesn't explicitly test for 0x00). I've verified with =pattern now,
> too.
> 
>> For the current implementation, I believe that all paddings should be initialized with this option, 
>> for -ftrivial-auto-var-init=zero, the padding will be initialized to zero as before, however, for
>> -ftrivial-auto-var-init=pattern, the padding will be initialized to 0xFE byte-repeatable patterns.
> 
> I've double-checked that I'm using the right gcc, with the flag.
> 
>>> 
>>> In looking at the gcc test cases, I think the wrong thing is
>>> being checked: we want to verify the padding itself. For example,
>>> in auto-init-17.c, the actual bytes after "four" need to be checked,
>>> rather than "four" itself.
>> 
>> ******For the current auto-init-17.c
>> 
>>  1 /* Verify zero initialization for array type with structure element with
>>  2    padding.  */
>>  3 /* { dg-do compile } */
>>  4 /* { dg-options "-ftrivial-auto-var-init=zero" } */
>>  5 
>>  6 struct test_trailing_hole {
>>  7         int one;
>>  8         int two;
>>  9         int three;
>> 10         char four;
>> 11         /* "sizeof(unsigned long) - 1" byte padding hole here. */
>> 12 };
>> 13 
>> 14 
>> 15 int foo ()
>> 16 {
>> 17   struct test_trailing_hole var[10];
>> 18   return var[2].four;
>> 19 }
>> 20 
>> 21 /* { dg-final { scan-assembler "movl\t\\\$0," } } */
>> 22 /* { dg-final { scan-assembler "movl\t\\\$20," } } */
>> 23 /* { dg-final { scan-assembler "rep stosq" } } */
>> ~  
>> ******We have the assembly as: (-ftrivial-auto-var-init=zero)
>> 
>>        .file   "auto-init-17.c"
>>        .text
>>        .globl  foo
>>        .type   foo, @function
>> foo:
>> .LFB0:
>>        .cfi_startproc
>>        pushq   %rbp
>>        .cfi_def_cfa_offset 16
>>        .cfi_offset 6, -16
>>        movq    %rsp, %rbp
>>        .cfi_def_cfa_register 6
>>        subq    $40, %rsp
>>        leaq    -160(%rbp), %rax
>>        movq    %rax, %rsi
>>        movl    $0, %eax
>>        movl    $20, %edx
>>        movq    %rsi, %rdi
>>        movq    %rdx, %rcx
>>        rep stosq
>>        movzbl  -116(%rbp), %eax
>>        movsbl  %al, %eax
>>        leave
>>        .cfi_def_cfa 7, 8
>>        ret
>>        .cfi_endproc
>> .LFE0:
>>        .size   foo, .-foo
>>        .section        .note.GNU-stack,"",@progbits
>> 
>> From the above, we can see,  “zero” will be used to initialize 8 * 20 = 16 * 10 bytes of memory starting from the beginning of “var”, that include all the padding holes inside
>> This array of structure. 
>> 
>> I didn’t see issue with padding initialization here.
> 
> Hm, agreed -- this test does do the right thing.
> 
>>> But this isn't actually sufficient because they may _accidentally_
>>> be zero already. The kernel tests specifically make sure to fill the
>>> about-to-be-used stack with 0xff before calling a function like foo()
>>> above.
> 
> I've extracted the kernel test to build for userspace, and it behaves
> the same way. See attached "stackinit.c".
> 
> $ gcc-build/auto-var-init.4/installed/bin/gcc -O2 -Wall -o stackinit stackinit.c
> $ ./stackinit 2>&1 | grep failures:
> stackinit: failures: 23
> $ gcc-build/auto-var-init.4/installed/bin/gcc -O2 -Wall -ftrivial-auto-var-init=zero -o stackinit stackinit.c
> stackinit.c: In function ‘__leaf_switch_none’:
> stackinit.c:326:26: warning: statement will never be executed
> [-Wswitch-unreachable]
>  326 |                 uint64_t var;
>      |                          ^~~
> $ ./stackinit 2>&1 | grep failures:
> stackinit: failures: 6
> 
> Same failures as seen in the kernel test (and an expected warning
> about the initialization that will never happen for a pre-case switch
> statement).
> 
>>> 
>>> (And as an aside, it seems like naming the test cases with some details
>>> about what is being tested in the filename would be nice -- it was
>>> a little weird having to dig through their numeric names to find the
>>> padding tests.)
>> 
>> Yes, I will fix the testing names to more reflect the testing details. 
> 
> Great!
> 
> -- 
> Kees Cook
> <stackinit.c>


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-13 23:16       ` Qing Zhao
@ 2021-07-14  2:42         ` Kees Cook
  2021-07-14  7:14         ` Richard Biener
  1 sibling, 0 replies; 25+ messages in thread
From: Kees Cook @ 2021-07-14  2:42 UTC (permalink / raw)
  To: Qing Zhao; +Cc: Richard Biener, Richard Sandiford, gcc-patches Qing Zhao via

On Tue, Jul 13, 2021 at 11:16:59PM +0000, Qing Zhao wrote:
> Hi, Kees,
> 
> I took a look at the kernel testing case you attached in the previous email, and found the testing failed with the following case:
> 
> #define INIT_STRUCT_static_all          = { .one = arg->one,            \
>                                             .two = arg->two,            \
>                                             .three = arg->three,        \
>                                             .four = arg->four,          \
>                                         }
> 
> i.e, when the structure type auto variable has been explicitly initialized in the source code.  -ftrivial-auto-var-init in the 4th version
> does not initialize the paddings for such variables.  
> 
> But in the previous version of the patches ( 2 or 3), -ftrivial-auto-var-init initializes the paddings for such variables.
> 
> I intended to remove this part of the code from the 4th version of the patch since the implementation for initializing such paddings is completely different from 
> the initializing of the whole structure as a whole with memset in this version of the implementation. 
> 
> If we really need this functionality, I will add another separate patch for this additional functionality, but not with this patch.

Yes, this is required to get proper coverage for initialization in the
kernel (or, really, any program). Without this, things are still left
uninitialized in the padding of structs.

A separate patch is fine by me; my only desire is to still have it be
part of -ftrivial-auto-var-init when it's all done. :)

Thanks!

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-13 23:16       ` Qing Zhao
  2021-07-14  2:42         ` Kees Cook
@ 2021-07-14  7:14         ` Richard Biener
  2021-07-14 14:09           ` Qing Zhao
  1 sibling, 1 reply; 25+ messages in thread
From: Richard Biener @ 2021-07-14  7:14 UTC (permalink / raw)
  To: Qing Zhao; +Cc: Kees Cook, Richard Sandiford, gcc-patches Qing Zhao via

On Wed, Jul 14, 2021 at 1:17 AM Qing Zhao <qing.zhao@oracle.com> wrote:
>
> Hi, Kees,
>
> I took a look at the kernel testing case you attached in the previous email, and found the testing failed with the following case:
>
> #define INIT_STRUCT_static_all          = { .one = arg->one,            \
>                                             .two = arg->two,            \
>                                             .three = arg->three,        \
>                                             .four = arg->four,          \
>                                         }
>
> i.e, when the structure type auto variable has been explicitly initialized in the source code.  -ftrivial-auto-var-init in the 4th version
> does not initialize the paddings for such variables.
>
> But in the previous version of the patches ( 2 or 3), -ftrivial-auto-var-init initializes the paddings for such variables.
>
> I intended to remove this part of the code from the 4th version of the patch since the implementation for initializing such paddings is completely different from
> the initializing of the whole structure as a whole with memset in this version of the implementation.
>
> If we really need this functionality, I will add another separate patch for this additional functionality, but not with this patch.
>
> Richard, what’s your comment and suggestions on this?

I think this can be addressed in the gimplifier by adjusting
gimplify_init_constructor to clear
the object before the initialization (if it's not done via aggregate
copying).  The clearing
could be done via .DEFERRED_INIT.

Note that I think .DEFERRED_INIT can be elided for variables that do
not have their address
taken - otherwise we'll also have to worry about aggregate copy
initialization and SRA
decomposing the copy, initializing only the used parts.

Richard.

> Thanks.
>
> Qing
>
> > On Jul 13, 2021, at 4:29 PM, Kees Cook <keescook@chromium.org> wrote:
> >
> > On Mon, Jul 12, 2021 at 08:28:55PM +0000, Qing Zhao wrote:
> >>> On Jul 12, 2021, at 12:56 PM, Kees Cook <keescook@chromium.org> wrote:
> >>> On Wed, Jul 07, 2021 at 05:38:02PM +0000, Qing Zhao wrote:
> >>>> This is the 4th version of the patch for the new security feature for GCC.
> >>>
> >>> It looks like padding initialization has regressed to where things where
> >>> in version 1[1] (it was, however, working in version 2[2]). I'm seeing
> >>> these failures again in the kernel self-test:
> >>>
> >>> test_stackinit: small_hole_static_all FAIL (uninit bytes: 3)
> >>> test_stackinit: big_hole_static_all FAIL (uninit bytes: 61)
> >>> test_stackinit: trailing_hole_static_all FAIL (uninit bytes: 7)
> >>> test_stackinit: small_hole_dynamic_all FAIL (uninit bytes: 3)
> >>> test_stackinit: big_hole_dynamic_all FAIL (uninit bytes: 61)
> >>> test_stackinit: trailing_hole_dynamic_all FAIL (uninit bytes: 7)
> >>
> >> Are the above failures for -ftrivial-auto-var-init=zero or -ftrivial-auto-var-init=pattern?  Or both?
> >
> > Yes, I was only testing =zero (the kernel test handles =pattern as well:
> > it doesn't explicitly test for 0x00). I've verified with =pattern now,
> > too.
> >
> >> For the current implementation, I believe that all paddings should be initialized with this option,
> >> for -ftrivial-auto-var-init=zero, the padding will be initialized to zero as before, however, for
> >> -ftrivial-auto-var-init=pattern, the padding will be initialized to 0xFE byte-repeatable patterns.
> >
> > I've double-checked that I'm using the right gcc, with the flag.
> >
> >>>
> >>> In looking at the gcc test cases, I think the wrong thing is
> >>> being checked: we want to verify the padding itself. For example,
> >>> in auto-init-17.c, the actual bytes after "four" need to be checked,
> >>> rather than "four" itself.
> >>
> >> ******For the current auto-init-17.c
> >>
> >>  1 /* Verify zero initialization for array type with structure element with
> >>  2    padding.  */
> >>  3 /* { dg-do compile } */
> >>  4 /* { dg-options "-ftrivial-auto-var-init=zero" } */
> >>  5
> >>  6 struct test_trailing_hole {
> >>  7         int one;
> >>  8         int two;
> >>  9         int three;
> >> 10         char four;
> >> 11         /* "sizeof(unsigned long) - 1" byte padding hole here. */
> >> 12 };
> >> 13
> >> 14
> >> 15 int foo ()
> >> 16 {
> >> 17   struct test_trailing_hole var[10];
> >> 18   return var[2].four;
> >> 19 }
> >> 20
> >> 21 /* { dg-final { scan-assembler "movl\t\\\$0," } } */
> >> 22 /* { dg-final { scan-assembler "movl\t\\\$20," } } */
> >> 23 /* { dg-final { scan-assembler "rep stosq" } } */
> >> ~
> >> ******We have the assembly as: (-ftrivial-auto-var-init=zero)
> >>
> >>        .file   "auto-init-17.c"
> >>        .text
> >>        .globl  foo
> >>        .type   foo, @function
> >> foo:
> >> .LFB0:
> >>        .cfi_startproc
> >>        pushq   %rbp
> >>        .cfi_def_cfa_offset 16
> >>        .cfi_offset 6, -16
> >>        movq    %rsp, %rbp
> >>        .cfi_def_cfa_register 6
> >>        subq    $40, %rsp
> >>        leaq    -160(%rbp), %rax
> >>        movq    %rax, %rsi
> >>        movl    $0, %eax
> >>        movl    $20, %edx
> >>        movq    %rsi, %rdi
> >>        movq    %rdx, %rcx
> >>        rep stosq
> >>        movzbl  -116(%rbp), %eax
> >>        movsbl  %al, %eax
> >>        leave
> >>        .cfi_def_cfa 7, 8
> >>        ret
> >>        .cfi_endproc
> >> .LFE0:
> >>        .size   foo, .-foo
> >>        .section        .note.GNU-stack,"",@progbits
> >>
> >> From the above, we can see,  “zero” will be used to initialize 8 * 20 = 16 * 10 bytes of memory starting from the beginning of “var”, that include all the padding holes inside
> >> This array of structure.
> >>
> >> I didn’t see issue with padding initialization here.
> >
> > Hm, agreed -- this test does do the right thing.
> >
> >>> But this isn't actually sufficient because they may _accidentally_
> >>> be zero already. The kernel tests specifically make sure to fill the
> >>> about-to-be-used stack with 0xff before calling a function like foo()
> >>> above.
> >
> > I've extracted the kernel test to build for userspace, and it behaves
> > the same way. See attached "stackinit.c".
> >
> > $ gcc-build/auto-var-init.4/installed/bin/gcc -O2 -Wall -o stackinit stackinit.c
> > $ ./stackinit 2>&1 | grep failures:
> > stackinit: failures: 23
> > $ gcc-build/auto-var-init.4/installed/bin/gcc -O2 -Wall -ftrivial-auto-var-init=zero -o stackinit stackinit.c
> > stackinit.c: In function ‘__leaf_switch_none’:
> > stackinit.c:326:26: warning: statement will never be executed
> > [-Wswitch-unreachable]
> >  326 |                 uint64_t var;
> >      |                          ^~~
> > $ ./stackinit 2>&1 | grep failures:
> > stackinit: failures: 6
> >
> > Same failures as seen in the kernel test (and an expected warning
> > about the initialization that will never happen for a pre-case switch
> > statement).
> >
> >>>
> >>> (And as an aside, it seems like naming the test cases with some details
> >>> about what is being tested in the filename would be nice -- it was
> >>> a little weird having to dig through their numeric names to find the
> >>> padding tests.)
> >>
> >> Yes, I will fix the testing names to more reflect the testing details.
> >
> > Great!
> >
> > --
> > Kees Cook
> > <stackinit.c>
>

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-14  7:14         ` Richard Biener
@ 2021-07-14 14:09           ` Qing Zhao
  2021-07-14 19:11             ` Kees Cook
  2021-07-15  7:56             ` Richard Biener
  0 siblings, 2 replies; 25+ messages in thread
From: Qing Zhao @ 2021-07-14 14:09 UTC (permalink / raw)
  To: Richard Biener; +Cc: Kees Cook, Richard Sandiford, gcc-patches Qing Zhao via

Hi, Richard,

> On Jul 14, 2021, at 2:14 AM, Richard Biener <richard.guenther@gmail.com> wrote:
> 
> On Wed, Jul 14, 2021 at 1:17 AM Qing Zhao <qing.zhao@oracle.com> wrote:
>> 
>> Hi, Kees,
>> 
>> I took a look at the kernel testing case you attached in the previous email, and found the testing failed with the following case:
>> 
>> #define INIT_STRUCT_static_all          = { .one = arg->one,            \
>>                                            .two = arg->two,            \
>>                                            .three = arg->three,        \
>>                                            .four = arg->four,          \
>>                                        }
>> 
>> i.e, when the structure type auto variable has been explicitly initialized in the source code.  -ftrivial-auto-var-init in the 4th version
>> does not initialize the paddings for such variables.
>> 
>> But in the previous version of the patches ( 2 or 3), -ftrivial-auto-var-init initializes the paddings for such variables.
>> 
>> I intended to remove this part of the code from the 4th version of the patch since the implementation for initializing such paddings is completely different from
>> the initializing of the whole structure as a whole with memset in this version of the implementation.
>> 
>> If we really need this functionality, I will add another separate patch for this additional functionality, but not with this patch.
>> 
>> Richard, what’s your comment and suggestions on this?
> 
> I think this can be addressed in the gimplifier by adjusting
> gimplify_init_constructor to clear
> the object before the initialization (if it's not done via aggregate
> copying).  

I did this in the previous versions of the patch like the following:

@@ -5001,6 +5185,17 @@ gimplify_init_constructor (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 	  /* If a single access to the target must be ensured and all elements
 	     are zero, then it's optimal to clear whatever their number.  */
 	  cleared = true;
+	else if (flag_trivial_auto_var_init > AUTO_INIT_UNINITIALIZED
+		 && !TREE_STATIC (object)
+		 && type_has_padding (type))
+	  /* If the user requests to initialize automatic variables with
+	     paddings inside the type, we should initialize the paddings too.
+	     C guarantees that brace-init with fewer initializers than members
+	     aggregate will initialize the rest of the aggregate as-if it were
+	     static initialization.  In turn static initialization guarantees
+	     that pad is initialized to zero bits.
+	     So, it's better to clear the whole record under such situation.  */
+	  cleared = true;
 	else
 	  cleared = false;

Then the paddings are also initialized to zeroes with this option. (Even for -ftrivial-auto-var-init=pattern).

Is the above change Okay? (With this change, when -ftrivial-auto-var-init=pattern, the paddings for the
structure variables that have explicit initializer will be ZEROed, not 0xFE)

> The clearing
> could be done via .DEFERRED_INIT.

You mean to add additional calls to .DEFERRED_INIT for each individual padding of the structure in “gimplify_init_constructor"?
Then  later during RTL expand, expand these calls the same as other calls?
> 
> Note that I think .DEFERRED_INIT can be elided for variables that do
> not have their address
> taken - otherwise we'll also have to worry about aggregate copy
> initialization and SRA
> decomposing the copy, initializing only the used parts.

Please explain this a little bit more.

Thanks.

Qing
> 
> Richard.
> 
>> Thanks.
>> 
>> Qing
>> 
>>> On Jul 13, 2021, at 4:29 PM, Kees Cook <keescook@chromium.org> wrote:
>>> 
>>> On Mon, Jul 12, 2021 at 08:28:55PM +0000, Qing Zhao wrote:
>>>>> On Jul 12, 2021, at 12:56 PM, Kees Cook <keescook@chromium.org> wrote:
>>>>> On Wed, Jul 07, 2021 at 05:38:02PM +0000, Qing Zhao wrote:
>>>>>> This is the 4th version of the patch for the new security feature for GCC.
>>>>> 
>>>>> It looks like padding initialization has regressed to where things where
>>>>> in version 1[1] (it was, however, working in version 2[2]). I'm seeing
>>>>> these failures again in the kernel self-test:
>>>>> 
>>>>> test_stackinit: small_hole_static_all FAIL (uninit bytes: 3)
>>>>> test_stackinit: big_hole_static_all FAIL (uninit bytes: 61)
>>>>> test_stackinit: trailing_hole_static_all FAIL (uninit bytes: 7)
>>>>> test_stackinit: small_hole_dynamic_all FAIL (uninit bytes: 3)
>>>>> test_stackinit: big_hole_dynamic_all FAIL (uninit bytes: 61)
>>>>> test_stackinit: trailing_hole_dynamic_all FAIL (uninit bytes: 7)
>>>> 
>>>> Are the above failures for -ftrivial-auto-var-init=zero or -ftrivial-auto-var-init=pattern?  Or both?
>>> 
>>> Yes, I was only testing =zero (the kernel test handles =pattern as well:
>>> it doesn't explicitly test for 0x00). I've verified with =pattern now,
>>> too.
>>> 
>>>> For the current implementation, I believe that all paddings should be initialized with this option,
>>>> for -ftrivial-auto-var-init=zero, the padding will be initialized to zero as before, however, for
>>>> -ftrivial-auto-var-init=pattern, the padding will be initialized to 0xFE byte-repeatable patterns.
>>> 
>>> I've double-checked that I'm using the right gcc, with the flag.
>>> 
>>>>> 
>>>>> In looking at the gcc test cases, I think the wrong thing is
>>>>> being checked: we want to verify the padding itself. For example,
>>>>> in auto-init-17.c, the actual bytes after "four" need to be checked,
>>>>> rather than "four" itself.
>>>> 
>>>> ******For the current auto-init-17.c
>>>> 
>>>> 1 /* Verify zero initialization for array type with structure element with
>>>> 2    padding.  */
>>>> 3 /* { dg-do compile } */
>>>> 4 /* { dg-options "-ftrivial-auto-var-init=zero" } */
>>>> 5
>>>> 6 struct test_trailing_hole {
>>>> 7         int one;
>>>> 8         int two;
>>>> 9         int three;
>>>> 10         char four;
>>>> 11         /* "sizeof(unsigned long) - 1" byte padding hole here. */
>>>> 12 };
>>>> 13
>>>> 14
>>>> 15 int foo ()
>>>> 16 {
>>>> 17   struct test_trailing_hole var[10];
>>>> 18   return var[2].four;
>>>> 19 }
>>>> 20
>>>> 21 /* { dg-final { scan-assembler "movl\t\\\$0," } } */
>>>> 22 /* { dg-final { scan-assembler "movl\t\\\$20," } } */
>>>> 23 /* { dg-final { scan-assembler "rep stosq" } } */
>>>> ~
>>>> ******We have the assembly as: (-ftrivial-auto-var-init=zero)
>>>> 
>>>>       .file   "auto-init-17.c"
>>>>       .text
>>>>       .globl  foo
>>>>       .type   foo, @function
>>>> foo:
>>>> .LFB0:
>>>>       .cfi_startproc
>>>>       pushq   %rbp
>>>>       .cfi_def_cfa_offset 16
>>>>       .cfi_offset 6, -16
>>>>       movq    %rsp, %rbp
>>>>       .cfi_def_cfa_register 6
>>>>       subq    $40, %rsp
>>>>       leaq    -160(%rbp), %rax
>>>>       movq    %rax, %rsi
>>>>       movl    $0, %eax
>>>>       movl    $20, %edx
>>>>       movq    %rsi, %rdi
>>>>       movq    %rdx, %rcx
>>>>       rep stosq
>>>>       movzbl  -116(%rbp), %eax
>>>>       movsbl  %al, %eax
>>>>       leave
>>>>       .cfi_def_cfa 7, 8
>>>>       ret
>>>>       .cfi_endproc
>>>> .LFE0:
>>>>       .size   foo, .-foo
>>>>       .section        .note.GNU-stack,"",@progbits
>>>> 
>>>> From the above, we can see,  “zero” will be used to initialize 8 * 20 = 16 * 10 bytes of memory starting from the beginning of “var”, that include all the padding holes inside
>>>> This array of structure.
>>>> 
>>>> I didn’t see issue with padding initialization here.
>>> 
>>> Hm, agreed -- this test does do the right thing.
>>> 
>>>>> But this isn't actually sufficient because they may _accidentally_
>>>>> be zero already. The kernel tests specifically make sure to fill the
>>>>> about-to-be-used stack with 0xff before calling a function like foo()
>>>>> above.
>>> 
>>> I've extracted the kernel test to build for userspace, and it behaves
>>> the same way. See attached "stackinit.c".
>>> 
>>> $ gcc-build/auto-var-init.4/installed/bin/gcc -O2 -Wall -o stackinit stackinit.c
>>> $ ./stackinit 2>&1 | grep failures:
>>> stackinit: failures: 23
>>> $ gcc-build/auto-var-init.4/installed/bin/gcc -O2 -Wall -ftrivial-auto-var-init=zero -o stackinit stackinit.c
>>> stackinit.c: In function ‘__leaf_switch_none’:
>>> stackinit.c:326:26: warning: statement will never be executed
>>> [-Wswitch-unreachable]
>>> 326 |                 uint64_t var;
>>>     |                          ^~~
>>> $ ./stackinit 2>&1 | grep failures:
>>> stackinit: failures: 6
>>> 
>>> Same failures as seen in the kernel test (and an expected warning
>>> about the initialization that will never happen for a pre-case switch
>>> statement).
>>> 
>>>>> 
>>>>> (And as an aside, it seems like naming the test cases with some details
>>>>> about what is being tested in the filename would be nice -- it was
>>>>> a little weird having to dig through their numeric names to find the
>>>>> padding tests.)
>>>> 
>>>> Yes, I will fix the testing names to more reflect the testing details.
>>> 
>>> Great!
>>> 
>>> --
>>> Kees Cook
>>> <stackinit.c>


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-14 14:09           ` Qing Zhao
@ 2021-07-14 19:11             ` Kees Cook
  2021-07-14 19:30               ` Qing Zhao
  2021-07-15  7:56             ` Richard Biener
  1 sibling, 1 reply; 25+ messages in thread
From: Kees Cook @ 2021-07-14 19:11 UTC (permalink / raw)
  To: Qing Zhao; +Cc: Richard Biener, Richard Sandiford, gcc-patches Qing Zhao via

[-- Attachment #1: Type: text/plain, Size: 3324 bytes --]

On Wed, Jul 14, 2021 at 02:09:50PM +0000, Qing Zhao wrote:
> Hi, Richard,
> 
> > On Jul 14, 2021, at 2:14 AM, Richard Biener <richard.guenther@gmail.com> wrote:
> > 
> > On Wed, Jul 14, 2021 at 1:17 AM Qing Zhao <qing.zhao@oracle.com> wrote:
> >> 
> >> Hi, Kees,
> >> 
> >> I took a look at the kernel testing case you attached in the previous email, and found the testing failed with the following case:
> >> 
> >> #define INIT_STRUCT_static_all          = { .one = arg->one,            \
> >>                                            .two = arg->two,            \
> >>                                            .three = arg->three,        \
> >>                                            .four = arg->four,          \
> >>                                        }
> >> 
> >> i.e, when the structure type auto variable has been explicitly initialized in the source code.  -ftrivial-auto-var-init in the 4th version
> >> does not initialize the paddings for such variables.
> >> 
> >> But in the previous version of the patches ( 2 or 3), -ftrivial-auto-var-init initializes the paddings for such variables.
> >> 
> >> I intended to remove this part of the code from the 4th version of the patch since the implementation for initializing such paddings is completely different from
> >> the initializing of the whole structure as a whole with memset in this version of the implementation.
> >> 
> >> If we really need this functionality, I will add another separate patch for this additional functionality, but not with this patch.
> >> 
> >> Richard, what’s your comment and suggestions on this?
> > 
> > I think this can be addressed in the gimplifier by adjusting
> > gimplify_init_constructor to clear
> > the object before the initialization (if it's not done via aggregate
> > copying).  
> 
> I did this in the previous versions of the patch like the following:
> 
> @@ -5001,6 +5185,17 @@ gimplify_init_constructor (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
>  	  /* If a single access to the target must be ensured and all elements
>  	     are zero, then it's optimal to clear whatever their number.  */
>  	  cleared = true;
> +	else if (flag_trivial_auto_var_init > AUTO_INIT_UNINITIALIZED
> +		 && !TREE_STATIC (object)
> +		 && type_has_padding (type))
> +	  /* If the user requests to initialize automatic variables with
> +	     paddings inside the type, we should initialize the paddings too.
> +	     C guarantees that brace-init with fewer initializers than members
> +	     aggregate will initialize the rest of the aggregate as-if it were
> +	     static initialization.  In turn static initialization guarantees
> +	     that pad is initialized to zero bits.
> +	     So, it's better to clear the whole record under such situation.  */
> +	  cleared = true;
>  	else
>  	  cleared = false;
> 
> Then the paddings are also initialized to zeroes with this option. (Even for -ftrivial-auto-var-init=pattern).

Thanks! I've tested with the attached patch to v4 and it passes all my
tests again.

> Is the above change Okay? (With this change, when -ftrivial-auto-var-init=pattern, the paddings for the
> structure variables that have explicit initializer will be ZEROed, not 0xFE)

Padding zeroing in the face of pattern-init is correct (and matches what
Clang does).

-Kees

-- 
Kees Cook

[-- Attachment #2: padding.patch --]
[-- Type: text/x-diff, Size: 4165 bytes --]

commit 8c52b69540b064e930e4d9e2e3dc011ca002306d
Author:     Kees Cook <keescook@chromium.org>
AuthorDate: Wed Jul 14 11:17:27 2021 -0700
Commit:     Kees Cook <keescook@chromium.org>
CommitDate: Wed Jul 14 12:08:56 2021 -0700

    Fix padding
    
    Based on v2 and BDDDE2D2-8594-4BF6-8142-CD09B0EBB445@oracle.com

diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 4db53cda77f8..dd3da86d6663 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -5071,6 +5071,18 @@ gimplify_init_constructor (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 	  /* If a single access to the target must be ensured and all elements
 	     are zero, then it's optimal to clear whatever their number.  */
 	  cleared = true;
+	else if (opt_for_fn (current_function_decl, flag_auto_var_init)
+		   > AUTO_INIT_UNINITIALIZED
+		 && !TREE_STATIC (object)
+		 && type_has_padding (type))
+	  /* If the user requests to initialize automatic variables with
+	     paddings inside the type, we should initialize the paddings too.
+	     C guarantees that brace-init with fewer initializers than members
+	     aggregate will initialize the rest of the aggregate as-if it were
+	     static initialization.  In turn static initialization guarantees
+	     that pad is initialized to zero bits.
+	     So, it's better to clear the whole record under such situation.  */
+	  cleared = true;
 	else
 	  cleared = false;
 
diff --git a/gcc/tree.c b/gcc/tree.c
index 1aa6e557a049..7889c10d639f 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -10818,6 +10818,72 @@ lower_bound_in_type (tree outer, tree inner)
     }
 }
 
+/* Returns true when the given TYPE has padding inside it.
+   return false otherwise.  */
+bool
+type_has_padding (tree type)
+{
+  switch (TREE_CODE (type))
+    {
+    case RECORD_TYPE:
+      {
+	unsigned HOST_WIDE_INT record_size
+	  = tree_to_uhwi (TYPE_SIZE_UNIT (type));
+	unsigned HOST_WIDE_INT size_sofar = 0;
+
+	for (tree field= TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+	  {
+	    if (TREE_CODE (field) != FIELD_DECL)
+	      continue;
+	    unsigned HOST_WIDE_INT cur_off = int_byte_position (field);
+	    if (size_sofar < cur_off)
+	      return true;
+	    size_sofar = cur_off
+			 + tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field)));
+	  }
+	if (size_sofar < record_size)
+	  return true;
+	/* If any of the field has padding, return true.  */
+	for (tree field= TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+	  {
+	    if ((TREE_CODE (field) != FIELD_DECL))
+	      continue;
+	    if (AGGREGATE_TYPE_P (TREE_TYPE (field))
+		&& type_has_padding (TREE_TYPE (field)))
+	      return true;
+	  }
+	return false;
+      }
+    case UNION_TYPE:
+    case QUAL_UNION_TYPE:
+      {
+	tree max_field = NULL;
+	unsigned max_size = 0;
+	for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+	  {
+	    if (TREE_CODE (field) != FIELD_DECL)
+	      continue;
+	    if (tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field))) >= max_size)
+	      {
+		max_size = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field)));
+		max_field = field;
+	      }
+	  }
+	if (AGGREGATE_TYPE_P (TREE_TYPE (max_field)))
+	  return type_has_padding (TREE_TYPE (max_field));
+	return false;
+      }
+    case ARRAY_TYPE:
+      {
+	if (AGGREGATE_TYPE_P (TREE_TYPE (type)))
+	  return type_has_padding (TREE_TYPE (type));
+	return false;
+      }
+    default:
+      return false;
+    }
+}
+
 /* Return nonzero if two operands that are suitable for PHI nodes are
    necessarily equal.  Specifically, both ARG0 and ARG1 must be either
    SSA_NAME or invariant.  Note that this is strictly an optimization.
diff --git a/gcc/tree.h b/gcc/tree.h
index 8bdf16d8b4ab..56a7947d935d 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5168,6 +5168,7 @@ extern bool operation_can_overflow (enum tree_code);
 extern bool operation_no_trapping_overflow (tree, enum tree_code);
 extern tree upper_bound_in_type (tree, tree);
 extern tree lower_bound_in_type (tree, tree);
+extern bool type_has_padding (tree);
 extern int operand_equal_for_phi_arg_p (const_tree, const_tree);
 extern tree create_artificial_label (location_t);
 extern const char *get_name (tree);

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-14 19:11             ` Kees Cook
@ 2021-07-14 19:30               ` Qing Zhao
  2021-07-14 21:23                 ` Kees Cook
  0 siblings, 1 reply; 25+ messages in thread
From: Qing Zhao @ 2021-07-14 19:30 UTC (permalink / raw)
  To: Kees Cook; +Cc: Richard Biener, Richard Sandiford, gcc-patches Qing Zhao via

Hi, Kees,


> On Jul 14, 2021, at 2:11 PM, Kees Cook <keescook@chromium.org> wrote:
> 
> On Wed, Jul 14, 2021 at 02:09:50PM +0000, Qing Zhao wrote:
>> Hi, Richard,
>> 
>>> On Jul 14, 2021, at 2:14 AM, Richard Biener <richard.guenther@gmail.com> wrote:
>>> 
>>> On Wed, Jul 14, 2021 at 1:17 AM Qing Zhao <qing.zhao@oracle.com> wrote:
>>>> 
>>>> Hi, Kees,
>>>> 
>>>> I took a look at the kernel testing case you attached in the previous email, and found the testing failed with the following case:
>>>> 
>>>> #define INIT_STRUCT_static_all          = { .one = arg->one,            \
>>>>                                           .two = arg->two,            \
>>>>                                           .three = arg->three,        \
>>>>                                           .four = arg->four,          \
>>>>                                       }
>>>> 
>>>> i.e, when the structure type auto variable has been explicitly initialized in the source code.  -ftrivial-auto-var-init in the 4th version
>>>> does not initialize the paddings for such variables.
>>>> 
>>>> But in the previous version of the patches ( 2 or 3), -ftrivial-auto-var-init initializes the paddings for such variables.
>>>> 
>>>> I intended to remove this part of the code from the 4th version of the patch since the implementation for initializing such paddings is completely different from
>>>> the initializing of the whole structure as a whole with memset in this version of the implementation.
>>>> 
>>>> If we really need this functionality, I will add another separate patch for this additional functionality, but not with this patch.
>>>> 
>>>> Richard, what’s your comment and suggestions on this?
>>> 
>>> I think this can be addressed in the gimplifier by adjusting
>>> gimplify_init_constructor to clear
>>> the object before the initialization (if it's not done via aggregate
>>> copying).  
>> 
>> I did this in the previous versions of the patch like the following:
>> 
>> @@ -5001,6 +5185,17 @@ gimplify_init_constructor (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
>> 	  /* If a single access to the target must be ensured and all elements
>> 	     are zero, then it's optimal to clear whatever their number.  */
>> 	  cleared = true;
>> +	else if (flag_trivial_auto_var_init > AUTO_INIT_UNINITIALIZED
>> +		 && !TREE_STATIC (object)
>> +		 && type_has_padding (type))
>> +	  /* If the user requests to initialize automatic variables with
>> +	     paddings inside the type, we should initialize the paddings too.
>> +	     C guarantees that brace-init with fewer initializers than members
>> +	     aggregate will initialize the rest of the aggregate as-if it were
>> +	     static initialization.  In turn static initialization guarantees
>> +	     that pad is initialized to zero bits.
>> +	     So, it's better to clear the whole record under such situation.  */
>> +	  cleared = true;
>> 	else
>> 	  cleared = false;
>> 
>> Then the paddings are also initialized to zeroes with this option. (Even for -ftrivial-auto-var-init=pattern).
> 
> Thanks! I've tested with the attached patch to v4 and it passes all my
> tests again.
> 
>> Is the above change Okay? (With this change, when -ftrivial-auto-var-init=pattern, the paddings for the
>> structure variables that have explicit initializer will be ZEROed, not 0xFE)
> 
> Padding zeroing in the face of pattern-init is correct (and matches what
> Clang does).

During the discussion before the 4th version of the patch, we have agreed that pattern-init will use 0xFE byte-repeatable patterns 
to initialize all the types (this includes the paddings when the structure type variables are not explicitly initialized). And will not match
Clang’s current behavior. 

If we initialize the paddings when the structure type variables are explicitly initialized to Zeroes, then there will be inconsistency 
between values that are used to initialize structure paddings under different situations, This looks not good to me.

If we have agreed on using 0xFE byte-repeatable patterns for pattern-init, then all the paddings should be initialized with the same 
pattern. 

This is the major reason I deleted the change in “gimplify_init_constructor” in the 4th version.  And considered a different implementation
for padding initializations with explicitly initialized structure variables. 

Qing

> 
> -Kees
> 
> -- 
> Kees Cook
> <padding.patch>


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-14 19:30               ` Qing Zhao
@ 2021-07-14 21:23                 ` Kees Cook
  2021-07-14 22:30                   ` Qing Zhao
  0 siblings, 1 reply; 25+ messages in thread
From: Kees Cook @ 2021-07-14 21:23 UTC (permalink / raw)
  To: Qing Zhao; +Cc: Richard Biener, Richard Sandiford, gcc-patches Qing Zhao via

On Wed, Jul 14, 2021 at 07:30:45PM +0000, Qing Zhao wrote:
> Hi, Kees,
> 
> 
> > On Jul 14, 2021, at 2:11 PM, Kees Cook <keescook@chromium.org> wrote:
> > 
> > On Wed, Jul 14, 2021 at 02:09:50PM +0000, Qing Zhao wrote:
> >> Hi, Richard,
> >> 
> >>> On Jul 14, 2021, at 2:14 AM, Richard Biener <richard.guenther@gmail.com> wrote:
> >>> 
> >>> On Wed, Jul 14, 2021 at 1:17 AM Qing Zhao <qing.zhao@oracle.com> wrote:
> >>>> 
> >>>> Hi, Kees,
> >>>> 
> >>>> I took a look at the kernel testing case you attached in the previous email, and found the testing failed with the following case:
> >>>> 
> >>>> #define INIT_STRUCT_static_all          = { .one = arg->one,            \
> >>>>                                           .two = arg->two,            \
> >>>>                                           .three = arg->three,        \
> >>>>                                           .four = arg->four,          \
> >>>>                                       }
> >>>> 
> >>>> i.e, when the structure type auto variable has been explicitly initialized in the source code.  -ftrivial-auto-var-init in the 4th version
> >>>> does not initialize the paddings for such variables.
> >>>> 
> >>>> But in the previous version of the patches ( 2 or 3), -ftrivial-auto-var-init initializes the paddings for such variables.
> >>>> 
> >>>> I intended to remove this part of the code from the 4th version of the patch since the implementation for initializing such paddings is completely different from
> >>>> the initializing of the whole structure as a whole with memset in this version of the implementation.
> >>>> 
> >>>> If we really need this functionality, I will add another separate patch for this additional functionality, but not with this patch.
> >>>> 
> >>>> Richard, what’s your comment and suggestions on this?
> >>> 
> >>> I think this can be addressed in the gimplifier by adjusting
> >>> gimplify_init_constructor to clear
> >>> the object before the initialization (if it's not done via aggregate
> >>> copying).  
> >> 
> >> I did this in the previous versions of the patch like the following:
> >> 
> >> @@ -5001,6 +5185,17 @@ gimplify_init_constructor (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
> >> 	  /* If a single access to the target must be ensured and all elements
> >> 	     are zero, then it's optimal to clear whatever their number.  */
> >> 	  cleared = true;
> >> +	else if (flag_trivial_auto_var_init > AUTO_INIT_UNINITIALIZED
> >> +		 && !TREE_STATIC (object)
> >> +		 && type_has_padding (type))
> >> +	  /* If the user requests to initialize automatic variables with
> >> +	     paddings inside the type, we should initialize the paddings too.
> >> +	     C guarantees that brace-init with fewer initializers than members
> >> +	     aggregate will initialize the rest of the aggregate as-if it were
> >> +	     static initialization.  In turn static initialization guarantees
> >> +	     that pad is initialized to zero bits.
> >> +	     So, it's better to clear the whole record under such situation.  */
> >> +	  cleared = true;
> >> 	else
> >> 	  cleared = false;
> >> 
> >> Then the paddings are also initialized to zeroes with this option. (Even for -ftrivial-auto-var-init=pattern).
> > 
> > Thanks! I've tested with the attached patch to v4 and it passes all my
> > tests again.
> > 
> >> Is the above change Okay? (With this change, when -ftrivial-auto-var-init=pattern, the paddings for the
> >> structure variables that have explicit initializer will be ZEROed, not 0xFE)
> > 
> > Padding zeroing in the face of pattern-init is correct (and matches what
> > Clang does).
> 
> During the discussion before the 4th version of the patch, we have agreed that pattern-init will use 0xFE byte-repeatable patterns 
> to initialize all the types (this includes the paddings when the structure type variables are not explicitly initialized). And will not match
> Clang’s current behavior. 

Right, that's fine.

> If we initialize the paddings when the structure type variables are explicitly initialized to Zeroes, then there will be inconsistency 
> between values that are used to initialize structure paddings under different situations, This looks not good to me.
> 
> If we have agreed on using 0xFE byte-repeatable patterns for pattern-init, then all the paddings should be initialized with the same 
> pattern. 

Ah! By "situation", you mean how the compiler chooses to initialize the
structure members?

It sounds like for =zero mode, padding will be 0, but for =pattern,
padding may be either 0x00 or 0xFE, depending on which kind of
initialization is internally chosen. Is that right? I'm fine with this
since the =zero case is what I'm primarily focused on being as safe
as possible.

-Kees

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-14 21:23                 ` Kees Cook
@ 2021-07-14 22:30                   ` Qing Zhao
  0 siblings, 0 replies; 25+ messages in thread
From: Qing Zhao @ 2021-07-14 22:30 UTC (permalink / raw)
  To: Kees Cook, Richard Biener; +Cc: Richard Sandiford, gcc-patches Qing Zhao via



> On Jul 14, 2021, at 4:23 PM, Kees Cook <keescook@chromium.org> wrote:
> 
> On Wed, Jul 14, 2021 at 07:30:45PM +0000, Qing Zhao wrote:
>> Hi, Kees,
>> 
>> 
>>> On Jul 14, 2021, at 2:11 PM, Kees Cook <keescook@chromium.org> wrote:
>>> 
>>> On Wed, Jul 14, 2021 at 02:09:50PM +0000, Qing Zhao wrote:
>>>> Hi, Richard,
>>>> 
>>>>> On Jul 14, 2021, at 2:14 AM, Richard Biener <richard.guenther@gmail.com> wrote:
>>>>> 
>>>>> On Wed, Jul 14, 2021 at 1:17 AM Qing Zhao <qing.zhao@oracle.com> wrote:
>>>>>> 
>>>>>> Hi, Kees,
>>>>>> 
>>>>>> I took a look at the kernel testing case you attached in the previous email, and found the testing failed with the following case:
>>>>>> 
>>>>>> #define INIT_STRUCT_static_all          = { .one = arg->one,            \
>>>>>>                                          .two = arg->two,            \
>>>>>>                                          .three = arg->three,        \
>>>>>>                                          .four = arg->four,          \
>>>>>>                                      }
>>>>>> 
>>>>>> i.e, when the structure type auto variable has been explicitly initialized in the source code.  -ftrivial-auto-var-init in the 4th version
>>>>>> does not initialize the paddings for such variables.
>>>>>> 
>>>>>> But in the previous version of the patches ( 2 or 3), -ftrivial-auto-var-init initializes the paddings for such variables.
>>>>>> 
>>>>>> I intended to remove this part of the code from the 4th version of the patch since the implementation for initializing such paddings is completely different from
>>>>>> the initializing of the whole structure as a whole with memset in this version of the implementation.
>>>>>> 
>>>>>> If we really need this functionality, I will add another separate patch for this additional functionality, but not with this patch.
>>>>>> 
>>>>>> Richard, what’s your comment and suggestions on this?
>>>>> 
>>>>> I think this can be addressed in the gimplifier by adjusting
>>>>> gimplify_init_constructor to clear
>>>>> the object before the initialization (if it's not done via aggregate
>>>>> copying).  
>>>> 
>>>> I did this in the previous versions of the patch like the following:
>>>> 
>>>> @@ -5001,6 +5185,17 @@ gimplify_init_constructor (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
>>>> 	  /* If a single access to the target must be ensured and all elements
>>>> 	     are zero, then it's optimal to clear whatever their number.  */
>>>> 	  cleared = true;
>>>> +	else if (flag_trivial_auto_var_init > AUTO_INIT_UNINITIALIZED
>>>> +		 && !TREE_STATIC (object)
>>>> +		 && type_has_padding (type))
>>>> +	  /* If the user requests to initialize automatic variables with
>>>> +	     paddings inside the type, we should initialize the paddings too.
>>>> +	     C guarantees that brace-init with fewer initializers than members
>>>> +	     aggregate will initialize the rest of the aggregate as-if it were
>>>> +	     static initialization.  In turn static initialization guarantees
>>>> +	     that pad is initialized to zero bits.
>>>> +	     So, it's better to clear the whole record under such situation.  */
>>>> +	  cleared = true;
>>>> 	else
>>>> 	  cleared = false;
>>>> 
>>>> Then the paddings are also initialized to zeroes with this option. (Even for -ftrivial-auto-var-init=pattern).
>>> 
>>> Thanks! I've tested with the attached patch to v4 and it passes all my
>>> tests again.
>>> 
>>>> Is the above change Okay? (With this change, when -ftrivial-auto-var-init=pattern, the paddings for the
>>>> structure variables that have explicit initializer will be ZEROed, not 0xFE)
>>> 
>>> Padding zeroing in the face of pattern-init is correct (and matches what
>>> Clang does).
>> 
>> During the discussion before the 4th version of the patch, we have agreed that pattern-init will use 0xFE byte-repeatable patterns 
>> to initialize all the types (this includes the paddings when the structure type variables are not explicitly initialized). And will not match
>> Clang’s current behavior. 
> 
> Right, that's fine.
> 
>> If we initialize the paddings when the structure type variables are explicitly initialized to Zeroes, then there will be inconsistency 
>> between values that are used to initialize structure paddings under different situations, This looks not good to me.
>> 
>> If we have agreed on using 0xFE byte-repeatable patterns for pattern-init, then all the paddings should be initialized with the same 
>> pattern. 
> 
> Ah! By "situation", you mean how the compiler chooses to initialize the
> structure members?

There are three situations that we should initialize the paddings of a structure type auto-variable:

1. When there is no any explicit initializer;
2. When there is an explicit initializer, and the initializer only partially initialize the structure variable;
3. When there is an explicit initializer, and the initializer fully initialize the structure variable;

The code examples for the above 3 situations are:

struct test_small_hole {
        size_t one;
        char two;
        /* 3 byte padding hole here. */
        int three;
        unsigned long four;
};

1.  struct test_small_hole tmp1;
2.  struct test_small_hole tmp2 = {.two = 0, }
3.  Struct test_small_hole tmp3 = {.one =1, .two = 2, .three = 3, .four = 4,}

The current GCC without this new feature initializes the paddings of 2 to zeroes already inside “gimplify_init_constructor”, 
“If the constructor isn’t complete, clear the whole object beforehand”. 

But for 1 and 3, the current GCC does  not initialize the paddings. 

With the new feature (-ftrivial-auto-var-init ), We want all the paddings to be initialized, including all the above 3 situations. 

*******In the 2nd and 3rd version of the patches:
For situation 1, the padding initialization is handled in “store_constructor”  as following:

"
-- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -6539,14 +6539,19 @@ store_constructor (tree exp, rtx target, int cleared, poly_int64 size,
 	    cleared = 1;
 	  }
 
-        /* If the constructor has fewer fields than the structure or
-	   if we are initializing the structure to mostly zeros, clear
-	   the whole structure first.  Don't do this if TARGET is a
-	   register whose mode size isn't equal to SIZE since
-	   clear_storage can't handle this case.  */
+	/* If the constructor has fewer fields than the structure,
+	   or if we are initializing the structure to mostly zeros,
+	   or if the user requests to initialize automatic variables with
+	   paddings inside the type,
+	   we should clear the whole structure first.
+	   Don't do this if TARGET is a register whose mode size isn't equal
+	   SIZE since clear_storage can't handle this case.  */
 	else if (known_size_p (size)
 		 && (((int) CONSTRUCTOR_NELTS (exp) != fields_length (type))
-		     || mostly_zeros_p (exp))
+		     || mostly_zeros_p (exp)
+		     || (flag_trivial_auto_var_init > AUTO_INIT_UNINITIALIZED
+			 && !TREE_STATIC (exp)
+			 && type_has_padding (type)))
 		 && (!REG_P (target)
 		     || known_eq (GET_MODE_SIZE (GET_MODE (target)), size)))
 	  {
“

For situation 2, and 3,  the padding initialization is handled in “gimplify_init_constructor”, similarly as above. 
All the paddings are initialized to “zeroes.

******in the 4th version of the patch:

For situation 1, the padding initialization is included with the whole structure variable initialization by memset of zeroes or 0xFE byte repeatable patterns. 
For situation 2, the padding initialization is still applied as before with “gimplify_init_constructor”, but its value always is zeroes even for pattern-init;
For situation 3, no padding initialization is applied.

So, in order to complete the padding initialization implementation, we need to answer the following question first:

Should all the padding initialization for situation 1, 2, 3 use the same value for pattern-init? 

If YES, then we should use “0xFE” to initialize all paddings for pattern-init, the implementation for padding initialization of situation 3 will be more complicate.

> 
> It sounds like for =zero mode, padding will be 0,

Yes.

> but for =pattern,
> padding may be either 0x00 or 0xFE, depending on which kind of
> initialization is internally chosen. Is that right?

Right with the current patch, but we need to decide whether we want consistent value in paddings for pattern init.

Qing


> I'm fine with this
> since the =zero case is what I'm primarily focused on being as safe
> as possible.
> 
> -Kees
> 
> -- 
> Kees Cook


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-14 14:09           ` Qing Zhao
  2021-07-14 19:11             ` Kees Cook
@ 2021-07-15  7:56             ` Richard Biener
  2021-07-15 14:16               ` Qing Zhao
  1 sibling, 1 reply; 25+ messages in thread
From: Richard Biener @ 2021-07-15  7:56 UTC (permalink / raw)
  To: Qing Zhao; +Cc: Kees Cook, Richard Sandiford, gcc-patches Qing Zhao via

On Wed, Jul 14, 2021 at 4:10 PM Qing Zhao <qing.zhao@oracle.com> wrote:
>
> Hi, Richard,
>
> > On Jul 14, 2021, at 2:14 AM, Richard Biener <richard.guenther@gmail.com> wrote:
> >
> > On Wed, Jul 14, 2021 at 1:17 AM Qing Zhao <qing.zhao@oracle.com> wrote:
> >>
> >> Hi, Kees,
> >>
> >> I took a look at the kernel testing case you attached in the previous email, and found the testing failed with the following case:
> >>
> >> #define INIT_STRUCT_static_all          = { .one = arg->one,            \
> >>                                            .two = arg->two,            \
> >>                                            .three = arg->three,        \
> >>                                            .four = arg->four,          \
> >>                                        }
> >>
> >> i.e, when the structure type auto variable has been explicitly initialized in the source code.  -ftrivial-auto-var-init in the 4th version
> >> does not initialize the paddings for such variables.
> >>
> >> But in the previous version of the patches ( 2 or 3), -ftrivial-auto-var-init initializes the paddings for such variables.
> >>
> >> I intended to remove this part of the code from the 4th version of the patch since the implementation for initializing such paddings is completely different from
> >> the initializing of the whole structure as a whole with memset in this version of the implementation.
> >>
> >> If we really need this functionality, I will add another separate patch for this additional functionality, but not with this patch.
> >>
> >> Richard, what’s your comment and suggestions on this?
> >
> > I think this can be addressed in the gimplifier by adjusting
> > gimplify_init_constructor to clear
> > the object before the initialization (if it's not done via aggregate
> > copying).
>
> I did this in the previous versions of the patch like the following:
>
> @@ -5001,6 +5185,17 @@ gimplify_init_constructor (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
>           /* If a single access to the target must be ensured and all elements
>              are zero, then it's optimal to clear whatever their number.  */
>           cleared = true;
> +       else if (flag_trivial_auto_var_init > AUTO_INIT_UNINITIALIZED
> +                && !TREE_STATIC (object)
> +                && type_has_padding (type))
> +         /* If the user requests to initialize automatic variables with
> +            paddings inside the type, we should initialize the paddings too.
> +            C guarantees that brace-init with fewer initializers than members
> +            aggregate will initialize the rest of the aggregate as-if it were
> +            static initialization.  In turn static initialization guarantees
> +            that pad is initialized to zero bits.
> +            So, it's better to clear the whole record under such situation.  */
> +         cleared = true;
>         else
>           cleared = false;
>
> Then the paddings are also initialized to zeroes with this option. (Even for -ftrivial-auto-var-init=pattern).
>
> Is the above change Okay? (With this change, when -ftrivial-auto-var-init=pattern, the paddings for the
> structure variables that have explicit initializer will be ZEROed, not 0xFE)

I guess that would be the simplest way, yes.

> > The clearing
> > could be done via .DEFERRED_INIT.
>
> You mean to add additional calls to .DEFERRED_INIT for each individual padding of the structure in “gimplify_init_constructor"?
> Then  later during RTL expand, expand these calls the same as other calls?

No, I actually meant to in your patch above set

    defered_padding_init = true;

and where 'cleared' is processed do sth like

  if (defered_padding_init)
    .. emit .DEFERRED_INIT for the _whole_ variable ..
  else if (cleared)
     .. original cleared handling ...

that would retain the pattern init but possibly be less efficient in the end.

> >
> > Note that I think .DEFERRED_INIT can be elided for variables that do
> > not have their address
> > taken - otherwise we'll also have to worry about aggregate copy
> > initialization and SRA
> > decomposing the copy, initializing only the used parts.
>
> Please explain this a little bit more.

For sth like

struct S { int i; long j; };

void bar (struct S);
struct S
foo (struct S *p)
{
  struct S q = *p;
  struct S r = q;
  bar (r);
  return r;
}

we don't get a .DEFERRED_INIT for 'r' (do we?) and SRA decomposes the init to

  <bb 2> :
  q = *p_2(D);
  q$i_9 = p_2(D)->i;
  q$j_10 = p_2(D)->j;
  r.i = q$i_9;
  r.j = q$j_10;
  bar (r);
  D.1953 = r;
  r ={v} {CLOBBER};
  return D.1953;

which leaves its padding uninitialized.  Hmm, and that even happens when
you make bar take struct S * and thus pass the address of 'r' to bar.

Richard.


> Thanks.
>
> Qing
> >
> > Richard.
> >
> >> Thanks.
> >>
> >> Qing
> >>
> >>> On Jul 13, 2021, at 4:29 PM, Kees Cook <keescook@chromium.org> wrote:
> >>>
> >>> On Mon, Jul 12, 2021 at 08:28:55PM +0000, Qing Zhao wrote:
> >>>>> On Jul 12, 2021, at 12:56 PM, Kees Cook <keescook@chromium.org> wrote:
> >>>>> On Wed, Jul 07, 2021 at 05:38:02PM +0000, Qing Zhao wrote:
> >>>>>> This is the 4th version of the patch for the new security feature for GCC.
> >>>>>
> >>>>> It looks like padding initialization has regressed to where things where
> >>>>> in version 1[1] (it was, however, working in version 2[2]). I'm seeing
> >>>>> these failures again in the kernel self-test:
> >>>>>
> >>>>> test_stackinit: small_hole_static_all FAIL (uninit bytes: 3)
> >>>>> test_stackinit: big_hole_static_all FAIL (uninit bytes: 61)
> >>>>> test_stackinit: trailing_hole_static_all FAIL (uninit bytes: 7)
> >>>>> test_stackinit: small_hole_dynamic_all FAIL (uninit bytes: 3)
> >>>>> test_stackinit: big_hole_dynamic_all FAIL (uninit bytes: 61)
> >>>>> test_stackinit: trailing_hole_dynamic_all FAIL (uninit bytes: 7)
> >>>>
> >>>> Are the above failures for -ftrivial-auto-var-init=zero or -ftrivial-auto-var-init=pattern?  Or both?
> >>>
> >>> Yes, I was only testing =zero (the kernel test handles =pattern as well:
> >>> it doesn't explicitly test for 0x00). I've verified with =pattern now,
> >>> too.
> >>>
> >>>> For the current implementation, I believe that all paddings should be initialized with this option,
> >>>> for -ftrivial-auto-var-init=zero, the padding will be initialized to zero as before, however, for
> >>>> -ftrivial-auto-var-init=pattern, the padding will be initialized to 0xFE byte-repeatable patterns.
> >>>
> >>> I've double-checked that I'm using the right gcc, with the flag.
> >>>
> >>>>>
> >>>>> In looking at the gcc test cases, I think the wrong thing is
> >>>>> being checked: we want to verify the padding itself. For example,
> >>>>> in auto-init-17.c, the actual bytes after "four" need to be checked,
> >>>>> rather than "four" itself.
> >>>>
> >>>> ******For the current auto-init-17.c
> >>>>
> >>>> 1 /* Verify zero initialization for array type with structure element with
> >>>> 2    padding.  */
> >>>> 3 /* { dg-do compile } */
> >>>> 4 /* { dg-options "-ftrivial-auto-var-init=zero" } */
> >>>> 5
> >>>> 6 struct test_trailing_hole {
> >>>> 7         int one;
> >>>> 8         int two;
> >>>> 9         int three;
> >>>> 10         char four;
> >>>> 11         /* "sizeof(unsigned long) - 1" byte padding hole here. */
> >>>> 12 };
> >>>> 13
> >>>> 14
> >>>> 15 int foo ()
> >>>> 16 {
> >>>> 17   struct test_trailing_hole var[10];
> >>>> 18   return var[2].four;
> >>>> 19 }
> >>>> 20
> >>>> 21 /* { dg-final { scan-assembler "movl\t\\\$0," } } */
> >>>> 22 /* { dg-final { scan-assembler "movl\t\\\$20," } } */
> >>>> 23 /* { dg-final { scan-assembler "rep stosq" } } */
> >>>> ~
> >>>> ******We have the assembly as: (-ftrivial-auto-var-init=zero)
> >>>>
> >>>>       .file   "auto-init-17.c"
> >>>>       .text
> >>>>       .globl  foo
> >>>>       .type   foo, @function
> >>>> foo:
> >>>> .LFB0:
> >>>>       .cfi_startproc
> >>>>       pushq   %rbp
> >>>>       .cfi_def_cfa_offset 16
> >>>>       .cfi_offset 6, -16
> >>>>       movq    %rsp, %rbp
> >>>>       .cfi_def_cfa_register 6
> >>>>       subq    $40, %rsp
> >>>>       leaq    -160(%rbp), %rax
> >>>>       movq    %rax, %rsi
> >>>>       movl    $0, %eax
> >>>>       movl    $20, %edx
> >>>>       movq    %rsi, %rdi
> >>>>       movq    %rdx, %rcx
> >>>>       rep stosq
> >>>>       movzbl  -116(%rbp), %eax
> >>>>       movsbl  %al, %eax
> >>>>       leave
> >>>>       .cfi_def_cfa 7, 8
> >>>>       ret
> >>>>       .cfi_endproc
> >>>> .LFE0:
> >>>>       .size   foo, .-foo
> >>>>       .section        .note.GNU-stack,"",@progbits
> >>>>
> >>>> From the above, we can see,  “zero” will be used to initialize 8 * 20 = 16 * 10 bytes of memory starting from the beginning of “var”, that include all the padding holes inside
> >>>> This array of structure.
> >>>>
> >>>> I didn’t see issue with padding initialization here.
> >>>
> >>> Hm, agreed -- this test does do the right thing.
> >>>
> >>>>> But this isn't actually sufficient because they may _accidentally_
> >>>>> be zero already. The kernel tests specifically make sure to fill the
> >>>>> about-to-be-used stack with 0xff before calling a function like foo()
> >>>>> above.
> >>>
> >>> I've extracted the kernel test to build for userspace, and it behaves
> >>> the same way. See attached "stackinit.c".
> >>>
> >>> $ gcc-build/auto-var-init.4/installed/bin/gcc -O2 -Wall -o stackinit stackinit.c
> >>> $ ./stackinit 2>&1 | grep failures:
> >>> stackinit: failures: 23
> >>> $ gcc-build/auto-var-init.4/installed/bin/gcc -O2 -Wall -ftrivial-auto-var-init=zero -o stackinit stackinit.c
> >>> stackinit.c: In function ‘__leaf_switch_none’:
> >>> stackinit.c:326:26: warning: statement will never be executed
> >>> [-Wswitch-unreachable]
> >>> 326 |                 uint64_t var;
> >>>     |                          ^~~
> >>> $ ./stackinit 2>&1 | grep failures:
> >>> stackinit: failures: 6
> >>>
> >>> Same failures as seen in the kernel test (and an expected warning
> >>> about the initialization that will never happen for a pre-case switch
> >>> statement).
> >>>
> >>>>>
> >>>>> (And as an aside, it seems like naming the test cases with some details
> >>>>> about what is being tested in the filename would be nice -- it was
> >>>>> a little weird having to dig through their numeric names to find the
> >>>>> padding tests.)
> >>>>
> >>>> Yes, I will fix the testing names to more reflect the testing details.
> >>>
> >>> Great!
> >>>
> >>> --
> >>> Kees Cook
> >>> <stackinit.c>
>

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-15  7:56             ` Richard Biener
@ 2021-07-15 14:16               ` Qing Zhao
  2021-07-15 14:45                 ` Qing Zhao
  0 siblings, 1 reply; 25+ messages in thread
From: Qing Zhao @ 2021-07-15 14:16 UTC (permalink / raw)
  To: Richard Biener; +Cc: Kees Cook, Richard Sandiford, gcc-patches Qing Zhao via

Hi, Richard,

> On Jul 15, 2021, at 2:56 AM, Richard Biener <richard.guenther@gmail.com> wrote:
> 
>>> On Wed, Jul 14, 2021 at 1:17 AM Qing Zhao <qing.zhao@oracle.com> wrote:
>>>> 
>>>> Hi, Kees,
>>>> 
>>>> I took a look at the kernel testing case you attached in the previous email, and found the testing failed with the following case:
>>>> 
>>>> #define INIT_STRUCT_static_all          = { .one = arg->one,            \
>>>>                                           .two = arg->two,            \
>>>>                                           .three = arg->three,        \
>>>>                                           .four = arg->four,          \
>>>>                                       }
>>>> 
>>>> i.e, when the structure type auto variable has been explicitly initialized in the source code.  -ftrivial-auto-var-init in the 4th version
>>>> does not initialize the paddings for such variables.
>>>> 
>>>> But in the previous version of the patches ( 2 or 3), -ftrivial-auto-var-init initializes the paddings for such variables.
>>>> 
>>>> I intended to remove this part of the code from the 4th version of the patch since the implementation for initializing such paddings is completely different from
>>>> the initializing of the whole structure as a whole with memset in this version of the implementation.
>>>> 
>>>> If we really need this functionality, I will add another separate patch for this additional functionality, but not with this patch.
>>>> 
>>>> Richard, what’s your comment and suggestions on this?
>>> 
>>> I think this can be addressed in the gimplifier by adjusting
>>> gimplify_init_constructor to clear
>>> the object before the initialization (if it's not done via aggregate
>>> copying).
>> 
>> I did this in the previous versions of the patch like the following:
>> 
>> @@ -5001,6 +5185,17 @@ gimplify_init_constructor (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
>>          /* If a single access to the target must be ensured and all elements
>>             are zero, then it's optimal to clear whatever their number.  */
>>          cleared = true;
>> +       else if (flag_trivial_auto_var_init > AUTO_INIT_UNINITIALIZED
>> +                && !TREE_STATIC (object)
>> +                && type_has_padding (type))
>> +         /* If the user requests to initialize automatic variables with
>> +            paddings inside the type, we should initialize the paddings too.
>> +            C guarantees that brace-init with fewer initializers than members
>> +            aggregate will initialize the rest of the aggregate as-if it were
>> +            static initialization.  In turn static initialization guarantees
>> +            that pad is initialized to zero bits.
>> +            So, it's better to clear the whole record under such situation.  */
>> +         cleared = true;
>>        else
>>          cleared = false;
>> 
>> Then the paddings are also initialized to zeroes with this option. (Even for -ftrivial-auto-var-init=pattern).
>> 
>> Is the above change Okay? (With this change, when -ftrivial-auto-var-init=pattern, the paddings for the
>> structure variables that have explicit initializer will be ZEROed, not 0xFE)
> 
> I guess that would be the simplest way, yes.
> 
>>> The clearing
>>> could be done via .DEFERRED_INIT.
>> 
>> You mean to add additional calls to .DEFERRED_INIT for each individual padding of the structure in “gimplify_init_constructor"?
>> Then  later during RTL expand, expand these calls the same as other calls?
> 
> No, I actually meant to in your patch above set
> 
>    defered_padding_init = true;
> 
> and where 'cleared' is processed do sth like
> 
>  if (defered_padding_init)
>    .. emit .DEFERRED_INIT for the _whole_ variable ..
>  else if (cleared)
>     .. original cleared handling ...
> 
> that would retain the pattern init but possibly be less efficient in the end.

Okay, I see.

Yes, then this will resolve the inconsistent pattern-init issue for paddings. 
I will try this.

> 
>>> 
>>> Note that I think .DEFERRED_INIT can be elided for variables that do
>>> not have their address
>>> taken - otherwise we'll also have to worry about aggregate copy
>>> initialization and SRA
>>> decomposing the copy, initializing only the used parts.
>> 
>> Please explain this a little bit more.
> 
> For sth like
> 
> struct S { int i; long j; };
> 
> void bar (struct S);
> struct S
> foo (struct S *p)
> {
>  struct S q = *p;
>  struct S r = q;
>  bar (r);
>  return r;
> }
> 
> we don't get a .DEFERRED_INIT for 'r' (do we?)

No, we don’t emit .DEFERRED_INIT for both ‘q’ and ‘r’ since they are all explicitly initialized.
With the current 4th patch, the paddings inside this structure variable is not initialized.

However, if we “clear” the whole structure in "gimplify_init_constructor “, the initialization might happen. I will check on this.

and SRA decomposes the init to
> 
> 
>  <bb 2> :
>  q = *p_2(D);
>  q$i_9 = p_2(D)->i;
>  q$j_10 = p_2(D)->j;
>  r.i = q$i_9;
>  r.j = q$j_10;
>  bar (r);
>  D.1953 = r;
>  r ={v} {CLOBBER};
>  return D.1953;
> 
> which leaves its padding uninitialized.  Hmm, and that even happens when
> you make bar take struct S * and thus pass the address of 'r' to bar.

Will try this example and see how to resolve this issue.

Thanks for your explanation.

Qing
> 
> Richard.
> 
> 
>> Thanks.
>> 
>> Qing
>>> 
>>> Richard.
>>> 
>>>> Thanks.
>>>> 
>>>> Qing
>>>> 
>>>>> On Jul 13, 2021, at 4:29 PM, Kees Cook <keescook@chromium.org> wrote:
>>>>> 
>>>>> On Mon, Jul 12, 2021 at 08:28:55PM +0000, Qing Zhao wrote:
>>>>>>> On Jul 12, 2021, at 12:56 PM, Kees Cook <keescook@chromium.org> wrote:
>>>>>>> On Wed, Jul 07, 2021 at 05:38:02PM +0000, Qing Zhao wrote:
>>>>>>>> This is the 4th version of the patch for the new security feature for GCC.
>>>>>>> 
>>>>>>> It looks like padding initialization has regressed to where things where
>>>>>>> in version 1[1] (it was, however, working in version 2[2]). I'm seeing
>>>>>>> these failures again in the kernel self-test:
>>>>>>> 
>>>>>>> test_stackinit: small_hole_static_all FAIL (uninit bytes: 3)
>>>>>>> test_stackinit: big_hole_static_all FAIL (uninit bytes: 61)
>>>>>>> test_stackinit: trailing_hole_static_all FAIL (uninit bytes: 7)
>>>>>>> test_stackinit: small_hole_dynamic_all FAIL (uninit bytes: 3)
>>>>>>> test_stackinit: big_hole_dynamic_all FAIL (uninit bytes: 61)
>>>>>>> test_stackinit: trailing_hole_dynamic_all FAIL (uninit bytes: 7)
>>>>>> 
>>>>>> Are the above failures for -ftrivial-auto-var-init=zero or -ftrivial-auto-var-init=pattern?  Or both?
>>>>> 
>>>>> Yes, I was only testing =zero (the kernel test handles =pattern as well:
>>>>> it doesn't explicitly test for 0x00). I've verified with =pattern now,
>>>>> too.
>>>>> 
>>>>>> For the current implementation, I believe that all paddings should be initialized with this option,
>>>>>> for -ftrivial-auto-var-init=zero, the padding will be initialized to zero as before, however, for
>>>>>> -ftrivial-auto-var-init=pattern, the padding will be initialized to 0xFE byte-repeatable patterns.
>>>>> 
>>>>> I've double-checked that I'm using the right gcc, with the flag.
>>>>> 
>>>>>>> 
>>>>>>> In looking at the gcc test cases, I think the wrong thing is
>>>>>>> being checked: we want to verify the padding itself. For example,
>>>>>>> in auto-init-17.c, the actual bytes after "four" need to be checked,
>>>>>>> rather than "four" itself.
>>>>>> 
>>>>>> ******For the current auto-init-17.c
>>>>>> 
>>>>>> 1 /* Verify zero initialization for array type with structure element with
>>>>>> 2    padding.  */
>>>>>> 3 /* { dg-do compile } */
>>>>>> 4 /* { dg-options "-ftrivial-auto-var-init=zero" } */
>>>>>> 5
>>>>>> 6 struct test_trailing_hole {
>>>>>> 7         int one;
>>>>>> 8         int two;
>>>>>> 9         int three;
>>>>>> 10         char four;
>>>>>> 11         /* "sizeof(unsigned long) - 1" byte padding hole here. */
>>>>>> 12 };
>>>>>> 13
>>>>>> 14
>>>>>> 15 int foo ()
>>>>>> 16 {
>>>>>> 17   struct test_trailing_hole var[10];
>>>>>> 18   return var[2].four;
>>>>>> 19 }
>>>>>> 20
>>>>>> 21 /* { dg-final { scan-assembler "movl\t\\\$0," } } */
>>>>>> 22 /* { dg-final { scan-assembler "movl\t\\\$20," } } */
>>>>>> 23 /* { dg-final { scan-assembler "rep stosq" } } */
>>>>>> ~
>>>>>> ******We have the assembly as: (-ftrivial-auto-var-init=zero)
>>>>>> 
>>>>>>      .file   "auto-init-17.c"
>>>>>>      .text
>>>>>>      .globl  foo
>>>>>>      .type   foo, @function
>>>>>> foo:
>>>>>> .LFB0:
>>>>>>      .cfi_startproc
>>>>>>      pushq   %rbp
>>>>>>      .cfi_def_cfa_offset 16
>>>>>>      .cfi_offset 6, -16
>>>>>>      movq    %rsp, %rbp
>>>>>>      .cfi_def_cfa_register 6
>>>>>>      subq    $40, %rsp
>>>>>>      leaq    -160(%rbp), %rax
>>>>>>      movq    %rax, %rsi
>>>>>>      movl    $0, %eax
>>>>>>      movl    $20, %edx
>>>>>>      movq    %rsi, %rdi
>>>>>>      movq    %rdx, %rcx
>>>>>>      rep stosq
>>>>>>      movzbl  -116(%rbp), %eax
>>>>>>      movsbl  %al, %eax
>>>>>>      leave
>>>>>>      .cfi_def_cfa 7, 8
>>>>>>      ret
>>>>>>      .cfi_endproc
>>>>>> .LFE0:
>>>>>>      .size   foo, .-foo
>>>>>>      .section        .note.GNU-stack,"",@progbits
>>>>>> 
>>>>>> From the above, we can see,  “zero” will be used to initialize 8 * 20 = 16 * 10 bytes of memory starting from the beginning of “var”, that include all the padding holes inside
>>>>>> This array of structure.
>>>>>> 
>>>>>> I didn’t see issue with padding initialization here.
>>>>> 
>>>>> Hm, agreed -- this test does do the right thing.
>>>>> 
>>>>>>> But this isn't actually sufficient because they may _accidentally_
>>>>>>> be zero already. The kernel tests specifically make sure to fill the
>>>>>>> about-to-be-used stack with 0xff before calling a function like foo()
>>>>>>> above.
>>>>> 
>>>>> I've extracted the kernel test to build for userspace, and it behaves
>>>>> the same way. See attached "stackinit.c".
>>>>> 
>>>>> $ gcc-build/auto-var-init.4/installed/bin/gcc -O2 -Wall -o stackinit stackinit.c
>>>>> $ ./stackinit 2>&1 | grep failures:
>>>>> stackinit: failures: 23
>>>>> $ gcc-build/auto-var-init.4/installed/bin/gcc -O2 -Wall -ftrivial-auto-var-init=zero -o stackinit stackinit.c
>>>>> stackinit.c: In function ‘__leaf_switch_none’:
>>>>> stackinit.c:326:26: warning: statement will never be executed
>>>>> [-Wswitch-unreachable]
>>>>> 326 |                 uint64_t var;
>>>>>    |                          ^~~
>>>>> $ ./stackinit 2>&1 | grep failures:
>>>>> stackinit: failures: 6
>>>>> 
>>>>> Same failures as seen in the kernel test (and an expected warning
>>>>> about the initialization that will never happen for a pre-case switch
>>>>> statement).
>>>>> 
>>>>>>> 
>>>>>>> (And as an aside, it seems like naming the test cases with some details
>>>>>>> about what is being tested in the filename would be nice -- it was
>>>>>>> a little weird having to dig through their numeric names to find the
>>>>>>> padding tests.)
>>>>>> 
>>>>>> Yes, I will fix the testing names to more reflect the testing details.
>>>>> 
>>>>> Great!
>>>>> 
>>>>> --
>>>>> Kees Cook
>>>>> <stackinit.c>
>> 


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
  2021-07-15 14:16               ` Qing Zhao
@ 2021-07-15 14:45                 ` Qing Zhao
  0 siblings, 0 replies; 25+ messages in thread
From: Qing Zhao @ 2021-07-15 14:45 UTC (permalink / raw)
  To: Richard Biener; +Cc: Richard Sandiford, gcc-patches Qing Zhao via, Kees Cook



> On Jul 15, 2021, at 9:16 AM, Qing Zhao via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
> 
>> 
>>>> 
>>>> Note that I think .DEFERRED_INIT can be elided for variables that do
>>>> not have their address
>>>> taken - otherwise we'll also have to worry about aggregate copy
>>>> initialization and SRA
>>>> decomposing the copy, initializing only the used parts.
>>> 
>>> Please explain this a little bit more.
>> 
>> For sth like
>> 
>> struct S { int i; long j; };
>> 
>> void bar (struct S);
>> struct S
>> foo (struct S *p)
>> {
>> struct S q = *p;
>> struct S r = q;
>> bar (r);
>> return r;
>> }
>> 
>> we don't get a .DEFERRED_INIT for 'r' (do we?)
> 
> No, we don’t emit .DEFERRED_INIT for both ‘q’ and ‘r’ since they are all explicitly initialized.

Another thought on this example:

I think for the auto variable ‘q’ and ‘r’ in function ‘foo’, their initialization depend on the incoming parameter ‘p’.

If ‘p’ is an auto variable in ‘foo’s caller, then the incoming parameter should be initialized well in the caller, including it’s padding. 

So, I don’t think that we need to worry about such situation. 

If every function guarantees all its own auto-variables to be initialized completely including the paddings.
Then we can guarantee all such copy initialization through parameters all initialized completely. 

Let me know if I miss anything here.

Qing
> With the current 4th patch, the paddings inside this structure variable is not initialized.
> 
> However, if we “clear” the whole structure in "gimplify_init_constructor “, the initialization might happen. I will check on this.
> 
> and SRA decomposes the init to
>> 
>> 
>> <bb 2> :
>> q = *p_2(D);
>> q$i_9 = p_2(D)->i;
>> q$j_10 = p_2(D)->j;
>> r.i = q$i_9;
>> r.j = q$j_10;
>> bar (r);
>> D.1953 = r;
>> r ={v} {CLOBBER};
>> return D.1953;
>> 
>> which leaves its padding uninitialized.  Hmm, and that even happens when
>> you make bar take struct S * and thus pass the address of 'r' to bar.
> 
> Will try this example and see how to resolve this issue.
> 
> Thanks for your explanation.
> 
> Qing
>> 
>> Richard.
>> 
>> 
>>> Thanks.
>>> 
>>> Qing
>>>> 
>>>> Richard.
>>>> 
>>>>> Thanks.
>>>>> 
>>>>> Qing
>>>>> 
>>>>>> On Jul 13, 2021, at 4:29 PM, Kees Cook <keescook@chromium.org> wrote:
>>>>>> 
>>>>>> On Mon, Jul 12, 2021 at 08:28:55PM +0000, Qing Zhao wrote:
>>>>>>>> On Jul 12, 2021, at 12:56 PM, Kees Cook <keescook@chromium.org> wrote:
>>>>>>>> On Wed, Jul 07, 2021 at 05:38:02PM +0000, Qing Zhao wrote:
>>>>>>>>> This is the 4th version of the patch for the new security feature for GCC.
>>>>>>>> 
>>>>>>>> It looks like padding initialization has regressed to where things where
>>>>>>>> in version 1[1] (it was, however, working in version 2[2]). I'm seeing
>>>>>>>> these failures again in the kernel self-test:
>>>>>>>> 
>>>>>>>> test_stackinit: small_hole_static_all FAIL (uninit bytes: 3)
>>>>>>>> test_stackinit: big_hole_static_all FAIL (uninit bytes: 61)
>>>>>>>> test_stackinit: trailing_hole_static_all FAIL (uninit bytes: 7)
>>>>>>>> test_stackinit: small_hole_dynamic_all FAIL (uninit bytes: 3)
>>>>>>>> test_stackinit: big_hole_dynamic_all FAIL (uninit bytes: 61)
>>>>>>>> test_stackinit: trailing_hole_dynamic_all FAIL (uninit bytes: 7)
>>>>>>> 
>>>>>>> Are the above failures for -ftrivial-auto-var-init=zero or -ftrivial-auto-var-init=pattern?  Or both?
>>>>>> 
>>>>>> Yes, I was only testing =zero (the kernel test handles =pattern as well:
>>>>>> it doesn't explicitly test for 0x00). I've verified with =pattern now,
>>>>>> too.
>>>>>> 
>>>>>>> For the current implementation, I believe that all paddings should be initialized with this option,
>>>>>>> for -ftrivial-auto-var-init=zero, the padding will be initialized to zero as before, however, for
>>>>>>> -ftrivial-auto-var-init=pattern, the padding will be initialized to 0xFE byte-repeatable patterns.
>>>>>> 
>>>>>> I've double-checked that I'm using the right gcc, with the flag.
>>>>>> 
>>>>>>>> 
>>>>>>>> In looking at the gcc test cases, I think the wrong thing is
>>>>>>>> being checked: we want to verify the padding itself. For example,
>>>>>>>> in auto-init-17.c, the actual bytes after "four" need to be checked,
>>>>>>>> rather than "four" itself.
>>>>>>> 
>>>>>>> ******For the current auto-init-17.c
>>>>>>> 
>>>>>>> 1 /* Verify zero initialization for array type with structure element with
>>>>>>> 2    padding.  */
>>>>>>> 3 /* { dg-do compile } */
>>>>>>> 4 /* { dg-options "-ftrivial-auto-var-init=zero" } */
>>>>>>> 5
>>>>>>> 6 struct test_trailing_hole {
>>>>>>> 7         int one;
>>>>>>> 8         int two;
>>>>>>> 9         int three;
>>>>>>> 10         char four;
>>>>>>> 11         /* "sizeof(unsigned long) - 1" byte padding hole here. */
>>>>>>> 12 };
>>>>>>> 13
>>>>>>> 14
>>>>>>> 15 int foo ()
>>>>>>> 16 {
>>>>>>> 17   struct test_trailing_hole var[10];
>>>>>>> 18   return var[2].four;
>>>>>>> 19 }
>>>>>>> 20
>>>>>>> 21 /* { dg-final { scan-assembler "movl\t\\\$0," } } */
>>>>>>> 22 /* { dg-final { scan-assembler "movl\t\\\$20," } } */
>>>>>>> 23 /* { dg-final { scan-assembler "rep stosq" } } */
>>>>>>> ~
>>>>>>> ******We have the assembly as: (-ftrivial-auto-var-init=zero)
>>>>>>> 
>>>>>>>     .file   "auto-init-17.c"
>>>>>>>     .text
>>>>>>>     .globl  foo
>>>>>>>     .type   foo, @function
>>>>>>> foo:
>>>>>>> .LFB0:
>>>>>>>     .cfi_startproc
>>>>>>>     pushq   %rbp
>>>>>>>     .cfi_def_cfa_offset 16
>>>>>>>     .cfi_offset 6, -16
>>>>>>>     movq    %rsp, %rbp
>>>>>>>     .cfi_def_cfa_register 6
>>>>>>>     subq    $40, %rsp
>>>>>>>     leaq    -160(%rbp), %rax
>>>>>>>     movq    %rax, %rsi
>>>>>>>     movl    $0, %eax
>>>>>>>     movl    $20, %edx
>>>>>>>     movq    %rsi, %rdi
>>>>>>>     movq    %rdx, %rcx
>>>>>>>     rep stosq
>>>>>>>     movzbl  -116(%rbp), %eax
>>>>>>>     movsbl  %al, %eax
>>>>>>>     leave
>>>>>>>     .cfi_def_cfa 7, 8
>>>>>>>     ret
>>>>>>>     .cfi_endproc
>>>>>>> .LFE0:
>>>>>>>     .size   foo, .-foo
>>>>>>>     .section        .note.GNU-stack,"",@progbits
>>>>>>> 
>>>>>>> From the above, we can see,  “zero” will be used to initialize 8 * 20 = 16 * 10 bytes of memory starting from the beginning of “var”, that include all the padding holes inside
>>>>>>> This array of structure.
>>>>>>> 
>>>>>>> I didn’t see issue with padding initialization here.
>>>>>> 
>>>>>> Hm, agreed -- this test does do the right thing.
>>>>>> 
>>>>>>>> But this isn't actually sufficient because they may _accidentally_
>>>>>>>> be zero already. The kernel tests specifically make sure to fill the
>>>>>>>> about-to-be-used stack with 0xff before calling a function like foo()
>>>>>>>> above.
>>>>>> 
>>>>>> I've extracted the kernel test to build for userspace, and it behaves
>>>>>> the same way. See attached "stackinit.c".
>>>>>> 
>>>>>> $ gcc-build/auto-var-init.4/installed/bin/gcc -O2 -Wall -o stackinit stackinit.c
>>>>>> $ ./stackinit 2>&1 | grep failures:
>>>>>> stackinit: failures: 23
>>>>>> $ gcc-build/auto-var-init.4/installed/bin/gcc -O2 -Wall -ftrivial-auto-var-init=zero -o stackinit stackinit.c
>>>>>> stackinit.c: In function ‘__leaf_switch_none’:
>>>>>> stackinit.c:326:26: warning: statement will never be executed
>>>>>> [-Wswitch-unreachable]
>>>>>> 326 |                 uint64_t var;
>>>>>>   |                          ^~~
>>>>>> $ ./stackinit 2>&1 | grep failures:
>>>>>> stackinit: failures: 6
>>>>>> 
>>>>>> Same failures as seen in the kernel test (and an expected warning
>>>>>> about the initialization that will never happen for a pre-case switch
>>>>>> statement).
>>>>>> 
>>>>>>>> 
>>>>>>>> (And as an aside, it seems like naming the test cases with some details
>>>>>>>> about what is being tested in the filename would be nice -- it was
>>>>>>>> a little weird having to dig through their numeric names to find the
>>>>>>>> padding tests.)
>>>>>>> 
>>>>>>> Yes, I will fix the testing names to more reflect the testing details.
>>>>>> 
>>>>>> Great!
>>>>>> 
>>>>>> --
>>>>>> Kees Cook
>>>>>> <stackinit.c>


^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2021-07-15 14:45 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-07 17:38 [patch][version 4]add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc Qing Zhao
2021-07-08 13:29 ` Martin Jambor
2021-07-08 15:00   ` Qing Zhao
2021-07-08 21:10   ` Qing Zhao
2021-07-09 16:18     ` Martin Jambor
2021-07-09 18:52       ` Qing Zhao
2021-07-12  7:51       ` Richard Sandiford
2021-07-12 15:31         ` Qing Zhao
2021-07-12 17:06           ` Martin Jambor
2021-07-12 18:13             ` Qing Zhao
2021-07-12 17:56 ` Kees Cook
2021-07-12 20:28   ` Qing Zhao
2021-07-13 21:29     ` Kees Cook
2021-07-13 23:09       ` Kees Cook
2021-07-13 23:16       ` Qing Zhao
2021-07-14  2:42         ` Kees Cook
2021-07-14  7:14         ` Richard Biener
2021-07-14 14:09           ` Qing Zhao
2021-07-14 19:11             ` Kees Cook
2021-07-14 19:30               ` Qing Zhao
2021-07-14 21:23                 ` Kees Cook
2021-07-14 22:30                   ` Qing Zhao
2021-07-15  7:56             ` Richard Biener
2021-07-15 14:16               ` Qing Zhao
2021-07-15 14:45                 ` Qing Zhao

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).