From 636a2ecfbc00e1403fba0d8baf1c6c10425fde6e Mon Sep 17 00:00:00 2001 From: Evgenii Stepanov Date: Fri, 20 Jan 2017 13:47:04 -0800 Subject: [PATCH] [cfi] Handle large libraries correctly. Fallback to unchecked if the shadow offset overflows int16_t. This may happen when a library's data segment is larger than 256MB. Also updated some comments. Bug: 22033465 Test: bionic device tests Change-Id: I8eef42f75099f24aed566499ff1731a0bbf01ff3 --- linker/linker_cfi.cpp | 12 ++++++++++-- linker/linker_cfi.h | 15 +++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/linker/linker_cfi.cpp b/linker/linker_cfi.cpp index f788c168c..e9cdab64a 100644 --- a/linker/linker_cfi.cpp +++ b/linker/linker_cfi.cpp @@ -96,14 +96,21 @@ void CFIShadowWriter::Add(uintptr_t begin, uintptr_t end, uintptr_t cfi_check) { uint16_t* shadow_end = MemToShadow(end - 1) + 1; ShadowWrite sw(shadow_begin, shadow_end); - uint16_t sv = ((begin + kShadowAlign - cfi_check) >> kCfiCheckGranularity) + kRegularShadowMin; + uint16_t sv_begin = ((begin + kShadowAlign - cfi_check) >> kCfiCheckGranularity) + kRegularShadowMin; // With each step of the loop below, __cfi_check address computation base is increased by // 2**ShadowGranularity. // To compensate for that, each next shadow value must be increased by 2**ShadowGranularity / // 2**CfiCheckGranularity. uint16_t sv_step = 1 << (kShadowGranularity - kCfiCheckGranularity); + uint16_t sv = sv_begin; for (uint16_t& s : sw) { + if (sv < sv_begin) { + // If shadow value wraps around, also fall back to unchecked. This means the binary is too + // large. FIXME: consider using a (slow) resolution function instead. + s = kUncheckedShadow; + continue; + } // If there is something there already, fall back to unchecked. This may happen in rare cases // with MAP_FIXED libraries. FIXME: consider using a (slow) resolution function instead. s = (s == kInvalidShadow) ? sv : kUncheckedShadow; @@ -191,6 +198,7 @@ bool CFIShadowWriter::NotifyLibDl(soinfo* solist, uintptr_t p) { bool CFIShadowWriter::MaybeInit(soinfo* new_si, soinfo* solist) { CHECK(initial_link_done); + CHECK(shadow_start == nullptr); // Check if CFI shadow must be initialized at this time. bool found = false; if (new_si == nullptr) { @@ -250,7 +258,7 @@ void CFIShadowWriter::BeforeUnload(soinfo* si) { } bool CFIShadowWriter::InitialLinkDone(soinfo* solist) { - CHECK(!initial_link_done) + CHECK(!initial_link_done); initial_link_done = true; return MaybeInit(nullptr, solist); } diff --git a/linker/linker_cfi.h b/linker/linker_cfi.h index df7642193..0e05f3714 100644 --- a/linker/linker_cfi.h +++ b/linker/linker_cfi.h @@ -29,7 +29,7 @@ // See documentation in http://clang.llvm.org/docs/ControlFlowIntegrityDesign.html#shared-library-support. // // Shadow is mapped and initialized lazily as soon as the first CFI-enabled DSO is loaded. -// It is updated after a set of libraries is loaded (but before any constructors are ran), and +// It is updated after any library is loaded (but before any constructors are ran), and // before any library is unloaded. class CFIShadowWriter : private CFIShadow { // Returns pointer to the shadow element for an address. @@ -56,17 +56,15 @@ class CFIShadowWriter : private CFIShadow { uintptr_t MapShadow(); // Initialize CFI shadow and update its contents for everything in solist if any loaded library is - // CFI-enabled. If soinfos != nullptr, do an incremental check by looking only at the libraries in - // soinfos[]; otherwise look at the entire solist. - // - // Returns false if the shadow is already initialized. It is the caller's responsibility to update - // the shadow for the new libraries in that case. - // Otherwise, returns true and leaves the shadow either up-to-date or uninitialized. + // CFI-enabled. If new_si != nullptr, do an incremental check by looking only at new_si; otherwise + // look at the entire solist. bool MaybeInit(soinfo *new_si, soinfo *solist); // Set a human readable name for the entire shadow region. void FixupVmaName(); + // Pass the pointer to the mapped shadow region to libdl. Must only be called once. + // Flips shadow_start to a non-nullptr value. bool NotifyLibDl(soinfo *solist, uintptr_t p); // Pointer to the shadow start address. @@ -75,7 +73,7 @@ class CFIShadowWriter : private CFIShadow { bool initial_link_done; public: - // Update shadow after loading a set of DSOs. + // Update shadow after loading a DSO. // This function will initialize the shadow if it sees a CFI-enabled DSO for the first time. // In that case it will retroactively update shadow for all previously loaded DSOs. "solist" is a // pointer to the global list. @@ -85,6 +83,7 @@ class CFIShadowWriter : private CFIShadow { // Update shadow before unloading a DSO. void BeforeUnload(soinfo* si); + // This is called as soon as the initial set of libraries is linked. bool InitialLinkDone(soinfo *solist); // Handle failure to locate __cfi_check for a target address. -- 2.11.0