Merge "Add basic support for object pointer poisoning"
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 678abe0..ecaa4c2 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2378,6 +2378,7 @@
   DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
   DCHECK(self != nullptr);
   self->AssertNoPendingException();
+  self->PoisonObjectPointers();
   if (descriptor[1] == '\0') {
     // only the descriptors of primitive types should be 1 character long, also avoid class lookup
     // for primitive classes that aren't backed by dex files.
diff --git a/runtime/mirror/obj_ptr.h b/runtime/mirror/obj_ptr.h
new file mode 100644
index 0000000..10378e8
--- /dev/null
+++ b/runtime/mirror/obj_ptr.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef ART_RUNTIME_MIRROR_OBJ_PTR_H_
+#define ART_RUNTIME_MIRROR_OBJ_PTR_H_
+
+#include "base/mutex.h"  // For Locks::mutator_lock_.
+#include "globals.h"
+#include "mirror/object_reference.h"
+#include "utils.h"
+
+namespace art {
+namespace mirror {
+
+class Object;
+
+// Value type representing a pointer to a mirror::Object of type MirrorType
+// Pass kPoison as a template boolean for testing in non-debug builds.
+// Note that the functions are not 100% thread safe and may have spurious positive check passes in
+// these cases.
+template<class MirrorType, bool kPoison = kIsDebugBuild>
+class ObjPtr {
+  static constexpr size_t kCookieShift =
+      sizeof(mirror::HeapReference<mirror::Object>) * kBitsPerByte - kObjectAlignmentShift;
+  static constexpr size_t kCookieBits = sizeof(uintptr_t) * kBitsPerByte - kCookieShift;
+  static constexpr uintptr_t kCookieMask = (static_cast<uintptr_t>(1u) << kCookieBits) - 1;
+
+  static_assert(kCookieBits >= kObjectAlignmentShift,
+                "must have a least kObjectAlignmentShift bits");
+
+ public:
+  ALWAYS_INLINE ObjPtr() REQUIRES_SHARED(Locks::mutator_lock_) : reference_(0u) {}
+
+  ALWAYS_INLINE explicit ObjPtr(MirrorType* ptr) REQUIRES_SHARED(Locks::mutator_lock_)
+      : reference_(Encode(ptr)) {}
+
+  ALWAYS_INLINE explicit ObjPtr(const ObjPtr& other) REQUIRES_SHARED(Locks::mutator_lock_)
+      = default;
+
+  ALWAYS_INLINE ObjPtr& operator=(const ObjPtr& other) {
+    reference_ = other.reference_;
+    return *this;
+  }
+
+  ALWAYS_INLINE ObjPtr& operator=(MirrorType* ptr) REQUIRES_SHARED(Locks::mutator_lock_) {
+    Assign(ptr);
+    return *this;
+  }
+
+  ALWAYS_INLINE void Assign(MirrorType* ptr) REQUIRES_SHARED(Locks::mutator_lock_) {
+    reference_ = Encode(ptr);
+  }
+
+  ALWAYS_INLINE MirrorType* operator->() const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return Get();
+  }
+
+  ALWAYS_INLINE MirrorType* Get() const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return Decode();
+  }
+
+  ALWAYS_INLINE bool IsNull() const {
+    return reference_ == 0;
+  }
+
+  ALWAYS_INLINE bool IsValid() const REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (!kPoison || IsNull()) {
+      return true;
+    }
+    return GetCookie() == TrimCookie(Thread::Current()->GetPoisonObjectCookie());
+  }
+
+  ALWAYS_INLINE void AssertValid() const REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (kPoison) {
+      CHECK(IsValid()) << "Stale object pointer, expected cookie "
+          << TrimCookie(Thread::Current()->GetPoisonObjectCookie()) << " but got " << GetCookie();
+    }
+  }
+
+  ALWAYS_INLINE bool operator==(const ObjPtr& ptr) const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return Decode() == ptr.Decode();
+  }
+
+  ALWAYS_INLINE bool operator==(const MirrorType* ptr) const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return Decode() == ptr;
+  }
+
+  ALWAYS_INLINE bool operator==(std::nullptr_t) const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return IsNull();
+  }
+
+  ALWAYS_INLINE bool operator!=(const ObjPtr& ptr) const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return Decode() != ptr.Decode();
+  }
+
+  ALWAYS_INLINE bool operator!=(const MirrorType* ptr) const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return Decode() != ptr;
+  }
+
+  ALWAYS_INLINE bool operator!=(std::nullptr_t) const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return !IsNull();
+  }
+
+ private:
+  // Trim off high bits of thread local cookie.
+  ALWAYS_INLINE static uintptr_t TrimCookie(uintptr_t cookie) {
+    return cookie & kCookieMask;
+  }
+
+  ALWAYS_INLINE uintptr_t GetCookie() const {
+    return reference_ >> kCookieShift;
+  }
+
+  ALWAYS_INLINE static uintptr_t Encode(MirrorType* ptr) REQUIRES_SHARED(Locks::mutator_lock_) {
+    uintptr_t ref = reinterpret_cast<uintptr_t>(ptr);
+    if (kPoison && ref != 0) {
+      DCHECK_LE(ref, 0xFFFFFFFFU);
+      ref >>= kObjectAlignmentShift;
+      // Put cookie in high bits.
+      Thread* self = Thread::Current();
+      DCHECK(self != nullptr);
+      ref |= self->GetPoisonObjectCookie() << kCookieShift;
+    }
+    return ref;
+  }
+
+  // Decode makes sure that the object pointer is valid.
+  ALWAYS_INLINE MirrorType* Decode() const REQUIRES_SHARED(Locks::mutator_lock_) {
+    AssertValid();
+    if (kPoison) {
+      return reinterpret_cast<MirrorType*>(
+          static_cast<uintptr_t>(static_cast<uint32_t>(reference_ << kObjectAlignmentShift)));
+    } else {
+      return reinterpret_cast<MirrorType*>(reference_);
+    }
+  }
+
+  // The encoded reference and cookie.
+  uintptr_t reference_;
+};
+
+
+}  // namespace mirror
+}  // namespace art
+
+#endif  // ART_RUNTIME_MIRROR_OBJ_PTR_H_
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index afd6115..f4ecfb5 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -35,6 +35,7 @@
 #include "gc/heap.h"
 #include "handle_scope-inl.h"
 #include "iftable-inl.h"
+#include "obj_ptr.h"
 #include "object-inl.h"
 #include "object_array-inl.h"
 #include "scoped_thread_state_change.h"
@@ -738,5 +739,62 @@
   EXPECT_NE(hash_code, 0);
 }
 
+TEST_F(ObjectTest, ObjectPointer) {
+  ScopedObjectAccess soa(Thread::Current());
+  jobject jclass_loader = LoadDex("XandY");
+  StackHandleScope<2> hs(soa.Self());
+  ObjPtr<mirror::Object, /*kPoison*/ true> null_ptr;
+  EXPECT_TRUE(null_ptr.IsNull());
+  EXPECT_TRUE(null_ptr.IsValid());
+  EXPECT_TRUE(null_ptr.Get() == nullptr);
+  EXPECT_TRUE(null_ptr == nullptr);
+  EXPECT_TRUE(null_ptr == null_ptr);
+  EXPECT_FALSE(null_ptr != null_ptr);
+  EXPECT_FALSE(null_ptr != nullptr);
+  null_ptr.AssertValid();
+  Handle<ClassLoader> class_loader(hs.NewHandle(soa.Decode<ClassLoader*>(jclass_loader)));
+  Handle<mirror::Class> h_X(
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), "LX;", class_loader)));
+  ObjPtr<Class, /*kPoison*/ true> X(h_X.Get());
+  EXPECT_TRUE(!X.IsNull());
+  EXPECT_TRUE(X.IsValid());
+  EXPECT_TRUE(X.Get() != nullptr);
+  EXPECT_EQ(h_X.Get(), X.Get());
+  // FindClass may cause thread suspension, it should invalidate X.
+  ObjPtr<Class, /*kPoison*/ true> Y(class_linker_->FindClass(soa.Self(), "LY;", class_loader));
+  EXPECT_TRUE(!Y.IsNull());
+  EXPECT_TRUE(Y.IsValid());
+  EXPECT_TRUE(Y.Get() != nullptr);
+
+  // Should IsNull be safe to call on null ObjPtr? I'll allow it for now.
+  EXPECT_TRUE(!X.IsNull());
+  EXPECT_TRUE(!X.IsValid());
+  // Make X valid again by copying out of handle.
+  X.Assign(h_X.Get());
+  EXPECT_TRUE(!X.IsNull());
+  EXPECT_TRUE(X.IsValid());
+  EXPECT_EQ(h_X.Get(), X.Get());
+
+  // Allow thread suspension to invalidate Y.
+  soa.Self()->AllowThreadSuspension();
+  EXPECT_TRUE(!Y.IsNull());
+  EXPECT_TRUE(!Y.IsValid());
+
+  // Test unpoisoned.
+  ObjPtr<mirror::Object, /*kPoison*/ false> unpoisoned;
+  EXPECT_TRUE(unpoisoned.IsNull());
+  EXPECT_TRUE(unpoisoned.IsValid());
+  EXPECT_TRUE(unpoisoned.Get() == nullptr);
+  EXPECT_TRUE(unpoisoned == nullptr);
+  EXPECT_TRUE(unpoisoned == unpoisoned);
+  EXPECT_FALSE(unpoisoned != unpoisoned);
+  EXPECT_FALSE(unpoisoned != nullptr);
+
+  unpoisoned = h_X.Get();
+  EXPECT_FALSE(unpoisoned.IsNull());
+  EXPECT_TRUE(unpoisoned == h_X.Get());
+  EXPECT_EQ(unpoisoned.Get(), h_X.Get());
+}
+
 }  // namespace mirror
 }  // namespace art
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index 216d8a7..298a974 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -59,6 +59,7 @@
   if (UNLIKELY(TestAllFlags())) {
     CheckSuspend();
   }
+  PoisonObjectPointers();
 }
 
 inline void Thread::CheckSuspend() {
diff --git a/runtime/thread.h b/runtime/thread.h
index 016c2bc..fb6bde6 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -471,6 +471,14 @@
   }
   void Notify() REQUIRES(!*wait_mutex_);
 
+  ALWAYS_INLINE void PoisonObjectPointers() {
+    ++poison_object_cookie_;
+  }
+
+  ALWAYS_INLINE uintptr_t GetPoisonObjectCookie() const {
+    return poison_object_cookie_;
+  }
+
  private:
   void NotifyLocked(Thread* self) REQUIRES(wait_mutex_);
 
@@ -1528,6 +1536,9 @@
   // Debug disable read barrier count, only is checked for debug builds and only in the runtime.
   uint8_t debug_disallow_read_barrier_ = 0;
 
+  // Note that it is not in the packed struct, may not be accessed for cross compilation.
+  uintptr_t poison_object_cookie_ = 0;
+
   // Pending extra checkpoints if checkpoint_function_ is already used.
   std::list<Closure*> checkpoint_overflow_ GUARDED_BY(Locks::thread_suspend_count_lock_);