Add basic support for object pointer poisoning
ObjPtr is a pointer that checks for heap corruption and is meant
to replace mirror::Object* in places where a mirror::Object* is a
local variable. Whenever there is a possible suspend point, the
current thread's object pointers are all invalidated. This is done
by storing a cookie in the object pointer associated with what thread
created it.
Added test case in object_test.
Example failure:
object_test F 25379 25379 object_pointer.h:70] Check failed:
IsValid() Invalid cookie, expected 0 but got 2
Bug: 31113334
Test: test-art-host-gtest-object_test
Change-Id: I9fa80ccaf2f0448621942935af702a243a3e1ee6
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