https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128409
Bug ID: 128409
Summary: ix86_expand_epilogue tests flag_cf_protection as a
boolean instead of checking CF_RETURN bit
Product: gcc
Version: 16.2.1
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: target
Assignee: unassigned at gcc dot gnu.org
Reporter: ubizjak at gmail dot com
Target Milestone: ---
flag_cf_protection is a bitmask (enum cf_protection_level in flag-types.h), not
a boolean. Besides the feature bits CF_BRANCH and CF_RETURN, it can carry the
bookkeeping bits CF_SET and CF_CHECK.
Some places in the i386 backend still use it "naked", so any nonzero value is
treated as if the shadow stack (CF_RETURN) were enabled, including values with
only CF_BRANCH, CF_SET or CF_CHECK set.
In gcc/config/i386/i386.cc (ix86_expand_epilogue):
pro_epilogue_adjust_stack (stack_pointer_rtx, sa,
const0_rtx, style,
flag_cf_protection);
and
/* In case of return from EH a simple return cannot be used
as a return address will be compared with a shadow stack
return address. Use indirect jump instead. */
if (style == 2 && flag_cf_protection)
With e.g. -fcf-protection=branch (IBT only, no shadow stack), the EH-return
epilogue still takes the indirect-jump path and passes set_cfa as if the shadow
stack were enabled, even though nothing compares the return address against a
shadow stack.
Testcase:
--cut here--
void
foo (long offset, void *handler)
{
__builtin_eh_return (offset, handler);
}
--cut here--
gcc -O2 -fcf-protection=branch (even -fcf-protection=check):
foo:
.cfi_startproc
endbr64
pushq %rdx
.cfi_def_cfa_offset 16
.cfi_offset 1, -16
movq %rdi, %rcx
pushq %rax
.cfi_def_cfa_offset 24
.cfi_offset 0, -24
movq %rsi, 16(%rsp,%rdi)
movq (%rsp), %rax
movq 8(%rsp), %rdx
leaq 16(%rsp,%rcx), %rsp
.cfi_def_cfa_offset 8
popq %rcx
.cfi_register 16, 2
.cfi_def_cfa_offset 0
jmp *%rcx
.cfi_endproc
Without -fcf-protection=return, a plain ret should be used, as in the no-CET
case. The indirect jump should only be used when CF_RETURN is set.