CVE-2020-1911: Access of Resource Using Incompatible Type ('Type Confusion') (CWE-843)
value: A type confusion vulnerability when resolving properties of JavaScript objects with specially-crafted prototype chains in Facebook Hermes prior to commit fe52854cdf6725c2eaa9e125995da76e6ceb27da allows attackers to potentially execute arbitrary code via crafted JavaScript. Note that this is only exploitable if the application using Hermes permits evaluation of untrusted JavaScript. Hence, most React Native applications are not affected.
CWE description: The product allocates or initializes a resource such as a pointer, object, or variable using one type, but it later accesses that resource using a type that is incompatible with the original type.
CVSS v3 base score: 9.8
Published: 2020-09-04T03:15Z
Repository: https://github.com/facebook/hermes
Language: C++
Files changed: API/jsi/jsi/test/testlib.cpp, lib/VM/JSObject.cpp
Commit date: 2020-08-26 11:43:37 -0700
Commit message: [CVE-2020-1911] Look up HostObject computed properties on the right object in the prototype chain.

Summary:
The change in the hermes repository fixes the security vulnerability
CVE-2020-1911.  This vulnerability only affects applications which
allow evaluation of uncontrolled, untrusted JavaScript code not
shipped with the app, so React Native apps will generally not be affected.

This revision includes a test for the bug.  The test is generic JSI
code, so it is included in the hermes and react-native repositories.

Changelog: [Internal]

Reviewed By: tmikov

Differential Revision: D23322992

fbshipit-source-id: 4e88c974afe1ad33a263f9cac03e9dc98d33649a

Diff: diff --git a/API/jsi/jsi/test/testlib.cpp b/API/jsi/jsi/test/testlib.cpp
index 996c2cb6..8cf831d3 100644
--- a/API/jsi/jsi/test/testlib.cpp
+++ b/API/jsi/jsi/test/testlib.cpp
@@ -392,10 +392,27 @@ TEST_P(JSITest, HostObjectTest) {
   EXPECT_FALSE(hasOwnPropertyName
                    .call(rt, howpn, String::createFromAscii(rt, "not_existing"))
                    .getBool());
 }
 
+TEST_P(JSITest, HostObjectProtoTest) {
+  class ProtoHostObject : public HostObject {
+    Value get(Runtime& rt, const PropNameID&) override {
+      return String::createFromAscii(rt, "phoprop");
+    }
+  };
+
+  rt.global().setProperty(
+      rt,
+      "pho",
+      Object::createFromHostObject(rt, std::make_shared<ProtoHostObject>()));
+
+  EXPECT_EQ(
+      eval("({__proto__: pho})[Symbol.toPrimitive]").getString(rt).utf8(rt),
+      "phoprop");
+}
+
 TEST_P(JSITest, ArrayTest) {
   eval("x = {1:2, '3':4, 5:'six', 'seven':['eight', 'nine']}");
 
   Object x = rt.global().getPropertyAsObject(rt, "x");
   Array names = x.getPropertyNames(rt);
diff --git a/lib/VM/JSObject.cpp b/lib/VM/JSObject.cpp
index bbaba302..83d60aa7 100644
--- a/lib/VM/JSObject.cpp
+++ b/lib/VM/JSObject.cpp
@@ -1171,11 +1171,11 @@ CallResult<PseudoHandle<>> JSObject::getComputedWithReceiver_RJS(
     return accessor->getter.get(runtime)->executeCall0(
         runtime->makeHandle(accessor->getter), runtime, receiver);
   } else if (desc.flags.hostObject) {
     SymbolID id{};
     LAZY_TO_IDENTIFIER(runtime, nameValPrimitiveHandle, id);
-    auto propRes = vmcast<HostObject>(selfHandle.get())->get(id);
+    auto propRes = vmcast<HostObject>(propObj.get())->get(id);
     if (propRes == ExecutionStatus::EXCEPTION)
       return ExecutionStatus::EXCEPTION;
     return createPseudoHandle(*propRes);
   } else {
     assert(desc.flags.proxyObject && "descriptor flags are impossible");

Vulnerable code:     auto propRes = vmcast<HostObject>(selfHandle.get())->get(id);
Fixed code: TEST_P(JSITest, HostObjectProtoTest) {
  class ProtoHostObject : public HostObject {
    Value get(Runtime& rt, const PropNameID&) override {
      return String::createFromAscii(rt, "phoprop");
    }
  };

  rt.global().setProperty(
      rt,
      "pho",
      Object::createFromHostObject(rt, std::make_shared<ProtoHostObject>()));

  EXPECT_EQ(
      eval("({__proto__: pho})[Symbol.toPrimitive]").getString(rt).utf8(rt),
      "phoprop");
}

    auto propRes = vmcast<HostObject>(propObj.get())->get(id);