Skip to content

Commit e0a2a58

Browse files
committed
[MERGE #5269 @VSadov] Fix AV when GetOwnPropertyDescriptor trap revokes the proxy
Merge pull request #5269 from VSadov:revokeCrash - Do not re-fetch proxy.target after invoking `handler.getOwnPropertyDescriptor` since the call may have revoked the proxy. Use the cached `targetObj` instead. Found by OSS-Fuzz - Do not consider the case a `TypeError`. Arguably the proxy was valid at the time of the entry. (also consistent with other JS runtimes).
2 parents d1dc14e + 4ccd8b6 commit e0a2a58

File tree

6 files changed

+56
-39
lines changed

6 files changed

+56
-39
lines changed

lib/Runtime/Library/JavascriptObject.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -674,12 +674,12 @@ using namespace Js;
674674
if (obj->IsExternal())
675675
{
676676
isPropertyDescriptorDefined = obj->HasOwnProperty(propertyId) ?
677-
JavascriptOperators::GetOwnPropertyDescriptor(obj, propertyId, scriptContext, &propertyDescriptor) : obj->GetDefaultPropertyDescriptor(propertyDescriptor);
677+
JavascriptOperators::GetOwnPropertyDescriptor(obj, propertyId, scriptContext, &propertyDescriptor) :
678+
FALSE;
678679
}
679680
else
680681
{
681-
isPropertyDescriptorDefined = JavascriptOperators::GetOwnPropertyDescriptor(obj, propertyId, scriptContext, &propertyDescriptor) ||
682-
obj->GetDefaultPropertyDescriptor(propertyDescriptor);
682+
isPropertyDescriptorDefined = JavascriptOperators::GetOwnPropertyDescriptor(obj, propertyId, scriptContext, &propertyDescriptor);
683683
}
684684
return isPropertyDescriptorDefined;
685685
}

lib/Runtime/Library/JavascriptProxy.cpp

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,7 @@ namespace Js
195195
target = nullptr;
196196
}
197197

198-
template <class Fn, class GetPropertyIdFunc>
199-
BOOL JavascriptProxy::GetPropertyDescriptorTrap(Var originalInstance, Fn fn, GetPropertyIdFunc getPropertyId, PropertyDescriptor* resultDescriptor, ScriptContext* requestContext)
198+
BOOL JavascriptProxy::GetPropertyDescriptorTrap(PropertyId propertyId, PropertyDescriptor* resultDescriptor, ScriptContext* requestContext)
200199
{
201200
PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
202201

@@ -223,17 +222,17 @@ namespace Js
223222

224223
Assert((static_cast<DynamicType*>(GetType()))->GetTypeHandler()->GetPropertyCount() == 0 ||
225224
(static_cast<DynamicType*>(GetType()))->GetTypeHandler()->GetPropertyId(GetScriptContext(), 0) == InternalPropertyIds::WeakMapKeyMap);
225+
226226
JavascriptFunction* gOPDMethod = GetMethodHelper(PropertyIds::getOwnPropertyDescriptor, requestContext);
227227

228228
//7. If trap is undefined, then
229229
// a.Return the result of calling the[[GetOwnProperty]] internal method of target with argument P.
230230
if (nullptr == gOPDMethod || GetScriptContext()->IsHeapEnumInProgress())
231231
{
232232
resultDescriptor->SetFromProxy(false);
233-
return fn(targetObj);
233+
return JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, resultDescriptor);
234234
}
235235

236-
PropertyId propertyId = getPropertyId();
237236
Var propertyName = GetName(requestContext, propertyId);
238237

239238
Assert(JavascriptString::Is(propertyName) || JavascriptSymbol::Is(propertyName));
@@ -254,9 +253,8 @@ namespace Js
254253
//11. Let targetDesc be the result of calling the[[GetOwnProperty]] internal method of target with argument P.
255254
//12. ReturnIfAbrupt(targetDesc).
256255
PropertyDescriptor targetDescriptor;
257-
BOOL hasProperty;
256+
BOOL hasProperty = JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, &targetDescriptor);
258257

259-
hasProperty = JavascriptOperators::GetOwnPropertyDescriptor(targetObj, getPropertyId(), requestContext, &targetDescriptor);
260258
//13. If trapResultObj is undefined, then
261259
//a.If targetDesc is undefined, then return undefined.
262260
//b.If targetDesc.[[Configurable]] is false, then throw a TypeError exception.
@@ -274,10 +272,13 @@ namespace Js
274272
{
275273
JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("getOwnPropertyDescriptor"));
276274
}
277-
if (!target->IsExtensible())
275+
276+
// do not use "target" here, the trap may have caused it to change
277+
if (!targetObj->IsExtensible())
278278
{
279279
JavascriptError::ThrowTypeError(requestContext, JSERR_InconsistentTrapResult, _u("getOwnPropertyDescriptor"));
280280
}
281+
281282
return FALSE;
282283
}
283284

@@ -293,7 +294,8 @@ namespace Js
293294
//i.Throw a TypeError exception.
294295
//22. Return resultDesc.
295296

296-
BOOL isTargetExtensible = target->IsExtensible();
297+
// do not use "target" here, the trap may have caused it to change
298+
BOOL isTargetExtensible = targetObj->IsExtensible();
297299
BOOL toProperty = JavascriptOperators::ToPropertyDescriptor(getResult, resultDescriptor, requestContext);
298300
if (!toProperty && isTargetExtensible)
299301
{
@@ -1246,16 +1248,6 @@ namespace Js
12461248
return trapResult;
12471249
}
12481250

1249-
BOOL JavascriptProxy::GetDefaultPropertyDescriptor(PropertyDescriptor& descriptor)
1250-
{
1251-
if (target == nullptr)
1252-
{
1253-
JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u(""));
1254-
}
1255-
1256-
return target->GetDefaultPropertyDescriptor(descriptor);
1257-
}
1258-
12591251
// 7.3.12 in ES 2015. While this should have been no observable behavior change. Till there is obvious change warrant this
12601252
// to be moved to JavascriptOperators, let's keep it in proxy only first.
12611253
BOOL JavascriptProxy::TestIntegrityLevel(IntegrityLevel integrityLevel, RecyclableObject* obj, ScriptContext* scriptContext)
@@ -1672,12 +1664,7 @@ namespace Js
16721664
BOOL JavascriptProxy::GetOwnPropertyDescriptor(RecyclableObject* obj, PropertyId propertyId, ScriptContext* requestContext, PropertyDescriptor* propertyDescriptor)
16731665
{
16741666
JavascriptProxy* proxy = JavascriptProxy::FromVar(obj);
1675-
auto fn = [&](RecyclableObject *targetObj)-> BOOL {
1676-
return JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propertyId, requestContext, propertyDescriptor);
1677-
};
1678-
auto getPropertyId = [&]() -> PropertyId {return propertyId; };
1679-
BOOL foundProperty = proxy->GetPropertyDescriptorTrap(obj, fn, getPropertyId, propertyDescriptor, requestContext);
1680-
return foundProperty;
1667+
return proxy->GetPropertyDescriptorTrap(propertyId, propertyDescriptor, requestContext);
16811668
}
16821669

16831670

lib/Runtime/Library/JavascriptProxy.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ namespace Js
116116
virtual BOOL IsExtensible() override;
117117
virtual BOOL PreventExtensions() override;
118118
virtual void ThrowIfCannotDefineProperty(PropertyId propId, const PropertyDescriptor& descriptor) { }
119-
virtual BOOL GetDefaultPropertyDescriptor(PropertyDescriptor& descriptor) override;
120119
virtual BOOL Seal() override;
121120
virtual BOOL Freeze() override;
122121
virtual BOOL IsSealed() override;
@@ -231,8 +230,7 @@ namespace Js
231230
template <class Fn, class GetPropertyIdFunc>
232231
BOOL GetPropertyTrap(Var instance, PropertyDescriptor* propertyDescriptor, Fn fn, GetPropertyIdFunc getPropertyId, ScriptContext* requestContext);
233232

234-
template <class Fn, class GetPropertyIdFunc>
235-
BOOL GetPropertyDescriptorTrap(Var originalInstance, Fn fn, GetPropertyIdFunc getPropertyId, PropertyDescriptor* resultDescriptor, ScriptContext* requestContext);
233+
BOOL GetPropertyDescriptorTrap(PropertyId propertyId, PropertyDescriptor* resultDescriptor, ScriptContext* requestContext);
236234

237235
#if ENABLE_TTD
238236
public:

lib/Runtime/Types/RecyclableObject.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -313,13 +313,6 @@ namespace Js
313313
// Do nothing
314314
}
315315

316-
BOOL RecyclableObject::GetDefaultPropertyDescriptor(PropertyDescriptor& descriptor)
317-
{
318-
// By default, when GetOwnPropertyDescriptor is called for a nonexistent property,
319-
// return undefined.
320-
return false;
321-
}
322-
323316
HRESULT RecyclableObject::QueryObjectInterface(REFIID riid, void **ppvObj)
324317
{
325318
Assert(!this->GetScriptContext()->GetThreadContext()->IsScriptActive());

lib/Runtime/Types/RecyclableObject.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,6 @@ namespace Js {
340340
virtual BOOL IsProtoImmutable() const { return false; }
341341
virtual BOOL PreventExtensions() { return false; }; // Sets [[Extensible]] flag of instance to false
342342
virtual void ThrowIfCannotDefineProperty(PropertyId propId, const PropertyDescriptor& descriptor);
343-
virtual BOOL GetDefaultPropertyDescriptor(PropertyDescriptor& descriptor);
344343
virtual BOOL Seal() { return false; } // Seals the instance, no additional property can be added or deleted
345344
virtual BOOL Freeze() { return false; } // Freezes the instance, no additional property can be added or deleted or written
346345
virtual BOOL IsSealed() { return false; }

test/es6/proxybugs.js

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,47 @@ var tests = [
230230
};
231231

232232
var obj = Proxy.revocable({}, handler);
233-
assert.throws( () => { Object.getOwnPropertyDescriptor(obj.proxy, 'a'); }, TypeError);
233+
Object.getOwnPropertyDescriptor(obj.proxy, 'a');
234+
assert.isTrue(trapCalled);
235+
}
236+
},
237+
{
238+
name: "Assertion validation : revoking the proxy in getOwnPropertyDescriptor trap, undefined argument",
239+
body() {
240+
var trapCalled = false;
241+
var handler = {
242+
getOwnPropertyDescriptor: function (a, b, c) {
243+
trapCalled = true;
244+
obj.revoke();
245+
246+
// used to cause AV
247+
a[undefined] = new String();
248+
}
249+
};
250+
251+
var obj = Proxy.revocable({}, handler);
252+
Object.getOwnPropertyDescriptor(obj.proxy);
253+
assert.isTrue(trapCalled);
254+
}
255+
},
256+
{
257+
name: "Assertion validation : revoking the proxy in getOwnPropertyDescriptor trap, not undefined return",
258+
body() {
259+
var trapCalled = false;
260+
var handler = {
261+
getOwnPropertyDescriptor: function (a, b, c) {
262+
trapCalled = true;
263+
let result = Object.getOwnPropertyDescriptor(obj, 'proxy');
264+
265+
obj.revoke();
266+
267+
// used to cause AV
268+
return result;
269+
}
270+
};
271+
272+
var obj = Proxy.revocable({}, handler);
273+
Object.getOwnPropertyDescriptor(obj.proxy);
234274
assert.isTrue(trapCalled);
235275
}
236276
},

0 commit comments

Comments
 (0)