https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128390
Bug ID: 128390
Summary: -fcf-protection=none does not clear a preceding
-fcf-protection=check (__CET__ stays defined, CET EH
return still used)
Product: gcc
Version: 16.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: driver
Assignee: unassigned at gcc dot gnu.org
Reporter: 220245569 at seu dot edu.cn
CC: 220255623 at seu dot edu.cn, jianhao.xu at seu dot edu.cn
Target Milestone: ---
Target: x86_64-*-* i?86-*-*
Reproducer
----------
$ cat trigger.c
#ifdef __CET__
int cet_macro = __CET__;
#endif
void
eh_ret (long off, void *handler)
{
__builtin_eh_return (off, handler);
}
Compile flag: x86_64-linux-gnu-gcc -O2 -fcf-protection=check
-fcf-protection=none -S trigger.c
# __CET__ after each option sequence (-dM -E)
-fcf-protection=none => (undefined)
-fcf-protection=full -fcf-protection=none => (undefined)
-fcf-protection=check -fcf-protection=none => #define __CET__ 8
# expected undefined
-fcf-protection=check -fcf-protection=none -fcf-protection=branch => #define
__CET__ 9
# eh_ret epilogue, -fcf-protection=check -fcf-protection=none
leaq 16(%rsp,%rcx), %rsp
popq %rcx # CET-style EH return still selected
jmp *%rcx
# eh_ret epilogue, -fcf-protection=none
leaq 16(%rsp,%rcx), %rsp
ret
Root cause
----------
fcf-protection= is an EnumSet option. In gcc/common.opt (2131-2146)
'none' (CF_NONE == 0) is in Set(1) with 'full', while 'check' (CF_CHECK)
is alone in Set(4). decode_cmdline_option only clears the bits of the
selected set (opts-common.cc:866-874), so =none clears CF_BRANCH|CF_RETURN
but keeps CF_CHECK. ix86_option_override_internal then adds CF_SET
(i386-options.cc:3144-3151), ix86_target_macros defines __CET__=8
(i386-c.cc:976-977) and the EH-return epilogue tests flag_cf_protection
(i386.cc:10765). The manual says "The value none turns off
instrumentation" and documents "-fcf-protection=none" as the way to
override a previous option. Distinct from the __CET__ value under
-fcf-protection=check alone.
Suggested Fix
-------------
Make =none reset every set, e.g. handle OPT_fcf_protection_ with value
CF_NONE specially (flag_cf_protection = CF_NONE), or give 'check' the same
Set as 'none' so that the mask covers CF_CHECK.