loader: Only extend segments if kernel supports page size migration

It has been found that some existing apps have implicit dependencies on
the address ranges in /proc/*/maps. Since segment extension changes the
range of the LOAD segment VMAs some of these apps crash, either by
SIGBUS or in yet unidentified ways.

Only perform the segment extension optimization if the kernel has the
necessary mitigations to ensure app compatibility.

Bug: 330117029
Bug: 327600007
Bug: 330767927
Bug: 328266487
Bug: 329803029
Test: Manual - Launch Free Fire Chaos app
Change-Id: I5b03b22c4a468f6646750a00942cc2d57f43d0de
Signed-off-by: Kalesh Singh <kaleshsingh@google.com>
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index 685ae70..80f1ad9 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -46,6 +46,8 @@
 #include "private/CFIShadow.h" // For kLibraryAlignment
 #include "private/elf_note.h"
 
+#include <android-base/file.h>
+
 static int GetTargetElfMachine() {
 #if defined(__arm__)
   return EM_ARM;
@@ -707,8 +709,28 @@
   return true;
 }
 
+/*
+ * Returns true if the kernel supports page size migration, else false.
+ */
+bool page_size_migration_supported() {
+  static bool pgsize_migration_enabled = []() {
+    std::string enabled;
+    if (!android::base::ReadFileToString("/sys/kernel/mm/pgsize_migration/enabled", &enabled)) {
+      return false;
+    }
+    return enabled.find("1") != std::string::npos;
+  }();
+  return pgsize_migration_enabled;
+}
+
 // Find the ELF note of type NT_ANDROID_TYPE_PAD_SEGMENT and check that the desc value is 1.
 bool ElfReader::ReadPadSegmentNote() {
+  if (!page_size_migration_supported()) {
+    // Don't attempt to read the note, since segment extension isn't
+    // supported; but return true so that loading can continue normally.
+    return true;
+  }
+
   // The ELF can have multiple PT_NOTE's, check them all
   for (size_t i = 0; i < phdr_num_; ++i) {
     const ElfW(Phdr)* phdr = &phdr_table_[i];