@@ -291,6 +291,13 @@ enum auto_init_type {
AUTO_INIT_ZERO = 2
};
+/* Initialization of padding bits with zeros. */
+enum zero_init_padding_bits_kind {
+ ZERO_INIT_PADDING_BITS_STANDARD = 0,
+ ZERO_INIT_PADDING_BITS_UNIONS = 1,
+ ZERO_INIT_PADDING_BITS_ALL = 2
+};
+
/* Different instrumentation modes. */
enum sanitize_code {
/* AddressSanitizer. */
@@ -1225,6 +1225,9 @@ extern void omp_clause_range_check_faile
(vec_safe_length (CONSTRUCTOR_ELTS (NODE)))
#define CONSTRUCTOR_NO_CLEARING(NODE) \
(CONSTRUCTOR_CHECK (NODE)->base.public_flag)
+/* True if even padding bits should be zeroed during initialization. */
+#define CONSTRUCTOR_ZERO_PADDING_BITS(NODE) \
+ (CONSTRUCTOR_CHECK (NODE)->base.default_def_flag)
/* Iterate through the vector V of CONSTRUCTOR_ELT elements, yielding the
value of each element (stored within VAL). IX must be a scratch variable
@@ -3505,6 +3505,22 @@ fzero-call-used-regs=
Common RejectNegative Joined
Clear call-used registers upon function return.
+fzero-init-padding-bits=
+Common Joined RejectNegative Enum(zero_init_padding_bits_kind) Var(flag_zero_init_padding_bits) Init(ZERO_INIT_PADDING_BITS_STANDARD)
+-fzero-init-padding-bits=[standard|unions|all] Zero padding bits in initializers.
+
+Enum
+Name(zero_init_padding_bits_kind) Type(enum zero_init_padding_bits_kind) UnknownError(unrecognized zero init padding bits kind %qs)
+
+EnumValue
+Enum(zero_init_padding_bits_kind) String(standard) Value(ZERO_INIT_PADDING_BITS_STANDARD)
+
+EnumValue
+Enum(zero_init_padding_bits_kind) String(unions) Value(ZERO_INIT_PADDING_BITS_UNIONS)
+
+EnumValue
+Enum(zero_init_padding_bits_kind) String(all) Value(ZERO_INIT_PADDING_BITS_ALL)
+
g
Common Driver RejectNegative JoinedOrMissing
Generate debug information in default format.
@@ -7219,6 +7219,28 @@ categorize_ctor_elements_1 (const_tree c
if (*p_complete && !complete_ctor_at_level_p (TREE_TYPE (ctor),
num_fields, elt_type))
*p_complete = 0;
+ else if (TREE_CODE (TREE_TYPE (ctor)) == UNION_TYPE
+ || TREE_CODE (TREE_TYPE (ctor)) == QUAL_UNION_TYPE)
+ {
+ if (*p_complete
+ && CONSTRUCTOR_ZERO_PADDING_BITS (ctor)
+ && (num_fields
+ ? simple_cst_equal (TYPE_SIZE (TREE_TYPE (ctor)),
+ TYPE_SIZE (elt_type)) != 1
+ : type_has_padding_at_level_p (TREE_TYPE (ctor))))
+ *p_complete = 0;
+ else if (*p_complete > 0
+ && (num_fields
+ ? simple_cst_equal (TYPE_SIZE (TREE_TYPE (ctor)),
+ TYPE_SIZE (elt_type)) != 1
+ : type_has_padding_at_level_p (TREE_TYPE (ctor))))
+ *p_complete = -1;
+ }
+ else if (*p_complete
+ && (CONSTRUCTOR_ZERO_PADDING_BITS (ctor)
+ || flag_zero_init_padding_bits == ZERO_INIT_PADDING_BITS_ALL)
+ && type_has_padding_at_level_p (TREE_TYPE (ctor)))
+ *p_complete = 0;
else if (*p_complete > 0
&& type_has_padding_at_level_p (TREE_TYPE (ctor)))
*p_complete = -1;
@@ -7293,19 +7315,32 @@ bool
complete_ctor_at_level_p (const_tree type, HOST_WIDE_INT num_elts,
const_tree last_type)
{
- if (TREE_CODE (type) == UNION_TYPE
- || TREE_CODE (type) == QUAL_UNION_TYPE)
+ if (TREE_CODE (type) == UNION_TYPE || TREE_CODE (type) == QUAL_UNION_TYPE)
{
if (num_elts == 0)
- return false;
+ {
+ if (flag_zero_init_padding_bits >= ZERO_INIT_PADDING_BITS_UNIONS)
+ return false;
+
+ /* If the CONSTRUCTOR doesn't have any elts, it is
+ incomplete if the union has at least one field. */
+ for (tree f = TYPE_FIELDS (type); f; f = DECL_CHAIN (f))
+ if (TREE_CODE (f) == FIELD_DECL)
+ return false;
+
+ return true;
+ }
gcc_assert (num_elts == 1 && last_type);
- /* ??? We could look at each element of the union, and find the
- largest element. Which would avoid comparing the size of the
- initialized element against any tail padding in the union.
- Doesn't seem worth the effort... */
- return simple_cst_equal (TYPE_SIZE (type), TYPE_SIZE (last_type)) == 1;
+ if (flag_zero_init_padding_bits >= ZERO_INIT_PADDING_BITS_UNIONS)
+ /* ??? We could look at each element of the union, and find the
+ largest element. Which would avoid comparing the size of the
+ initialized element against any tail padding in the union.
+ Doesn't seem worth the effort... */
+ return simple_cst_equal (TYPE_SIZE (type), TYPE_SIZE (last_type)) == 1;
+
+ return true;
}
return count_type_elements (type, true) == num_elts;
@@ -4814,12 +4814,22 @@ type_has_padding_at_level_p (tree type)
return false;
}
case UNION_TYPE:
+ case QUAL_UNION_TYPE:
+ bool any_fields;
+ any_fields = false;
/* If any of the fields is smaller than the whole, there is padding. */
for (tree f = TYPE_FIELDS (type); f; f = DECL_CHAIN (f))
- if (TREE_CODE (f) == FIELD_DECL)
- if (simple_cst_equal (TYPE_SIZE (TREE_TYPE (f)),
- TREE_TYPE (type)) != 1)
- return true;
+ if (TREE_CODE (f) != FIELD_DECL)
+ continue;
+ else if (simple_cst_equal (TYPE_SIZE (TREE_TYPE (f)),
+ TYPE_SIZE (type)) != 1)
+ return true;
+ else
+ any_fields = true;
+ /* If the union doesn't have any fields and still has non-zero size,
+ all of it is padding. */
+ if (!any_fields && !integer_zerop (TYPE_SIZE (type)))
+ return true;
return false;
case ARRAY_TYPE:
case COMPLEX_TYPE:
@@ -745,7 +745,8 @@ Objective-C and Objective-C++ Dialects}.
-ftrampolines -ftrampoline-impl=@r{[}stack@r{|}heap@r{]}
-ftrapv -fwrapv
-fvisibility=@r{[}default@r{|}internal@r{|}hidden@r{|}protected@r{]}
--fstrict-volatile-bitfields -fsync-libcalls}
+-fstrict-volatile-bitfields -fsync-libcalls
+-fzero-init-padding-bits=@var{value}}
@item Developer Options
@xref{Developer Options,,GCC Developer Options}.
@@ -20013,6 +20014,43 @@ The default value of this option is enab
of the option is @option{-fno-sync-libcalls}. This option is used in
the implementation of the @file{libatomic} runtime library.
+@opindex fzero-init-padding-bits=@var{value}
+@item -fzero-init-padding-bits=@var{value}
+Guarantee zero initalization of padding bits in automatic variable
+initializers.
+Certain languages guarantee zero initialization of padding bits in
+certain cases, e.g. C23 when using empty initializers (@code{@{@}}),
+or C++ when using zero-initialization or C guarantees that fields
+not specified in an initializer have their padding bits zero initialized.
+This option allows to change when padding bits in initializers are
+guaranteed to be zero initialized.
+The default is @code{-fzero-init-padding-bits=standard}, which makes
+no further guarantees than the corresponding standard. E.g.@:
+
+@smallexample
+ struct A @{ char a; unsigned long long b; char c; @};
+ union B @{ char a; unsigned long long b; @};
+ struct A a = @{@}; // C23 guarantees padding bits are zero.
+ struct A b = @{ 1, 2, 3 @}; // No guarantees.
+ union B c = @{@}; // C23 guarantees padding bits are zero.
+ union B d = @{ 1 @}; // No guarantees.
+@end smallexample
+
+@code{-fzero-init-padding-bits=unions} guarantees zero initialization
+of padding bits in unions on top of what the standards guarantee,
+if the initializer of an union is empty (then all bits of the union
+are zero initialized) or if the initialized member of the union is
+smaller than the size of the union (in that case guarantees padding
+bits outside of the initialized member to be zero initialized).
+This was the GCC behavior before GCC 15 and in the above example guarantees
+zero initialization of last @code{sizeof (unsigned long long) - 1}
+bytes in the union.
+
+@code{-fzero-init-padding-bits=all} guarantees additionally
+zero initialization of padding bits of other aggregates, so
+the padding in between @code{b.a} and @code{b.b} (if any) and
+tail padding in the structure (if any).
+
@end table
@node Developer Options
@@ -6189,6 +6189,7 @@ c_parser_braced_init (c_parser *parser,
gcc_assert (c_parser_next_token_is (parser, CPP_OPEN_BRACE));
bool save_c_omp_array_section_p = c_omp_array_section_p;
c_omp_array_section_p = false;
+ bool zero_init_padding_bits = false;
matching_braces braces;
braces.consume_open (parser);
if (nested_p)
@@ -6202,6 +6203,8 @@ c_parser_braced_init (c_parser *parser,
{
pedwarn_c11 (brace_loc, OPT_Wpedantic,
"ISO C forbids empty initializer braces before C23");
+ if (flag_isoc23)
+ zero_init_padding_bits = true;
}
else
{
@@ -6242,6 +6245,8 @@ c_parser_braced_init (c_parser *parser,
location_t close_loc = next_tok->location;
c_parser_consume_token (parser);
ret = pop_init_level (brace_loc, 0, &braced_init_obstack, close_loc);
+ if (zero_init_padding_bits && TREE_CODE (ret.value) == CONSTRUCTOR)
+ CONSTRUCTOR_ZERO_PADDING_BITS (ret.value) = 1;
obstack_free (&braced_init_obstack, NULL);
set_c_expr_source_range (&ret, brace_loc, close_loc);
return ret;
@@ -123,9 +123,12 @@ void test_union_2a (void __user *dst, u8
void test_union_2b (void __user *dst, u8 v)
{
- union un_b u = {0};
+ union un_b u = {0}; /* { dg-message "region created on stack here" "where" } */
+ /* { dg-message "capacity: 4 bytes" "capacity" { target *-*-* } .-1 } */
u.j = v;
- copy_to_user(dst, &u, sizeof (union un_b));
+ copy_to_user(dst, &u, sizeof (union un_b)); /* { dg-warning "potential exposure of sensitive information by copying uninitialized data from stack" "warning" } */
+ /* { dg-message "3 bytes are uninitialized" "note how much" { target *-*-* } .-1 } */
+ /* { dg-message "bytes 1 - 3 are uninitialized" "note how much" { target *-*-* } .-2 } */
}
void test_union_3a (void __user *dst, u32 v)
@@ -150,8 +153,11 @@ void test_union_4a (void __user *dst, u8
void test_union_4b (void __user *dst, u8 v)
{
- union un_b u = {0};
- copy_to_user(dst, &u, sizeof (union un_b)); /* { dg-bogus "" } */
+ union un_b u = {0}; /* { dg-message "region created on stack here" "where" } */
+ /* { dg-message "capacity: 4 bytes" "capacity" { target *-*-* } .-1 } */
+ copy_to_user(dst, &u, sizeof (union un_b)); /* { dg-warning "potential exposure of sensitive information by copying uninitialized data from stack" "warning" } */
+ /* { dg-message "3 bytes are uninitialized" "note how much" { target *-*-* } .-1 } */
+ /* { dg-message "bytes 1 - 3 are uninitialized" "note how much" { target *-*-* } .-2 } */
}
struct st_union_5