[Pal] Enforce that executable is always located at a predefined address

Previously, the PAL layer would put an executable at a first unoccupied
address range if the executable was position-independent (PIE). This
could lead to the same executable being located at different addresses
across forks (Graphene correctly checkpoints shared libraries but
has separate handling for the executable). This is a rare scenario
since the PAL initialization code is typically deterministic across
forks. However, rarely there would be small difference in allocations
which would lead to different base addresses for executable segments
in parent and child processes, and segfaults and data corruptions
happened (this was the case for Nginx on Ubuntu 18.04 with change in
the manifest file processing). This commit simply forces executables
to always be loaded at a predefined address (currently 0x00400000).
This commit is contained in:
Dmitrii Kuvaiskii
2020-04-20 02:59:43 +02:00
committed by Michał Kowalczyk
parent 90585e0d07
commit cd1648bcd8
2 changed files with 8 additions and 13 deletions
+7 -13
View File
@@ -279,22 +279,16 @@ map_elf_object_by_handle (PAL_HANDLE handle, enum object_type type,
#define APPEND_WRITECOPY(prot) ((prot)|PAL_PROT_WRITECOPY)
if (e_type == ET_DYN) {
/* This is a position-independent shared object. We can let the
kernel map it anywhere it likes, but we must have space for all
the segments in their specified positions relative to the first.
So we map the first segment without MAP_FIXED, but with its
extent increased to cover all the segments. Then we remove
access from excess portion, and there is known sufficient space
there to remap from the later segments.
/* This is a position-independent shared object. Graphene allows
* libraries to be mapped anywhere in address space, but the
* executable must be mapped at the exact address. This is because
* Graphene copies libraries during fork but does not copy executable.
* We must enforce that executable segments are located at the same
* addresses across forks: simply use a predefined base address. */
void* mapaddr = type == OBJECT_EXEC ? DEFAULT_OBJECT_EXEC_ADDR : NULL;
As a refinement, sometimes we have an address that we would
prefer to map such objects at; but this is only a preference,
the OS can do whatever it likes. */
void * mapaddr = NULL;
/* Remember which part of the address space this object uses. */
ret = _DkStreamMap(handle, (void **) &mapaddr,
APPEND_WRITECOPY(c->prot), c->mapoff, maplength);
if (ret < 0) {
print_error("failed to map dynamic segment from shared object",
ret);
+1
View File
@@ -318,6 +318,7 @@ int _DkAttestationQuote(PAL_PTR user_report_data, PAL_NUM user_report_data_size,
} while (0)
/* function and definition for loading binaries */
#define DEFAULT_OBJECT_EXEC_ADDR ((void*)0x00400000)
enum object_type { OBJECT_RTLD, OBJECT_EXEC, OBJECT_PRELOAD, OBJECT_EXTERNAL };
int check_elf_magic (const void* header, size_t len);