[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
diff --git a/linker/linker_cfi.cpp b/linker/linker_cfi.cpp
index f788c16..e9cdab6 100644
--- a/linker/linker_cfi.cpp
+++ b/linker/linker_cfi.cpp
@@ -96,14 +96,21 @@
   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::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 @@
 }
 
 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 df76421..0e05f37 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 @@
   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 @@
   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 @@
   // 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.