From cd1648bcd8e057b7ee596fbe4641dec73eca155d Mon Sep 17 00:00:00 2001 From: Dmitrii Kuvaiskii Date: Sat, 18 Apr 2020 02:18:53 +0000 Subject: [PATCH] [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). --- Pal/src/db_rtld.c | 20 +++++++------------- Pal/src/pal_internal.h | 1 + 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/Pal/src/db_rtld.c b/Pal/src/db_rtld.c index faaa588d..eef670ea 100644 --- a/Pal/src/db_rtld.c +++ b/Pal/src/db_rtld.c @@ -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); diff --git a/Pal/src/pal_internal.h b/Pal/src/pal_internal.h index 2814ecca..cc49f90d 100644 --- a/Pal/src/pal_internal.h +++ b/Pal/src/pal_internal.h @@ -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);