lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Mon, 7 Aug 2023 09:13:42 +0200
From:   Jo Van Bulck <jo.vanbulck@...kuleuven.be>
To:     "Huang, Kai" <kai.huang@...el.com>,
        "linux-sgx@...r.kernel.org" <linux-sgx@...r.kernel.org>,
        "jarkko@...nel.org" <jarkko@...nel.org>,
        "linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>
Cc:     "dave.hansen@...ux.intel.com" <dave.hansen@...ux.intel.com>
Subject: Re: [PATCH 2/5] selftests/sgx: Fix function pointer relocation in
 test enclave.

On 03.08.23 05:58, Huang, Kai wrote:
> Putting aside whether we should consider building the selftests using "-Os", it
> would be helpful to explain how can the "-Os" break the existing code, so that
> we can review why this fix is reasonable.  Perhaps it's obvious to others but
> it's not obvious to me what can go wrong here.

I dug deeper into this and the real problem here is that the enclave ELF 
is linked with -static but also needs to be relocatable, which, in my 
understanding, is not what -static is meant for (i.e., the man pages 
says -static overrides -pie). Particularly, with -static I noticed that 
global variables are hard-coded assuming the ELF is loaded at base 
address zero.

When reading more on this, it seems like the proper thing to do for 
static and relocatable binaries is to compile with -static-pie, which is 
added since gcc 8 [1] (and similarly supported by clang).

As a case in point, to hopefully make this clearer, consider the 
following C function:

extern uint8_t __enclave_base;
void *get_base(void) {
	return &__enclave_base;
}

Compiling with -static and -fPIC hard codes the __enclave_base symbol to 
zero (the start of the ELF enclave image):

00000000000023f4 <get_base>:
     23f4:	f3 0f 1e fa          	endbr64
     23f8:	55                   	push   %rbp
     23f9:	48 89 e5             	mov    %rsp,%rbp
     23fc:	48 c7 c0 00 00 00 00 	mov    $0x0,%rax
     2403:	5d                   	pop    %rbp
     2404:	c3                   	ret

Compiling with -static-pie and -fPIE properly emits a RIP-relative address:

00000000000023f4 <get_base>:
     23f4:	f3 0f 1e fa          	endbr64
     23f8:	55                   	push   %rbp
     23f9:	48 89 e5             	mov    %rsp,%rbp
     23fc:	48 8d 05 fd db ff ff 	lea    -0x2403(%rip),%rax        # 0 
<__enclave_base>
     2403:	5d                   	pop    %rbp
     2404:	c3                   	ret

Now, the fact that it currently *happens* to work is a mere coincidence 
of how the local encl_op_array initialization is compiled without 
optimizations with -static -fPIC:

00000000000023f4 <encl_body>:
     /* snipped */
     2408:       48 8d 05 ec fe ff ff    lea    -0x114(%rip),%rax 
# 22fb <do_encl_op_put_to_buf>
     240f:       48 89 45 b0             mov    %rax,-0x50(%rbp)
     2413:       48 8d 05 18 ff ff ff    lea    -0xe8(%rip),%rax 
# 2332 <do_encl_op_get_from_buf>
     241a:       48 89 45 b8             mov    %rax,-0x48(%rbp)
     241e:       48 8d 05 44 ff ff ff    lea    -0xbc(%rip),%rax 
# 2369 <do_encl_op_put_to_addr>
     /* snipped */

When compiling with optimizations with -static -fPIC -Os, encl_op_array 
is instead initialized with a prepared copy from .data:

00000000000021b5 <encl_body>:
     /* snipped */
     21bc:       48 8d 35 3d 2e 00 00    lea    0x2e3d(%rip),%rsi 
# 5000 <encl_buffer+0x2000>
     21c3:       48 8d 7c 24 b8          lea    -0x48(%rsp),%rdi
     21c8:       b9 10 00 00 00          mov    $0x10,%ecx
     21cd:       f3 a5                   rep movsl %ds:(%rsi),%es:(%rdi)
     /* snipped */

Thus, in this optimized code, encl_op_array will have function pointers 
that are *not* relocated. The compilation assumes the -static binary has 
base address zero and is not relocatable:

$ readelf -r test_encl.elf

There are no relocations in this file.

When compiling with -static-pie -PIE -Os, the same code is emitted *but* 
the binary is relocatable:

$ readelf -r test_encl.elf

Relocation section '.rela.dyn' at offset 0x4000 contains 12 entries:
   Offset          Info           Type           Sym. Value    Sym. Name 
+ Addend
# tcs1.ossa
000000000010  000000000008 R_X86_64_RELATIVE                    7000
# tcs1.oentry
000000000020  000000000008 R_X86_64_RELATIVE                    21e3
# tcs2.ossa
000000001010  000000000008 R_X86_64_RELATIVE                    8000
# tcs2.oentry
000000001020  000000000008 R_X86_64_RELATIVE                    21e3
# encl_op_array
000000006000  000000000008 R_X86_64_RELATIVE                    2098
000000006008  000000000008 R_X86_64_RELATIVE                    20ae
000000006010  000000000008 R_X86_64_RELATIVE                    20c4
000000006018  000000000008 R_X86_64_RELATIVE                    20d7
000000006020  000000000008 R_X86_64_RELATIVE                    20ea
000000006028  000000000008 R_X86_64_RELATIVE                    203e
000000006030  000000000008 R_X86_64_RELATIVE                    2000
000000006038  000000000008 R_X86_64_RELATIVE                    20ef

Apparently, for static-pie binaries, glibc includes a 
_dl_relocate_static_pie routine that will perform the relocations as 
part of the startup [2,3]. Since the enclave loading process is 
different and glibc is not included, we cannot rely on these relocations 
to be performed and we need to do them manually. Note: the first 4 
symbols in the relocation table above are from the TCS initialization in 
test_encl_bootstrap.S and should *not* be relocated (as these are 
relative to enclave base as per SGX spec).

Bottom line: the way I see it, the enclave should either ensure no 
relocations are needed, or perform the relocations manually where 
needed, or include a _dl_relocate_static_pie equivalent that parses the 
.rela.dyn ELF section and patches all relocations (minus the TCS 
symbols). Since the latter (most general) approach is likely going to 
make the selftest enclave unnecessarily complex by including ELF parsing 
etc, I propose to simply relocate the function-pointer table manually 
(which is indeed the only place that needs relocations).

I will include code to properly compile the selftest enclave with 
-static-pie as per above and relocate the function-pointer table in the 
next patch revision.

[1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81498
[2] https://stackoverflow.com/a/62587156
[3] 
https://sourceware.org/git/?p=glibc.git;a=blob;f=elf/dl-reloc-static-pie.c;h=382482fa739f10934aeb916650c65a4db231c481;hb=HEAD

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ