Add -fcf-check-attribute=[yes|no|none] for Linux kernel

Message ID 20220510162954.2755700-1-hjl.tools@gmail.com
State New
Headers
Series Add -fcf-check-attribute=[yes|no|none] for Linux kernel |

Commit Message

H.J. Lu May 10, 2022, 4:29 p.m. UTC
  When compiling Linux kernel with -fcf-protection=branch to enable x86
Indiret Branch Tracking (IBT), ENDBR is added to all global functions.
This creates more "legal" forward edges than necessary.  -mmanual-endbr
provides a way to insert ENDBR instruction at function entry only via
the 'cf_check' function attribute and programmers can add the 'cf_check'
function attribute to functions which can be reached by indirect branch.

Add -fcf-check-attribute=[yes|no|none] to imply "cf_check" or "nocf_check"
function attributes so that GCC can produce a diagnostic when there is a
mismatch in cf_check or nocf_check function attributes.

This has been tested on Linux kernel.

gcc/

	PR target/102953
	* attribs.cc (decl_attributes): Add implied cf_check or nocf_check
	function attributes.
	* common.opt: Add -fcf-check-attribute=.
	* flag-types.h (cf_check_attribute): New.
	* doc/invoke.texi: Document -fcf-check-attribute=.

gcc/c/

	PR target/102953
	* c-decl.cc (diagnose_mismatched_decls): Check implied cf_check
	and nocf_check function attributes.

gcc/testsuite/

	PR target/102953
	* gcc.target/i386/pr102953-3.c: New test.
	* gcc.target/i386/pr102953-4.c: Likewise.
	* gcc.target/i386/pr102953-5.c: Likewise.
	* gcc.target/i386/pr102953-6.c: Likewise.
---
 gcc/attribs.cc                             | 19 +++++++++++++++++++
 gcc/c/c-decl.cc                            | 22 +++++++++++++++++++++-
 gcc/common.opt                             | 16 ++++++++++++++++
 gcc/doc/invoke.texi                        | 12 ++++++++++++
 gcc/flag-types.h                           |  8 ++++++++
 gcc/testsuite/gcc.target/i386/pr102953-3.c |  8 ++++++++
 gcc/testsuite/gcc.target/i386/pr102953-4.c |  7 +++++++
 gcc/testsuite/gcc.target/i386/pr102953-5.c |  7 +++++++
 gcc/testsuite/gcc.target/i386/pr102953-6.c |  8 ++++++++
 9 files changed, 106 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/pr102953-3.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr102953-4.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr102953-5.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr102953-6.c
  

Comments

Richard Biener May 11, 2022, 6:39 a.m. UTC | #1
On Tue, 10 May 2022, H.J. Lu wrote:

> When compiling Linux kernel with -fcf-protection=branch to enable x86
> Indiret Branch Tracking (IBT), ENDBR is added to all global functions.
> This creates more "legal" forward edges than necessary.  -mmanual-endbr
> provides a way to insert ENDBR instruction at function entry only via
> the 'cf_check' function attribute and programmers can add the 'cf_check'
> function attribute to functions which can be reached by indirect branch.
> 
> Add -fcf-check-attribute=[yes|no|none] to imply "cf_check" or "nocf_check"
> function attributes so that GCC can produce a diagnostic when there is a
> mismatch in cf_check or nocf_check function attributes.
> 
> This has been tested on Linux kernel.

I don't quite understand the purpose but wouldn't
-fcf-check-attribute=[cf|nocf|off] be better than having no and none?
Since 'off' is the default you need 'off' only to be able to negate
an earlier cf/nocf?

> gcc/
> 
> 	PR target/102953
> 	* attribs.cc (decl_attributes): Add implied cf_check or nocf_check
> 	function attributes.
> 	* common.opt: Add -fcf-check-attribute=.
> 	* flag-types.h (cf_check_attribute): New.
> 	* doc/invoke.texi: Document -fcf-check-attribute=.
> 
> gcc/c/
> 
> 	PR target/102953
> 	* c-decl.cc (diagnose_mismatched_decls): Check implied cf_check
> 	and nocf_check function attributes.
> 
> gcc/testsuite/
> 
> 	PR target/102953
> 	* gcc.target/i386/pr102953-3.c: New test.
> 	* gcc.target/i386/pr102953-4.c: Likewise.
> 	* gcc.target/i386/pr102953-5.c: Likewise.
> 	* gcc.target/i386/pr102953-6.c: Likewise.
> ---
>  gcc/attribs.cc                             | 19 +++++++++++++++++++
>  gcc/c/c-decl.cc                            | 22 +++++++++++++++++++++-
>  gcc/common.opt                             | 16 ++++++++++++++++
>  gcc/doc/invoke.texi                        | 12 ++++++++++++
>  gcc/flag-types.h                           |  8 ++++++++
>  gcc/testsuite/gcc.target/i386/pr102953-3.c |  8 ++++++++
>  gcc/testsuite/gcc.target/i386/pr102953-4.c |  7 +++++++
>  gcc/testsuite/gcc.target/i386/pr102953-5.c |  7 +++++++
>  gcc/testsuite/gcc.target/i386/pr102953-6.c |  8 ++++++++
>  9 files changed, 106 insertions(+), 1 deletion(-)
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr102953-3.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr102953-4.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr102953-5.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr102953-6.c
> 
> diff --git a/gcc/attribs.cc b/gcc/attribs.cc
> index b219f878042..34e8707eac1 100644
> --- a/gcc/attribs.cc
> +++ b/gcc/attribs.cc
> @@ -694,6 +694,25 @@ decl_attributes (tree *node, tree attributes, int flags,
>  	attributes = tree_cons (get_identifier ("no_icf"),  NULL, attributes);
>      }
>  
> +  /* -fcf-check-attribute=[yes|no] implies "cf_check" or "nocf_check"
> +     function attribute.  */
> +  if (TREE_CODE (*node) == FUNCTION_DECL
> +      && flag_cf_check_attribute != CF_CHECK_ATTRIBUTE_NONE
> +      && !fndecl_built_in_p (*node)
> +      && lookup_attribute ("nocf_check",
> +			   DECL_ATTRIBUTES (*node)) == nullptr
> +      && lookup_attribute ("cf_check",
> +			   DECL_ATTRIBUTES (*node)) == nullptr
> +      && (!attributes
> +	  || (lookup_attribute ("nocf_check", attributes) == nullptr
> +	      && lookup_attribute ("cf_check", attributes) == nullptr)))
> +    {
> +      const char *attr = (flag_cf_check_attribute == CF_CHECK_ATTRIBUTE_YES
> +			  ? "cf_check" : "nocf_check");
> +      attributes = tree_cons (get_identifier (attr), nullptr,
> +			      attributes);
> +    }
> +
>    targetm.insert_attributes (*node, &attributes);
>  
>    /* Note that attributes on the same declaration are not necessarily
> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> index c701f07befe..787c39dc0fe 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -2133,7 +2133,27 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
>  		error ("conflicting type qualifiers for %q+D", newdecl);
>  	    }
>  	  else
> -	    error ("conflicting types for %q+D; have %qT", newdecl, newtype);
> +	    {
> +	      if (flag_cf_check_attribute == CF_CHECK_ATTRIBUTE_NO
> +		  && (!lookup_attribute ("nocf_check",
> +					  TYPE_ATTRIBUTES (oldtype))
> +		       != !lookup_attribute ("nocf_check",
> +					     TYPE_ATTRIBUTES (newtype))))
> +		error ("conflicting types for %q+D; have %qT with "
> +		       "implied %<nocf_check%> attribute",
> +		       newdecl, newtype);
> +	      else if (flag_cf_check_attribute == CF_CHECK_ATTRIBUTE_YES
> +		       && (!lookup_attribute ("cf_check",
> +					     TYPE_ATTRIBUTES (oldtype))
> +			  != !lookup_attribute ("cf_check",
> +						TYPE_ATTRIBUTES (newtype))))
> +		error ("conflicting types for %q+D; have %qT with "
> +		       "implied %<cf_check%> attribute",
> +		       newdecl, newtype);
> +	      else
> +		error ("conflicting types for %q+D; have %qT",
> +		       newdecl, newtype);
> +	    }
>  	  diagnose_arglist_conflict (newdecl, olddecl, newtype, oldtype);
>  	  locate_old_decl (olddecl);
>  	  return false;
> diff --git a/gcc/common.opt b/gcc/common.opt
> index 8a0dafc522d..d5952a44765 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -1877,6 +1877,22 @@ Enum(cf_protection_level) String(check) Value(CF_CHECK)
>  EnumValue
>  Enum(cf_protection_level) String(none) Value(CF_NONE)
>  
> +fcf-check-attribute=
> +Target RejectNegative Joined Enum(cf_check_attribute) Var(flag_cf_check_attribute) Init(CF_CHECK_ATTRIBUTE_NONE)
> +-fcf-check-attribute=[none|yes|no] Select the implied function attribute.
> +
> +Enum
> +Name(cf_check_attribute) Type(enum cf_check_attribute) UnknownError(unknown default Control-Flow attribute %qs)
> +
> +EnumValue
> +Enum(cf_check_attribute) String(none) Value(CF_CHECK_ATTRIBUTE_NONE)
> +
> +EnumValue
> +Enum(cf_check_attribute) String(yes) Value(CF_CHECK_ATTRIBUTE_YES)
> +
> +EnumValue
> +Enum(cf_check_attribute) String(no) Value(CF_CHECK_ATTRIBUTE_NO)
> +
>  finstrument-functions
>  Common Var(flag_instrument_function_entry_exit)
>  Instrument function entry and exit with profiling calls.
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 7a35d9613a4..22e02b8be7e 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -606,6 +606,7 @@ Objective-C and Objective-C++ Dialects}.
>  -fasan-shadow-offset=@var{number}  -fsanitize-sections=@var{s1},@var{s2},... @gol
>  -fsanitize-undefined-trap-on-error  -fbounds-check @gol
>  -fcf-protection=@r{[}full@r{|}branch@r{|}return@r{|}none@r{|}check@r{]} @gol
> +-fcf-check-attribute=@r{[}none@r{|}yes@r{|}no@r{]} @gol
>  -fharden-compares -fharden-conditional-branches @gol
>  -fstack-protector  -fstack-protector-all  -fstack-protector-strong @gol
>  -fstack-protector-explicit  -fstack-check @gol
> @@ -16070,6 +16071,17 @@ Currently the x86 GNU/Linux target provides an implementation based
>  on Intel Control-flow Enforcement Technology (CET) which works for
>  i686 processor or newer.
>  
> +@item -fcf-check-attribute=@r{[}none@r{|}yes@r{|}no@r{]}
> +@opindex fcf-check-attribute
> +Select the implied function attribute for code instrumentation of
> +control-flow transfers.
> +
> +The value @code{yes} makes the @code{cf_check} attribute implied on
> +functions.  The value @code{no} makes the @code{nocf_check} attribute
> +implied on functions.  The value @code{none}, which is the default,
> +doesn't imply the @code{cf_check} nor @code{nocf_check} attributes on
> +functions.
> +
>  @item -fharden-compares
>  @opindex fharden-compares
>  For every logical test that survives gimple optimizations and is
> diff --git a/gcc/flag-types.h b/gcc/flag-types.h
> index 2c8498169e0..c84d67b1ac8 100644
> --- a/gcc/flag-types.h
> +++ b/gcc/flag-types.h
> @@ -449,6 +449,14 @@ enum cf_protection_level
>    CF_CHECK = 1 << 3
>  };
>  
> +/* Default Control-Flow Protection attribute.  */
> +enum cf_check_attribute
> +{
> +  CF_CHECK_ATTRIBUTE_NONE = 0,
> +  CF_CHECK_ATTRIBUTE_YES,
> +  CF_CHECK_ATTRIBUTE_NO
> +};
> +
>  /* Parloops schedule type.  */
>  enum parloops_schedule_type
>  {
> diff --git a/gcc/testsuite/gcc.target/i386/pr102953-3.c b/gcc/testsuite/gcc.target/i386/pr102953-3.c
> new file mode 100644
> index 00000000000..fd92c9c577e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr102953-3.c
> @@ -0,0 +1,8 @@
> +/* { dg-do compile } */
> +/* { dg-options "-Wall -fcf-protection -mmanual-endbr -fcf-check-attribute=no" } */
> +
> +static void __attribute__((cf_check)) foo(void);
> +static void foo(void) /* { dg-error "implied 'nocf_check' attribute" } */
> +{
> +}
> +void (*ptr)(void) = foo; /* { dg-warning "incompatible pointer type" } */
> diff --git a/gcc/testsuite/gcc.target/i386/pr102953-4.c b/gcc/testsuite/gcc.target/i386/pr102953-4.c
> new file mode 100644
> index 00000000000..cd8f4279180
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr102953-4.c
> @@ -0,0 +1,7 @@
> +/* { dg-do compile } */
> +/* { dg-options "-Wall -fcf-protection -mmanual-endbr -fcf-check-attribute=no" } */
> +
> +static void foo(void)
> +{
> +}
> +void (*ptr)(void) = foo; /* { dg-warning "incompatible pointer type" } */
> diff --git a/gcc/testsuite/gcc.target/i386/pr102953-5.c b/gcc/testsuite/gcc.target/i386/pr102953-5.c
> new file mode 100644
> index 00000000000..b1bd4afe85f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr102953-5.c
> @@ -0,0 +1,7 @@
> +/* { dg-do compile } */
> +/* { dg-options "-Wall -fcf-protection -mmanual-endbr -fcf-check-attribute=no" } */
> +
> +extern void foo(void);
> +void __attribute__((cf_check)) foo(void) /* { dg-error "implied 'nocf_check' attribute" } */
> +{
> +}
> diff --git a/gcc/testsuite/gcc.target/i386/pr102953-6.c b/gcc/testsuite/gcc.target/i386/pr102953-6.c
> new file mode 100644
> index 00000000000..ee0c66f94cb
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr102953-6.c
> @@ -0,0 +1,8 @@
> +/* { dg-do compile } */
> +/* { dg-options "-Wall -fcf-protection -mmanual-endbr -fcf-check-attribute=no" } */
> +
> +static int __attribute__((cf_check)) foo(char a[], int b)
> +{
> +  return 0;
> +}
> +int (*ptr)(char[], int) = foo;
>
  
H.J. Lu May 11, 2022, 2:53 p.m. UTC | #2
On Tue, May 10, 2022 at 11:39 PM Richard Biener <rguenther@suse.de> wrote:
>
> On Tue, 10 May 2022, H.J. Lu wrote:
>
> > When compiling Linux kernel with -fcf-protection=branch to enable x86
> > Indiret Branch Tracking (IBT), ENDBR is added to all global functions.
> > This creates more "legal" forward edges than necessary.  -mmanual-endbr
> > provides a way to insert ENDBR instruction at function entry only via
> > the 'cf_check' function attribute and programmers can add the 'cf_check'
> > function attribute to functions which can be reached by indirect branch.
> >
> > Add -fcf-check-attribute=[yes|no|none] to imply "cf_check" or "nocf_check"
> > function attributes so that GCC can produce a diagnostic when there is a
> > mismatch in cf_check or nocf_check function attributes.
> >
> > This has been tested on Linux kernel.
>
> I don't quite understand the purpose but wouldn't
> -fcf-check-attribute=[cf|nocf|off] be better than having no and none?
> Since 'off' is the default you need 'off' only to be able to negate
> an earlier cf/nocf?

This option adds implied "cf_check" or "nocf_check" attributes if there
is none.   I need an option not to add them by default.    I can use
 -fcf-check-attribute=none or  -fcf-check-attribute=off.  Which one is
better?

Thanks.

> > gcc/
> >
> >       PR target/102953
> >       * attribs.cc (decl_attributes): Add implied cf_check or nocf_check
> >       function attributes.
> >       * common.opt: Add -fcf-check-attribute=.
> >       * flag-types.h (cf_check_attribute): New.
> >       * doc/invoke.texi: Document -fcf-check-attribute=.
> >
> > gcc/c/
> >
> >       PR target/102953
> >       * c-decl.cc (diagnose_mismatched_decls): Check implied cf_check
> >       and nocf_check function attributes.
> >
> > gcc/testsuite/
> >
> >       PR target/102953
> >       * gcc.target/i386/pr102953-3.c: New test.
> >       * gcc.target/i386/pr102953-4.c: Likewise.
> >       * gcc.target/i386/pr102953-5.c: Likewise.
> >       * gcc.target/i386/pr102953-6.c: Likewise.
> > ---
> >  gcc/attribs.cc                             | 19 +++++++++++++++++++
> >  gcc/c/c-decl.cc                            | 22 +++++++++++++++++++++-
> >  gcc/common.opt                             | 16 ++++++++++++++++
> >  gcc/doc/invoke.texi                        | 12 ++++++++++++
> >  gcc/flag-types.h                           |  8 ++++++++
> >  gcc/testsuite/gcc.target/i386/pr102953-3.c |  8 ++++++++
> >  gcc/testsuite/gcc.target/i386/pr102953-4.c |  7 +++++++
> >  gcc/testsuite/gcc.target/i386/pr102953-5.c |  7 +++++++
> >  gcc/testsuite/gcc.target/i386/pr102953-6.c |  8 ++++++++
> >  9 files changed, 106 insertions(+), 1 deletion(-)
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr102953-3.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr102953-4.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr102953-5.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr102953-6.c
> >
> > diff --git a/gcc/attribs.cc b/gcc/attribs.cc
> > index b219f878042..34e8707eac1 100644
> > --- a/gcc/attribs.cc
> > +++ b/gcc/attribs.cc
> > @@ -694,6 +694,25 @@ decl_attributes (tree *node, tree attributes, int flags,
> >       attributes = tree_cons (get_identifier ("no_icf"),  NULL, attributes);
> >      }
> >
> > +  /* -fcf-check-attribute=[yes|no] implies "cf_check" or "nocf_check"
> > +     function attribute.  */
> > +  if (TREE_CODE (*node) == FUNCTION_DECL
> > +      && flag_cf_check_attribute != CF_CHECK_ATTRIBUTE_NONE
> > +      && !fndecl_built_in_p (*node)
> > +      && lookup_attribute ("nocf_check",
> > +                        DECL_ATTRIBUTES (*node)) == nullptr
> > +      && lookup_attribute ("cf_check",
> > +                        DECL_ATTRIBUTES (*node)) == nullptr
> > +      && (!attributes
> > +       || (lookup_attribute ("nocf_check", attributes) == nullptr
> > +           && lookup_attribute ("cf_check", attributes) == nullptr)))
> > +    {
> > +      const char *attr = (flag_cf_check_attribute == CF_CHECK_ATTRIBUTE_YES
> > +                       ? "cf_check" : "nocf_check");
> > +      attributes = tree_cons (get_identifier (attr), nullptr,
> > +                           attributes);
> > +    }
> > +
> >    targetm.insert_attributes (*node, &attributes);
> >
> >    /* Note that attributes on the same declaration are not necessarily
> > diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> > index c701f07befe..787c39dc0fe 100644
> > --- a/gcc/c/c-decl.cc
> > +++ b/gcc/c/c-decl.cc
> > @@ -2133,7 +2133,27 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
> >               error ("conflicting type qualifiers for %q+D", newdecl);
> >           }
> >         else
> > -         error ("conflicting types for %q+D; have %qT", newdecl, newtype);
> > +         {
> > +           if (flag_cf_check_attribute == CF_CHECK_ATTRIBUTE_NO
> > +               && (!lookup_attribute ("nocf_check",
> > +                                       TYPE_ATTRIBUTES (oldtype))
> > +                    != !lookup_attribute ("nocf_check",
> > +                                          TYPE_ATTRIBUTES (newtype))))
> > +             error ("conflicting types for %q+D; have %qT with "
> > +                    "implied %<nocf_check%> attribute",
> > +                    newdecl, newtype);
> > +           else if (flag_cf_check_attribute == CF_CHECK_ATTRIBUTE_YES
> > +                    && (!lookup_attribute ("cf_check",
> > +                                          TYPE_ATTRIBUTES (oldtype))
> > +                       != !lookup_attribute ("cf_check",
> > +                                             TYPE_ATTRIBUTES (newtype))))
> > +             error ("conflicting types for %q+D; have %qT with "
> > +                    "implied %<cf_check%> attribute",
> > +                    newdecl, newtype);
> > +           else
> > +             error ("conflicting types for %q+D; have %qT",
> > +                    newdecl, newtype);
> > +         }
> >         diagnose_arglist_conflict (newdecl, olddecl, newtype, oldtype);
> >         locate_old_decl (olddecl);
> >         return false;
> > diff --git a/gcc/common.opt b/gcc/common.opt
> > index 8a0dafc522d..d5952a44765 100644
> > --- a/gcc/common.opt
> > +++ b/gcc/common.opt
> > @@ -1877,6 +1877,22 @@ Enum(cf_protection_level) String(check) Value(CF_CHECK)
> >  EnumValue
> >  Enum(cf_protection_level) String(none) Value(CF_NONE)
> >
> > +fcf-check-attribute=
> > +Target RejectNegative Joined Enum(cf_check_attribute) Var(flag_cf_check_attribute) Init(CF_CHECK_ATTRIBUTE_NONE)
> > +-fcf-check-attribute=[none|yes|no] Select the implied function attribute.
> > +
> > +Enum
> > +Name(cf_check_attribute) Type(enum cf_check_attribute) UnknownError(unknown default Control-Flow attribute %qs)
> > +
> > +EnumValue
> > +Enum(cf_check_attribute) String(none) Value(CF_CHECK_ATTRIBUTE_NONE)
> > +
> > +EnumValue
> > +Enum(cf_check_attribute) String(yes) Value(CF_CHECK_ATTRIBUTE_YES)
> > +
> > +EnumValue
> > +Enum(cf_check_attribute) String(no) Value(CF_CHECK_ATTRIBUTE_NO)
> > +
> >  finstrument-functions
> >  Common Var(flag_instrument_function_entry_exit)
> >  Instrument function entry and exit with profiling calls.
> > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> > index 7a35d9613a4..22e02b8be7e 100644
> > --- a/gcc/doc/invoke.texi
> > +++ b/gcc/doc/invoke.texi
> > @@ -606,6 +606,7 @@ Objective-C and Objective-C++ Dialects}.
> >  -fasan-shadow-offset=@var{number}  -fsanitize-sections=@var{s1},@var{s2},... @gol
> >  -fsanitize-undefined-trap-on-error  -fbounds-check @gol
> >  -fcf-protection=@r{[}full@r{|}branch@r{|}return@r{|}none@r{|}check@r{]} @gol
> > +-fcf-check-attribute=@r{[}none@r{|}yes@r{|}no@r{]} @gol
> >  -fharden-compares -fharden-conditional-branches @gol
> >  -fstack-protector  -fstack-protector-all  -fstack-protector-strong @gol
> >  -fstack-protector-explicit  -fstack-check @gol
> > @@ -16070,6 +16071,17 @@ Currently the x86 GNU/Linux target provides an implementation based
> >  on Intel Control-flow Enforcement Technology (CET) which works for
> >  i686 processor or newer.
> >
> > +@item -fcf-check-attribute=@r{[}none@r{|}yes@r{|}no@r{]}
> > +@opindex fcf-check-attribute
> > +Select the implied function attribute for code instrumentation of
> > +control-flow transfers.
> > +
> > +The value @code{yes} makes the @code{cf_check} attribute implied on
> > +functions.  The value @code{no} makes the @code{nocf_check} attribute
> > +implied on functions.  The value @code{none}, which is the default,
> > +doesn't imply the @code{cf_check} nor @code{nocf_check} attributes on
> > +functions.
> > +
> >  @item -fharden-compares
> >  @opindex fharden-compares
> >  For every logical test that survives gimple optimizations and is
> > diff --git a/gcc/flag-types.h b/gcc/flag-types.h
> > index 2c8498169e0..c84d67b1ac8 100644
> > --- a/gcc/flag-types.h
> > +++ b/gcc/flag-types.h
> > @@ -449,6 +449,14 @@ enum cf_protection_level
> >    CF_CHECK = 1 << 3
> >  };
> >
> > +/* Default Control-Flow Protection attribute.  */
> > +enum cf_check_attribute
> > +{
> > +  CF_CHECK_ATTRIBUTE_NONE = 0,
> > +  CF_CHECK_ATTRIBUTE_YES,
> > +  CF_CHECK_ATTRIBUTE_NO
> > +};
> > +
> >  /* Parloops schedule type.  */
> >  enum parloops_schedule_type
> >  {
> > diff --git a/gcc/testsuite/gcc.target/i386/pr102953-3.c b/gcc/testsuite/gcc.target/i386/pr102953-3.c
> > new file mode 100644
> > index 00000000000..fd92c9c577e
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr102953-3.c
> > @@ -0,0 +1,8 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-Wall -fcf-protection -mmanual-endbr -fcf-check-attribute=no" } */
> > +
> > +static void __attribute__((cf_check)) foo(void);
> > +static void foo(void) /* { dg-error "implied 'nocf_check' attribute" } */
> > +{
> > +}
> > +void (*ptr)(void) = foo; /* { dg-warning "incompatible pointer type" } */
> > diff --git a/gcc/testsuite/gcc.target/i386/pr102953-4.c b/gcc/testsuite/gcc.target/i386/pr102953-4.c
> > new file mode 100644
> > index 00000000000..cd8f4279180
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr102953-4.c
> > @@ -0,0 +1,7 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-Wall -fcf-protection -mmanual-endbr -fcf-check-attribute=no" } */
> > +
> > +static void foo(void)
> > +{
> > +}
> > +void (*ptr)(void) = foo; /* { dg-warning "incompatible pointer type" } */
> > diff --git a/gcc/testsuite/gcc.target/i386/pr102953-5.c b/gcc/testsuite/gcc.target/i386/pr102953-5.c
> > new file mode 100644
> > index 00000000000..b1bd4afe85f
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr102953-5.c
> > @@ -0,0 +1,7 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-Wall -fcf-protection -mmanual-endbr -fcf-check-attribute=no" } */
> > +
> > +extern void foo(void);
> > +void __attribute__((cf_check)) foo(void) /* { dg-error "implied 'nocf_check' attribute" } */
> > +{
> > +}
> > diff --git a/gcc/testsuite/gcc.target/i386/pr102953-6.c b/gcc/testsuite/gcc.target/i386/pr102953-6.c
> > new file mode 100644
> > index 00000000000..ee0c66f94cb
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr102953-6.c
> > @@ -0,0 +1,8 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-Wall -fcf-protection -mmanual-endbr -fcf-check-attribute=no" } */
> > +
> > +static int __attribute__((cf_check)) foo(char a[], int b)
> > +{
> > +  return 0;
> > +}
> > +int (*ptr)(char[], int) = foo;
> >
>
> --
> Richard Biener <rguenther@suse.de>
> SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg,
> Germany; GF: Ivo Totev; HRB 36809 (AG Nuernberg)
  
Richard Biener May 12, 2022, 6:46 a.m. UTC | #3
On Wed, 11 May 2022, H.J. Lu wrote:

> On Tue, May 10, 2022 at 11:39 PM Richard Biener <rguenther@suse.de> wrote:
> >
> > On Tue, 10 May 2022, H.J. Lu wrote:
> >
> > > When compiling Linux kernel with -fcf-protection=branch to enable x86
> > > Indiret Branch Tracking (IBT), ENDBR is added to all global functions.
> > > This creates more "legal" forward edges than necessary.  -mmanual-endbr
> > > provides a way to insert ENDBR instruction at function entry only via
> > > the 'cf_check' function attribute and programmers can add the 'cf_check'
> > > function attribute to functions which can be reached by indirect branch.
> > >
> > > Add -fcf-check-attribute=[yes|no|none] to imply "cf_check" or "nocf_check"
> > > function attributes so that GCC can produce a diagnostic when there is a
> > > mismatch in cf_check or nocf_check function attributes.
> > >
> > > This has been tested on Linux kernel.
> >
> > I don't quite understand the purpose but wouldn't
> > -fcf-check-attribute=[cf|nocf|off] be better than having no and none?
> > Since 'off' is the default you need 'off' only to be able to negate
> > an earlier cf/nocf?
> 
> This option adds implied "cf_check" or "nocf_check" attributes if there
> is none.   I need an option not to add them by default.    I can use
>  -fcf-check-attribute=none or  -fcf-check-attribute=off.  Which one is
> better?

I guess 'none' is then better, but still -fcf-check-attribute=[cf|nocf]
is then better matching than -fcf-check-attribute=[yes|no].

Richard.

> Thanks.
> 
> > > gcc/
> > >
> > >       PR target/102953
> > >       * attribs.cc (decl_attributes): Add implied cf_check or nocf_check
> > >       function attributes.
> > >       * common.opt: Add -fcf-check-attribute=.
> > >       * flag-types.h (cf_check_attribute): New.
> > >       * doc/invoke.texi: Document -fcf-check-attribute=.
> > >
> > > gcc/c/
> > >
> > >       PR target/102953
> > >       * c-decl.cc (diagnose_mismatched_decls): Check implied cf_check
> > >       and nocf_check function attributes.
> > >
> > > gcc/testsuite/
> > >
> > >       PR target/102953
> > >       * gcc.target/i386/pr102953-3.c: New test.
> > >       * gcc.target/i386/pr102953-4.c: Likewise.
> > >       * gcc.target/i386/pr102953-5.c: Likewise.
> > >       * gcc.target/i386/pr102953-6.c: Likewise.
> > > ---
> > >  gcc/attribs.cc                             | 19 +++++++++++++++++++
> > >  gcc/c/c-decl.cc                            | 22 +++++++++++++++++++++-
> > >  gcc/common.opt                             | 16 ++++++++++++++++
> > >  gcc/doc/invoke.texi                        | 12 ++++++++++++
> > >  gcc/flag-types.h                           |  8 ++++++++
> > >  gcc/testsuite/gcc.target/i386/pr102953-3.c |  8 ++++++++
> > >  gcc/testsuite/gcc.target/i386/pr102953-4.c |  7 +++++++
> > >  gcc/testsuite/gcc.target/i386/pr102953-5.c |  7 +++++++
> > >  gcc/testsuite/gcc.target/i386/pr102953-6.c |  8 ++++++++
> > >  9 files changed, 106 insertions(+), 1 deletion(-)
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr102953-3.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr102953-4.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr102953-5.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr102953-6.c
> > >
> > > diff --git a/gcc/attribs.cc b/gcc/attribs.cc
> > > index b219f878042..34e8707eac1 100644
> > > --- a/gcc/attribs.cc
> > > +++ b/gcc/attribs.cc
> > > @@ -694,6 +694,25 @@ decl_attributes (tree *node, tree attributes, int flags,
> > >       attributes = tree_cons (get_identifier ("no_icf"),  NULL, attributes);
> > >      }
> > >
> > > +  /* -fcf-check-attribute=[yes|no] implies "cf_check" or "nocf_check"
> > > +     function attribute.  */
> > > +  if (TREE_CODE (*node) == FUNCTION_DECL
> > > +      && flag_cf_check_attribute != CF_CHECK_ATTRIBUTE_NONE
> > > +      && !fndecl_built_in_p (*node)
> > > +      && lookup_attribute ("nocf_check",
> > > +                        DECL_ATTRIBUTES (*node)) == nullptr
> > > +      && lookup_attribute ("cf_check",
> > > +                        DECL_ATTRIBUTES (*node)) == nullptr
> > > +      && (!attributes
> > > +       || (lookup_attribute ("nocf_check", attributes) == nullptr
> > > +           && lookup_attribute ("cf_check", attributes) == nullptr)))
> > > +    {
> > > +      const char *attr = (flag_cf_check_attribute == CF_CHECK_ATTRIBUTE_YES
> > > +                       ? "cf_check" : "nocf_check");
> > > +      attributes = tree_cons (get_identifier (attr), nullptr,
> > > +                           attributes);
> > > +    }
> > > +
> > >    targetm.insert_attributes (*node, &attributes);
> > >
> > >    /* Note that attributes on the same declaration are not necessarily
> > > diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> > > index c701f07befe..787c39dc0fe 100644
> > > --- a/gcc/c/c-decl.cc
> > > +++ b/gcc/c/c-decl.cc
> > > @@ -2133,7 +2133,27 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
> > >               error ("conflicting type qualifiers for %q+D", newdecl);
> > >           }
> > >         else
> > > -         error ("conflicting types for %q+D; have %qT", newdecl, newtype);
> > > +         {
> > > +           if (flag_cf_check_attribute == CF_CHECK_ATTRIBUTE_NO
> > > +               && (!lookup_attribute ("nocf_check",
> > > +                                       TYPE_ATTRIBUTES (oldtype))
> > > +                    != !lookup_attribute ("nocf_check",
> > > +                                          TYPE_ATTRIBUTES (newtype))))
> > > +             error ("conflicting types for %q+D; have %qT with "
> > > +                    "implied %<nocf_check%> attribute",
> > > +                    newdecl, newtype);
> > > +           else if (flag_cf_check_attribute == CF_CHECK_ATTRIBUTE_YES
> > > +                    && (!lookup_attribute ("cf_check",
> > > +                                          TYPE_ATTRIBUTES (oldtype))
> > > +                       != !lookup_attribute ("cf_check",
> > > +                                             TYPE_ATTRIBUTES (newtype))))
> > > +             error ("conflicting types for %q+D; have %qT with "
> > > +                    "implied %<cf_check%> attribute",
> > > +                    newdecl, newtype);
> > > +           else
> > > +             error ("conflicting types for %q+D; have %qT",
> > > +                    newdecl, newtype);
> > > +         }
> > >         diagnose_arglist_conflict (newdecl, olddecl, newtype, oldtype);
> > >         locate_old_decl (olddecl);
> > >         return false;
> > > diff --git a/gcc/common.opt b/gcc/common.opt
> > > index 8a0dafc522d..d5952a44765 100644
> > > --- a/gcc/common.opt
> > > +++ b/gcc/common.opt
> > > @@ -1877,6 +1877,22 @@ Enum(cf_protection_level) String(check) Value(CF_CHECK)
> > >  EnumValue
> > >  Enum(cf_protection_level) String(none) Value(CF_NONE)
> > >
> > > +fcf-check-attribute=
> > > +Target RejectNegative Joined Enum(cf_check_attribute) Var(flag_cf_check_attribute) Init(CF_CHECK_ATTRIBUTE_NONE)
> > > +-fcf-check-attribute=[none|yes|no] Select the implied function attribute.
> > > +
> > > +Enum
> > > +Name(cf_check_attribute) Type(enum cf_check_attribute) UnknownError(unknown default Control-Flow attribute %qs)
> > > +
> > > +EnumValue
> > > +Enum(cf_check_attribute) String(none) Value(CF_CHECK_ATTRIBUTE_NONE)
> > > +
> > > +EnumValue
> > > +Enum(cf_check_attribute) String(yes) Value(CF_CHECK_ATTRIBUTE_YES)
> > > +
> > > +EnumValue
> > > +Enum(cf_check_attribute) String(no) Value(CF_CHECK_ATTRIBUTE_NO)
> > > +
> > >  finstrument-functions
> > >  Common Var(flag_instrument_function_entry_exit)
> > >  Instrument function entry and exit with profiling calls.
> > > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> > > index 7a35d9613a4..22e02b8be7e 100644
> > > --- a/gcc/doc/invoke.texi
> > > +++ b/gcc/doc/invoke.texi
> > > @@ -606,6 +606,7 @@ Objective-C and Objective-C++ Dialects}.
> > >  -fasan-shadow-offset=@var{number}  -fsanitize-sections=@var{s1},@var{s2},... @gol
> > >  -fsanitize-undefined-trap-on-error  -fbounds-check @gol
> > >  -fcf-protection=@r{[}full@r{|}branch@r{|}return@r{|}none@r{|}check@r{]} @gol
> > > +-fcf-check-attribute=@r{[}none@r{|}yes@r{|}no@r{]} @gol
> > >  -fharden-compares -fharden-conditional-branches @gol
> > >  -fstack-protector  -fstack-protector-all  -fstack-protector-strong @gol
> > >  -fstack-protector-explicit  -fstack-check @gol
> > > @@ -16070,6 +16071,17 @@ Currently the x86 GNU/Linux target provides an implementation based
> > >  on Intel Control-flow Enforcement Technology (CET) which works for
> > >  i686 processor or newer.
> > >
> > > +@item -fcf-check-attribute=@r{[}none@r{|}yes@r{|}no@r{]}
> > > +@opindex fcf-check-attribute
> > > +Select the implied function attribute for code instrumentation of
> > > +control-flow transfers.
> > > +
> > > +The value @code{yes} makes the @code{cf_check} attribute implied on
> > > +functions.  The value @code{no} makes the @code{nocf_check} attribute
> > > +implied on functions.  The value @code{none}, which is the default,
> > > +doesn't imply the @code{cf_check} nor @code{nocf_check} attributes on
> > > +functions.
> > > +
> > >  @item -fharden-compares
> > >  @opindex fharden-compares
> > >  For every logical test that survives gimple optimizations and is
> > > diff --git a/gcc/flag-types.h b/gcc/flag-types.h
> > > index 2c8498169e0..c84d67b1ac8 100644
> > > --- a/gcc/flag-types.h
> > > +++ b/gcc/flag-types.h
> > > @@ -449,6 +449,14 @@ enum cf_protection_level
> > >    CF_CHECK = 1 << 3
> > >  };
> > >
> > > +/* Default Control-Flow Protection attribute.  */
> > > +enum cf_check_attribute
> > > +{
> > > +  CF_CHECK_ATTRIBUTE_NONE = 0,
> > > +  CF_CHECK_ATTRIBUTE_YES,
> > > +  CF_CHECK_ATTRIBUTE_NO
> > > +};
> > > +
> > >  /* Parloops schedule type.  */
> > >  enum parloops_schedule_type
> > >  {
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr102953-3.c b/gcc/testsuite/gcc.target/i386/pr102953-3.c
> > > new file mode 100644
> > > index 00000000000..fd92c9c577e
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr102953-3.c
> > > @@ -0,0 +1,8 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-Wall -fcf-protection -mmanual-endbr -fcf-check-attribute=no" } */
> > > +
> > > +static void __attribute__((cf_check)) foo(void);
> > > +static void foo(void) /* { dg-error "implied 'nocf_check' attribute" } */
> > > +{
> > > +}
> > > +void (*ptr)(void) = foo; /* { dg-warning "incompatible pointer type" } */
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr102953-4.c b/gcc/testsuite/gcc.target/i386/pr102953-4.c
> > > new file mode 100644
> > > index 00000000000..cd8f4279180
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr102953-4.c
> > > @@ -0,0 +1,7 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-Wall -fcf-protection -mmanual-endbr -fcf-check-attribute=no" } */
> > > +
> > > +static void foo(void)
> > > +{
> > > +}
> > > +void (*ptr)(void) = foo; /* { dg-warning "incompatible pointer type" } */
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr102953-5.c b/gcc/testsuite/gcc.target/i386/pr102953-5.c
> > > new file mode 100644
> > > index 00000000000..b1bd4afe85f
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr102953-5.c
> > > @@ -0,0 +1,7 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-Wall -fcf-protection -mmanual-endbr -fcf-check-attribute=no" } */
> > > +
> > > +extern void foo(void);
> > > +void __attribute__((cf_check)) foo(void) /* { dg-error "implied 'nocf_check' attribute" } */
> > > +{
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr102953-6.c b/gcc/testsuite/gcc.target/i386/pr102953-6.c
> > > new file mode 100644
> > > index 00000000000..ee0c66f94cb
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr102953-6.c
> > > @@ -0,0 +1,8 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-Wall -fcf-protection -mmanual-endbr -fcf-check-attribute=no" } */
> > > +
> > > +static int __attribute__((cf_check)) foo(char a[], int b)
> > > +{
> > > +  return 0;
> > > +}
> > > +int (*ptr)(char[], int) = foo;
> > >
> >
> > --
> > Richard Biener <rguenther@suse.de>
> > SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg,
> > Germany; GF: Ivo Totev; HRB 36809 (AG Nuernberg)
> 
> 
> 
>
  

Patch

diff --git a/gcc/attribs.cc b/gcc/attribs.cc
index b219f878042..34e8707eac1 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -694,6 +694,25 @@  decl_attributes (tree *node, tree attributes, int flags,
 	attributes = tree_cons (get_identifier ("no_icf"),  NULL, attributes);
     }
 
+  /* -fcf-check-attribute=[yes|no] implies "cf_check" or "nocf_check"
+     function attribute.  */
+  if (TREE_CODE (*node) == FUNCTION_DECL
+      && flag_cf_check_attribute != CF_CHECK_ATTRIBUTE_NONE
+      && !fndecl_built_in_p (*node)
+      && lookup_attribute ("nocf_check",
+			   DECL_ATTRIBUTES (*node)) == nullptr
+      && lookup_attribute ("cf_check",
+			   DECL_ATTRIBUTES (*node)) == nullptr
+      && (!attributes
+	  || (lookup_attribute ("nocf_check", attributes) == nullptr
+	      && lookup_attribute ("cf_check", attributes) == nullptr)))
+    {
+      const char *attr = (flag_cf_check_attribute == CF_CHECK_ATTRIBUTE_YES
+			  ? "cf_check" : "nocf_check");
+      attributes = tree_cons (get_identifier (attr), nullptr,
+			      attributes);
+    }
+
   targetm.insert_attributes (*node, &attributes);
 
   /* Note that attributes on the same declaration are not necessarily
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index c701f07befe..787c39dc0fe 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -2133,7 +2133,27 @@  diagnose_mismatched_decls (tree newdecl, tree olddecl,
 		error ("conflicting type qualifiers for %q+D", newdecl);
 	    }
 	  else
-	    error ("conflicting types for %q+D; have %qT", newdecl, newtype);
+	    {
+	      if (flag_cf_check_attribute == CF_CHECK_ATTRIBUTE_NO
+		  && (!lookup_attribute ("nocf_check",
+					  TYPE_ATTRIBUTES (oldtype))
+		       != !lookup_attribute ("nocf_check",
+					     TYPE_ATTRIBUTES (newtype))))
+		error ("conflicting types for %q+D; have %qT with "
+		       "implied %<nocf_check%> attribute",
+		       newdecl, newtype);
+	      else if (flag_cf_check_attribute == CF_CHECK_ATTRIBUTE_YES
+		       && (!lookup_attribute ("cf_check",
+					     TYPE_ATTRIBUTES (oldtype))
+			  != !lookup_attribute ("cf_check",
+						TYPE_ATTRIBUTES (newtype))))
+		error ("conflicting types for %q+D; have %qT with "
+		       "implied %<cf_check%> attribute",
+		       newdecl, newtype);
+	      else
+		error ("conflicting types for %q+D; have %qT",
+		       newdecl, newtype);
+	    }
 	  diagnose_arglist_conflict (newdecl, olddecl, newtype, oldtype);
 	  locate_old_decl (olddecl);
 	  return false;
diff --git a/gcc/common.opt b/gcc/common.opt
index 8a0dafc522d..d5952a44765 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1877,6 +1877,22 @@  Enum(cf_protection_level) String(check) Value(CF_CHECK)
 EnumValue
 Enum(cf_protection_level) String(none) Value(CF_NONE)
 
+fcf-check-attribute=
+Target RejectNegative Joined Enum(cf_check_attribute) Var(flag_cf_check_attribute) Init(CF_CHECK_ATTRIBUTE_NONE)
+-fcf-check-attribute=[none|yes|no] Select the implied function attribute.
+
+Enum
+Name(cf_check_attribute) Type(enum cf_check_attribute) UnknownError(unknown default Control-Flow attribute %qs)
+
+EnumValue
+Enum(cf_check_attribute) String(none) Value(CF_CHECK_ATTRIBUTE_NONE)
+
+EnumValue
+Enum(cf_check_attribute) String(yes) Value(CF_CHECK_ATTRIBUTE_YES)
+
+EnumValue
+Enum(cf_check_attribute) String(no) Value(CF_CHECK_ATTRIBUTE_NO)
+
 finstrument-functions
 Common Var(flag_instrument_function_entry_exit)
 Instrument function entry and exit with profiling calls.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 7a35d9613a4..22e02b8be7e 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -606,6 +606,7 @@  Objective-C and Objective-C++ Dialects}.
 -fasan-shadow-offset=@var{number}  -fsanitize-sections=@var{s1},@var{s2},... @gol
 -fsanitize-undefined-trap-on-error  -fbounds-check @gol
 -fcf-protection=@r{[}full@r{|}branch@r{|}return@r{|}none@r{|}check@r{]} @gol
+-fcf-check-attribute=@r{[}none@r{|}yes@r{|}no@r{]} @gol
 -fharden-compares -fharden-conditional-branches @gol
 -fstack-protector  -fstack-protector-all  -fstack-protector-strong @gol
 -fstack-protector-explicit  -fstack-check @gol
@@ -16070,6 +16071,17 @@  Currently the x86 GNU/Linux target provides an implementation based
 on Intel Control-flow Enforcement Technology (CET) which works for
 i686 processor or newer.
 
+@item -fcf-check-attribute=@r{[}none@r{|}yes@r{|}no@r{]}
+@opindex fcf-check-attribute
+Select the implied function attribute for code instrumentation of
+control-flow transfers.
+
+The value @code{yes} makes the @code{cf_check} attribute implied on
+functions.  The value @code{no} makes the @code{nocf_check} attribute
+implied on functions.  The value @code{none}, which is the default,
+doesn't imply the @code{cf_check} nor @code{nocf_check} attributes on
+functions.
+
 @item -fharden-compares
 @opindex fharden-compares
 For every logical test that survives gimple optimizations and is
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index 2c8498169e0..c84d67b1ac8 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -449,6 +449,14 @@  enum cf_protection_level
   CF_CHECK = 1 << 3
 };
 
+/* Default Control-Flow Protection attribute.  */
+enum cf_check_attribute
+{
+  CF_CHECK_ATTRIBUTE_NONE = 0,
+  CF_CHECK_ATTRIBUTE_YES,
+  CF_CHECK_ATTRIBUTE_NO
+};
+
 /* Parloops schedule type.  */
 enum parloops_schedule_type
 {
diff --git a/gcc/testsuite/gcc.target/i386/pr102953-3.c b/gcc/testsuite/gcc.target/i386/pr102953-3.c
new file mode 100644
index 00000000000..fd92c9c577e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr102953-3.c
@@ -0,0 +1,8 @@ 
+/* { dg-do compile } */
+/* { dg-options "-Wall -fcf-protection -mmanual-endbr -fcf-check-attribute=no" } */
+
+static void __attribute__((cf_check)) foo(void);
+static void foo(void) /* { dg-error "implied 'nocf_check' attribute" } */
+{
+}
+void (*ptr)(void) = foo; /* { dg-warning "incompatible pointer type" } */
diff --git a/gcc/testsuite/gcc.target/i386/pr102953-4.c b/gcc/testsuite/gcc.target/i386/pr102953-4.c
new file mode 100644
index 00000000000..cd8f4279180
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr102953-4.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile } */
+/* { dg-options "-Wall -fcf-protection -mmanual-endbr -fcf-check-attribute=no" } */
+
+static void foo(void)
+{
+}
+void (*ptr)(void) = foo; /* { dg-warning "incompatible pointer type" } */
diff --git a/gcc/testsuite/gcc.target/i386/pr102953-5.c b/gcc/testsuite/gcc.target/i386/pr102953-5.c
new file mode 100644
index 00000000000..b1bd4afe85f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr102953-5.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile } */
+/* { dg-options "-Wall -fcf-protection -mmanual-endbr -fcf-check-attribute=no" } */
+
+extern void foo(void);
+void __attribute__((cf_check)) foo(void) /* { dg-error "implied 'nocf_check' attribute" } */
+{
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr102953-6.c b/gcc/testsuite/gcc.target/i386/pr102953-6.c
new file mode 100644
index 00000000000..ee0c66f94cb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr102953-6.c
@@ -0,0 +1,8 @@ 
+/* { dg-do compile } */
+/* { dg-options "-Wall -fcf-protection -mmanual-endbr -fcf-check-attribute=no" } */
+
+static int __attribute__((cf_check)) foo(char a[], int b)
+{
+  return 0;
+}
+int (*ptr)(char[], int) = foo;