Block access to java.lang.Object.getClass in injected Java objects

Prevents invocations of java.lang.Object.getClass from JavaScript code via an
injected Java object.

On an invocation attempt, logs an EventLog message, and sends a message to
WebChromeClient.onConsoleMessage callback.

Bug: 13694467
Change-Id: I9eab38aac48c6ae4ad49dd06c67c8ccf0af17ea7
diff --git a/content/browser/renderer_host/java/java_bound_object.cc b/content/browser/renderer_host/java/java_bound_object.cc
index e2ad87a..a790816 100644
--- a/content/browser/renderer_host/java/java_bound_object.cc
+++ b/content/browser/renderer_host/java/java_bound_object.cc
@@ -4,6 +4,9 @@
 
 #include "content/browser/renderer_host/java/java_bound_object.h"
 
+#include <log/log.h>
+#include <unistd.h>
+
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/memory/singleton.h"
@@ -45,6 +48,8 @@
 const char kReturningJavaLangReflectMethodArray[] =
     "()[Ljava/lang/reflect/Method;";
 const char kTakesJavaLangClassReturningBoolean[] = "(Ljava/lang/Class;)Z";
+const char kAccessToObjectGetClassIsBlocked[] =
+    "Access to java.lang.Object.getClass is blocked";
 
 // Our special NPObject type.  We extend an NPObject with a pointer to a
 // JavaBoundObject.  We also add static methods for each of the NPObject
@@ -797,6 +802,7 @@
     : java_object_(AttachCurrentThread(), object.obj()),
       manager_(manager),
       are_methods_set_up_(false),
+      object_get_class_method_id_(NULL),
       safe_annotation_clazz_(safe_annotation_clazz) {
   BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
@@ -853,6 +859,22 @@
     return false;
   }
 
+  // Block access to java.lang.Object.getClass.
+  // As it is declared to be final, it is sufficient to compare methodIDs.
+  if (method->id() == object_get_class_method_id_) {
+    // See frameworks/base/core/java/android/webkit/EventLogTags.logtags
+    LOG_EVENT_INT(70151, getuid());
+    // Also, send a message to WebChromeClient.onConsoleMessage callback
+    BrowserThread::PostTask(
+        BrowserThread::UI,
+        FROM_HERE,
+        base::Bind(&JavaBridgeDispatcherHostManager::AddMessageToConsole,
+                   manager_,
+                   logging::LOG_ERROR,
+                   kAccessToObjectGetClassIsBlocked));
+    return false;
+  }
+
   // Coerce
   std::vector<jvalue> parameters(arg_count);
   for (size_t i = 0; i < arg_count; ++i) {
@@ -888,6 +910,13 @@
   are_methods_set_up_ = true;
 
   JNIEnv* env = AttachCurrentThread();
+
+   object_get_class_method_id_ = GetMethodIDFromClassName(
+      env,
+      kJavaLangObject,
+      kGetClass,
+      kReturningJavaLangClass);
+
   ScopedJavaLocalRef<jobject> obj = java_object_.get(env);
 
   if (obj.is_null()) {
@@ -895,11 +924,7 @@
   }
 
   ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>(
-      env->CallObjectMethod(obj.obj(),  GetMethodIDFromClassName(
-          env,
-          kJavaLangObject,
-          kGetClass,
-          kReturningJavaLangClass))));
+      env->CallObjectMethod(obj.obj(), object_get_class_method_id_)));
 
   ScopedJavaLocalRef<jobjectArray> methods(env, static_cast<jobjectArray>(
       env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName(
diff --git a/content/browser/renderer_host/java/java_bound_object.h b/content/browser/renderer_host/java/java_bound_object.h
index ff97fdb..6b10e4f 100644
--- a/content/browser/renderer_host/java/java_bound_object.h
+++ b/content/browser/renderer_host/java/java_bound_object.h
@@ -78,6 +78,7 @@
   typedef std::multimap<std::string, linked_ptr<JavaMethod> > JavaMethodMap;
   mutable JavaMethodMap methods_;
   mutable bool are_methods_set_up_;
+  mutable jmethodID object_get_class_method_id_;
 
   base::android::ScopedJavaGlobalRef<jclass> safe_annotation_clazz_;
 
diff --git a/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.cc b/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.cc
index 02cf4dc..be46f68 100644
--- a/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.cc
+++ b/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.cc
@@ -7,6 +7,7 @@
 #include "base/android/jni_android.h"
 #include "base/android/jni_helper.h"
 #include "base/android/scoped_java_ref.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
@@ -14,6 +15,8 @@
 #include "content/browser/renderer_host/java/java_bridge_dispatcher_host.h"
 #include "content/common/android/hash_set.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
 #include "third_party/WebKit/public/web/WebBindings.h"
 
 namespace content {
@@ -154,4 +157,13 @@
   }
 }
 
+void JavaBridgeDispatcherHostManager::AddMessageToConsole(
+    int32 level,
+    const char* message) {
+  WebContentsDelegate* delegate = web_contents()->GetDelegate();
+  if (delegate)
+    delegate->AddMessageToConsole(
+        web_contents(), level, ASCIIToUTF16(message), 0, ASCIIToUTF16(""));
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h b/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h
index 29523b5..602f1bf 100644
--- a/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h
+++ b/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h
@@ -56,6 +56,8 @@
   void JavaBoundObjectCreated(const base::android::JavaRef<jobject>& object);
   void JavaBoundObjectDestroyed(const base::android::JavaRef<jobject>& object);
 
+  void AddMessageToConsole(int32 level, const char* message);
+
  private:
   typedef std::map<RenderViewHost*, scoped_refptr<JavaBridgeDispatcherHost> >
       InstanceMap;