Avoid using SafeCopy on userfaultfd compacted spaces

Userfaultfd doesn't allow faults generating from kernel-space for
unprivileged processes. Therefore, avoid using SafeCopy to fetch
class in fault-handler.

Also add a run-test to cause null-pointer exceptions which exercises
fault-handler.

Bug: 160737021
Test: ART_USE_READ_BARRIER=false art/test/testrunner/testrunner.py -t 2045-uffd-kernelfault
Change-Id: If54bb01d441fab5489289e0ec195896700fac662
(cherry picked from commit f716e21fb87e4fd1c875997cf11882352b9fbab5)
Merged-In: If54bb01d441fab5489289e0ec195896700fac662
diff --git a/runtime/base/gc_visited_arena_pool.h b/runtime/base/gc_visited_arena_pool.h
index 57b742d..4f176ef 100644
--- a/runtime/base/gc_visited_arena_pool.h
+++ b/runtime/base/gc_visited_arena_pool.h
@@ -108,6 +108,16 @@
   void LockReclaimMemory() override {}
   void TrimMaps() override {}
 
+  bool Contains(void* ptr) {
+    std::lock_guard<std::mutex> lock(lock_);
+    for (auto& map : maps_) {
+      if (map.HasAddress(ptr)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   template <typename PageVisitor>
   void VisitRoots(PageVisitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_) {
     std::lock_guard<std::mutex> lock(lock_);
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index f8bd213..c6940fa 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -25,6 +25,7 @@
 #include "base/safe_copy.h"
 #include "base/stl_util.h"
 #include "dex/dex_file_types.h"
+#include "gc/space/bump_pointer_space.h"
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
 #include "mirror/class.h"
@@ -62,9 +63,20 @@
 
 static mirror::Class* SafeGetDeclaringClass(ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (gUseUserfaultfd) {
+    // Avoid SafeCopy on userfaultfd updated memory ranges as kernel-space
+    // userfaults are not allowed, which can otherwise happen if compaction is
+    // simultaneously going on.
+    Runtime* runtime = Runtime::Current();
+    DCHECK_NE(runtime->GetHeap()->MarkCompactCollector(), nullptr);
+    GcVisitedArenaPool* pool = static_cast<GcVisitedArenaPool*>(runtime->GetLinearAllocArenaPool());
+    if (pool->Contains(method)) {
+      return method->GetDeclaringClassUnchecked<kWithoutReadBarrier>().Ptr();
+    }
+  }
+
   char* method_declaring_class =
       reinterpret_cast<char*>(method) + ArtMethod::DeclaringClassOffset().SizeValue();
-
   // ArtMethod::declaring_class_ is a GcRoot<mirror::Class>.
   // Read it out into as a CompressedReference directly for simplicity's sake.
   mirror::CompressedReference<mirror::Class> cls;
@@ -84,8 +96,18 @@
 }
 
 static mirror::Class* SafeGetClass(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
-  char* obj_cls = reinterpret_cast<char*>(obj) + mirror::Object::ClassOffset().SizeValue();
+  if (gUseUserfaultfd) {
+    // Avoid SafeCopy on userfaultfd updated memory ranges as kernel-space
+    // userfaults are not allowed, which can otherwise happen if compaction is
+    // simultaneously going on.
+    gc::Heap* heap = Runtime::Current()->GetHeap();
+    DCHECK_NE(heap->MarkCompactCollector(), nullptr);
+    if (heap->GetBumpPointerSpace()->Contains(obj)) {
+      return obj->GetClass();
+    }
+  }
 
+  char* obj_cls = reinterpret_cast<char*>(obj) + mirror::Object::ClassOffset().SizeValue();
   mirror::HeapReference<mirror::Class> cls;
   ssize_t rc = SafeCopy(&cls, obj_cls, sizeof(cls));
   CHECK_NE(-1, rc);
diff --git a/test/2045-uffd-kernelfault/expected-stderr.txt b/test/2045-uffd-kernelfault/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/2045-uffd-kernelfault/expected-stderr.txt
diff --git a/test/2045-uffd-kernelfault/expected-stdout.txt b/test/2045-uffd-kernelfault/expected-stdout.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/test/2045-uffd-kernelfault/expected-stdout.txt
@@ -0,0 +1 @@
+Done
diff --git a/test/2045-uffd-kernelfault/info.txt b/test/2045-uffd-kernelfault/info.txt
new file mode 100644
index 0000000..c0967d5
--- /dev/null
+++ b/test/2045-uffd-kernelfault/info.txt
@@ -0,0 +1,2 @@
+Test that fault-handler doesn't cause userfaultfd kernel-faults, which are not
+allowed in unpriviledged processes.
diff --git a/test/2045-uffd-kernelfault/run.py b/test/2045-uffd-kernelfault/run.py
new file mode 100644
index 0000000..5b262bb
--- /dev/null
+++ b/test/2045-uffd-kernelfault/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+  # Limit the Java heap to 20MiB to force more GCs.
+  ctx.default_run(args, runtime_option=["-Xmx20m"])
diff --git a/test/2045-uffd-kernelfault/src/Main.java b/test/2045-uffd-kernelfault/src/Main.java
new file mode 100644
index 0000000..c5fac30
--- /dev/null
+++ b/test/2045-uffd-kernelfault/src/Main.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+    // TODO: Reduce it once the userfaultfd GC is tested long enough.
+    static final long DURATION_IN_MILLIS = 10_000;
+
+    static public Object obj = null;
+    static public Object[] array = new Object[4096];
+
+    public static void main(String args[]) {
+      final long start_time = System.currentTimeMillis();
+      long end_time = start_time;
+      int idx = 0;
+      while (end_time - start_time < DURATION_IN_MILLIS) {
+        try {
+          // Trigger a null-pointer exception
+          System.out.println(obj.toString());
+        } catch (NullPointerException npe) {
+          // Small enough to be not allocated in large-object space and hence keep the compaction
+          // phase longer, while keeping marking phase shorter (as there aren't any references to
+          // chase).
+          array[idx++] = new byte[3000];
+          idx %= array.length;
+        }
+        end_time = System.currentTimeMillis();
+      }
+      System.out.println("Done");
+    }
+}