Bug 85620

Summary: Missing ENDBR after swapcontext
Product: gcc Reporter: H.J. Lu <hjl.tools>
Component: middle-endAssignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED FIXED    
Severity: normal CC: aoliva, fw
Priority: P3    
Version: 8.0   
Target Milestone: 13.0   
Host: Target: x86_64-*-*, i?86-*-*
Build: Known to work:
Known to fail: Last reconfirmed: 2018-05-02 00:00:00
Bug Depends on:    
Bug Blocks: 81652    
Attachments: A patch

Description H.J. Lu 2018-05-02 20:37:09 UTC
special_function_p has

      /* ECF_RETURNS_TWICE is safe even for -ffreestanding.  */
      if (! strcmp (tname, "setjmp")
          || ! strcmp (tname, "sigsetjmp")
          || ! strcmp (name, "savectx")
          || ! strcmp (name, "vfork")
          || ! strcmp (name, "getcontext"))
                             ^^^^^^^^^^^^ getcontext never returns twice.
Instead swapcontext may return twice.
        flags |= ECF_RETURNS_TWICE;
Comment 1 Andrew Pinski 2018-05-02 20:42:36 UTC
getcontext does return twice.  Swapcontext only returns once.

If you get the current context, and then latter on swap the context, you just returned to the place where getcontext is called.  you do setcontext (or swapcontext again), you get the swapcontext context.  That is it only ever returns once.
Comment 2 Andrew Pinski 2018-05-02 20:44:04 UTC
http://pubs.opengroup.org/onlinepubs/007904975/functions/getcontext.html

If the ucp argument was created with getcontext(), program execution continues as if the corresponding call of getcontext() had just returned. 

That means it does return twice.
Comment 3 H.J. Lu 2018-05-02 20:47:45 UTC
Technically, swapcontext only returns once, but not when it is called.
In that sense, it returns twice.
Comment 4 Andrew Pinski 2018-05-02 21:02:29 UTC
(In reply to H.J. Lu from comment #3)
> Technically, swapcontext only returns once, but not when it is called.
> In that sense, it returns twice.

Huh?  For the purpose of ECF_RETURNS_TWICE, swapcontext does not need to be marked as such.  ECF_RETURNS_TWICE is only needed for CFG reasons.
Comment 5 H.J. Lu 2018-05-02 22:30:25 UTC
[hjl@gnu-skx-1 ucontext-1]$ cat y.i
struct ucontext;
typedef struct ucontext ucontext_t;

extern int swapcontext (ucontext_t *__restrict __oucp,
                        const ucontext_t *__restrict __ucp);

extern int res;

void
foo (ucontext_t *oucp, ucontext_t *ucp)
{
  res = swapcontext (oucp, ucp);
}
[hjl@gnu-skx-1 ucontext-1]$ make y.s
/export/build/gnu/gcc-8-test/build-x86_64-linux/gcc/xgcc -B/export/build/gnu/gcc-8-test/build-x86_64-linux/gcc/ -O2  -fcf-protection -S y.i
[hjl@gnu-skx-1 ucontext-1]$ cat y.s
	.file	"y.i"
	.text
	.p2align 4,,15
	.globl	foo
	.type	foo, @function
foo:
.LFB0:
	.cfi_startproc
	endbr64
	subq	$8, %rsp
	.cfi_def_cfa_offset 16
	call	swapcontext  <<<<<<< This may return via indirect branch.
<<<<<<<<<<< Need ENDBR here.
	movl	%eax, res(%rip)
	addq	$8, %rsp
	.cfi_def_cfa_offset 8
	ret
	.cfi_endproc
.LFE0:
	.size	foo, .-foo
	.ident	"GCC: (GNU) 8.1.1 20180502"
	.section	.note.GNU-stack,"",@progbits
	.section	.note.gnu.property,"a"
	.align 8
	.long	 1f - 0f
	.long	 4f - 1f
	.long	 5
0:
	.string	 "GNU"
1:
	.align 8
	.long	 0xc0000002
	.long	 3f - 2f
2:
	.long	 0x3
3:
	.align 8
4:
[hjl@gnu-skx-1 ucontext-1]$
Comment 6 H.J. Lu 2018-05-17 13:46:32 UTC
It has been fixed in glibc.
Comment 7 H.J. Lu 2018-06-07 23:04:56 UTC
We want to generate ENDBR after swapcontext. swapcontext didn't return the
first time.  It was just kind of "suspended".  Mark it return twice will
unnecessarily disable compiler optimization.
Comment 8 hjl@gcc.gnu.org 2018-07-04 03:02:05 UTC
Author: hjl
Date: Wed Jul  4 03:01:33 2018
New Revision: 262370

URL: https://gcc.gnu.org/viewcvs?rev=262370&root=gcc&view=rev
Log:
i386: Add indirect_return function attribute

On x86, swapcontext may return via indirect branch when shadow stack
is enabled.  To support code instrumentation of control-flow transfers
with -fcf-protection, add indirect_return function attribute to inform
compiler that a function may return via indirect branch.

Note: Unlike setjmp, swapcontext only returns once.  Mark it return
twice will unnecessarily disable compiler optimization as shown in
the testcase here.

gcc/

	PR target/85620
	* config/i386/i386.c (rest_of_insert_endbranch): Also generate
	ENDBRANCH for non-tail call which may return via indirect branch.
	* doc/extend.texi: Document indirect_return attribute.

gcc/testsuite/

	PR target/85620
	* gcc.target/i386/pr85620-1.c: New test.
	* gcc.target/i386/pr85620-2.c: Likewise.
	* gcc.target/i386/pr85620-3.c: Likewise.
	* gcc.target/i386/pr85620-4.c: Likewise.

Added:
    trunk/gcc/testsuite/gcc.target/i386/pr85620-1.c
    trunk/gcc/testsuite/gcc.target/i386/pr85620-2.c
    trunk/gcc/testsuite/gcc.target/i386/pr85620-3.c
    trunk/gcc/testsuite/gcc.target/i386/pr85620-4.c
Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/config/i386/i386.c
    trunk/gcc/doc/extend.texi
    trunk/gcc/testsuite/ChangeLog
Comment 9 H.J. Lu 2018-07-04 13:46:56 UTC
Fixed for GCC 9.
Comment 10 Alexandre Oliva 2022-07-14 13:08:04 UTC
ISTM that if you have to insert an endbr over indirect_return, a call should only be eligible for sibcall if the caller is also marked with indirect_return, otherwise the callee will indirectly return directly to an upstack caller that may not have an endbr after the call, no?
Comment 11 H.J. Lu 2022-07-14 16:10:23 UTC
Created attachment 53299 [details]
A patch
Comment 12 GCC Commits 2022-07-15 23:58:46 UTC
The master branch has been updated by H.J. Lu <hjl@gcc.gnu.org>:

https://gcc.gnu.org/g:2582080f19e8fe9c1204dfb6fecf744311f00777

commit r13-1717-g2582080f19e8fe9c1204dfb6fecf744311f00777
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Thu Jul 14 10:31:21 2022 -0700

    x86: Disable sibcall if indirect_return attribute doesn't match
    
    When shadow stack is enabled, function with indirect_return attribute
    may return via indirect jump.  In this case, we need to disable sibcall
    if caller doesn't have indirect_return attribute and indirect branch
    tracking is enabled since compiler won't generate ENDBR when calling the
    caller.
    
    gcc/
    
            PR target/85620
            * config/i386/i386.cc (ix86_function_ok_for_sibcall): Return
            false if callee has indirect_return attribute and caller
            doesn't.
    
    gcc/testsuite/
    
            PR target/85620
            * gcc.target/i386/pr85620-2.c: Updated.
            * gcc.target/i386/pr85620-5.c: New test.
            * gcc.target/i386/pr85620-6.c: Likewise.
            * gcc.target/i386/pr85620-7.c: Likewise.
Comment 13 Andrew Pinski 2024-04-03 19:58:36 UTC
.