Fix the System.arraycopy race condition.

Bug: 5247258
Change-Id: I48c5d77e8312db0f2c1d8f944b17f6920799685e
diff --git a/src/java_lang_System.cc b/src/java_lang_System.cc
index a9652cc..72cf092 100644
--- a/src/java_lang_System.cc
+++ b/src/java_lang_System.cc
@@ -185,7 +185,7 @@
 
   // Neither class is primitive. Are the types trivially compatible?
   const size_t width = sizeof(Object*);
-  if (dstComponentType->IsAssignableFrom(srcComponentType)) {
+  if (dstArray == srcArray || dstComponentType->IsAssignableFrom(srcComponentType)) {
     // Yes. Bulk copy.
     COMPILE_ASSERT(sizeof(width) == sizeof(uint32_t), move32_assumes_Object_references_are_32_bit);
     move32(dstBytes + dstPos * width, srcBytes + srcPos * width, length * width);
@@ -193,49 +193,49 @@
     return;
   }
 
-  // The arrays are not trivially compatible.  However, we
-  // may still be able to do this if the destination object is
-  // compatible (e.g. copy Object[] to String[], but the Object
-  // being copied is actually a String).  We need to copy elements
-  // one by one until something goes wrong.
-  //
-  // Because of overlapping moves, what we really want to do
-  // is compare the types and count up how many we can move,
-  // then call move32() to shift the actual data.  If we just
-  // start from the front we could do a smear rather than a move.
+  // The arrays are not trivially compatible. However, we may still be able to copy some or all of
+  // the elements if the source objects are compatible (for example, copying an Object[] to
+  // String[], the Objects being copied might actually be Strings).
+  // We can't do a bulk move because that would introduce a check-use race condition, so we copy
+  // elements one by one.
 
-  // TODO: this idea is flawed. a malicious caller could exploit the check-use
-  // race by modifying the source array after we check but before we copy,
-  // and cause us to copy incompatible elements.
+  // We already dealt with overlapping copies, so we don't need to cope with that case below.
+  CHECK_NE(dstArray, srcArray);
 
-  Object* const * srcObj = reinterpret_cast<Object* const *>(srcBytes + srcPos * width);
+  Object* const * srcObjects = reinterpret_cast<Object* const *>(srcBytes + srcPos * width);
+  Object** dstObjects = reinterpret_cast<Object**>(dstBytes + dstPos * width);
   Class* dstClass = dstArray->GetClass()->GetComponentType();
 
-  Class* initialElementClass = NULL;
-  if (length > 0 && srcObj[0] != NULL) {
-    initialElementClass = srcObj[0]->GetClass();
-    if (!dstClass->IsAssignableFrom(initialElementClass)) {
-      initialElementClass = NULL;
+  // We want to avoid redundant IsAssignableFrom checks where possible, so we cache a class that
+  // we know is assignable to the destination array's component type.
+  Class* lastAssignableElementClass = dstClass;
+
+  int i = 0;
+  for (; i < length; ++i) {
+    Object* o = srcObjects[i];
+    if (o != NULL) {
+      Class* oClass = o->GetClass();
+      if (lastAssignableElementClass == oClass) {
+        dstObjects[i] = o;
+      } else if (dstClass->IsAssignableFrom(oClass)) {
+        lastAssignableElementClass = oClass;
+        dstObjects[i] = o;
+      } else {
+        // Can't put this element into the array.
+        break;
+      }
+    } else {
+      dstObjects[i] = NULL;
     }
   }
 
-  int copyCount;
-  for (copyCount = 0; copyCount < length; copyCount++) {
-    if (srcObj[copyCount] != NULL && srcObj[copyCount]->GetClass() != initialElementClass && !dstClass->IsAssignableFrom(srcObj[copyCount]->GetClass())) {
-      // Can't put this element into the array.
-      // We'll copy up to this point, then throw.
-      break;
-    }
-  }
-
-  move32(dstBytes + dstPos * width, srcBytes + srcPos * width, copyCount * width);
   Heap::WriteBarrierArray(dstArray, dstPos, length);
-  if (copyCount != length) {
-    std::string actualSrcType(PrettyTypeOf(srcObj[copyCount]));
+  if (i != length) {
+    std::string actualSrcType(PrettyTypeOf(srcObjects[i]));
     std::string dstType(PrettyTypeOf(dstArray));
     self->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
         "source[%d] of type %s cannot be stored in destination array of type %s",
-        srcPos + copyCount, actualSrcType.c_str(), dstType.c_str());
+        srcPos + i, actualSrcType.c_str(), dstType.c_str());
     return;
   }
 }