Commit 69e904a8 authored by barraclough@apple.com's avatar barraclough@apple.com

Poisoning of strict caller,arguments inappropriately poisoning "in"

https://bugs.webkit.org/show_bug.cgi?id=63398

Reviewed by Oliver Hunt.

Source/JavaScriptCore: 

This fixes the problem by correctly implementing the spec -
the error should actually be being thrown from a standard JS getter/setter.
This implements spec correct behaviour for strict mode JS functions & bound
functions, I'll follow up with a patch to do the same for arguments.

* runtime/JSBoundFunction.cpp:
(JSC::JSBoundFunction::finishCreation):
    - Add the poisoned caller/arguments properties.
* runtime/JSBoundFunction.h:
* runtime/JSFunction.cpp:
(JSC::JSFunction::finishCreation):
(JSC::JSFunction::getOwnPropertySlot):
(JSC::JSFunction::getOwnPropertyDescriptor):
(JSC::JSFunction::put):
    - If the caller/arguments are accessed on a strict mode function, lazily add the ThrowTypeError getter.
* runtime/JSFunction.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::createThrowTypeError):
(JSC::JSGlobalObject::visitChildren):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::throwTypeErrorGetterSetter):
    - Add a ThrowTypeError type, per ES5 13.2.3.
* runtime/JSGlobalObjectFunctions.cpp:
(JSC::globalFuncThrowTypeError):
* runtime/JSGlobalObjectFunctions.h:
    - Implementation of ThrowTypeError.
* runtime/JSObject.cpp:
(JSC::JSObject::initializeGetterSetterProperty):
* runtime/JSObject.h:
    - This function adds a new property (must not exist already) that is an initialized getter/setter.

LayoutTests: 

* fast/js/basic-strict-mode-expected.txt:
* fast/js/script-tests/basic-strict-mode.js:
    - Add tests.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@97905 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 272b0d42
2011-10-19 Gavin Barraclough <barraclough@apple.com>
Poisoning of strict caller,arguments inappropriately poisoning "in"
https://bugs.webkit.org/show_bug.cgi?id=63398
Reviewed by Oliver Hunt.
* fast/js/basic-strict-mode-expected.txt:
* fast/js/script-tests/basic-strict-mode.js:
- Add tests.
2011-10-19 Erik Arvidsson <arv@chromium.org>
Fix test breakage after r97881
......@@ -56,16 +56,20 @@ PASS (function (){'use strict'; with(1){};}) threw exception SyntaxError: 'with'
PASS (function(){(function (){'use strict'; with(1){};})}) threw exception SyntaxError: 'with' statements are not valid in strict mode.
PASS (function (){'use strict'; arguments.callee; })() threw exception TypeError: Unable to access callee of strict mode function.
PASS (function (){'use strict'; arguments.caller; })() threw exception TypeError: Unable to access caller of strict mode function.
PASS (function f(){'use strict'; f.arguments; })() threw exception TypeError: Cannot access arguments property of a strict mode function.
PASS (function f(){'use strict'; f.caller; })() threw exception TypeError: Cannot access caller property of a strict mode function.
PASS (function f(){'use strict'; f.arguments=5; })() threw exception TypeError: Cannot access arguments property of a strict mode function.
PASS (function f(){'use strict'; f.caller=5; })() threw exception TypeError: Cannot access caller property of a strict mode function.
PASS (function f(){'use strict'; f.arguments; })() threw exception TypeError: Type error.
PASS (function f(){'use strict'; f.caller; })() threw exception TypeError: Type error.
PASS (function f(){'use strict'; f.arguments=5; })() threw exception TypeError: Type error.
PASS (function f(){'use strict'; f.caller=5; })() threw exception TypeError: Type error.
PASS (function (arg){'use strict'; arguments.callee; })() threw exception TypeError: Unable to access callee of strict mode function.
PASS (function (arg){'use strict'; arguments.caller; })() threw exception TypeError: Unable to access caller of strict mode function.
PASS (function f(arg){'use strict'; f.arguments; })() threw exception TypeError: Cannot access arguments property of a strict mode function.
PASS (function f(arg){'use strict'; f.caller; })() threw exception TypeError: Cannot access caller property of a strict mode function.
PASS (function f(arg){'use strict'; f.arguments=5; })() threw exception TypeError: Cannot access arguments property of a strict mode function.
PASS (function f(arg){'use strict'; f.caller=5; })() threw exception TypeError: Cannot access caller property of a strict mode function.
PASS (function f(arg){'use strict'; f.arguments; })() threw exception TypeError: Type error.
PASS (function f(arg){'use strict'; f.caller; })() threw exception TypeError: Type error.
PASS (function f(arg){'use strict'; f.arguments=5; })() threw exception TypeError: Type error.
PASS (function f(arg){'use strict'; f.caller=5; })() threw exception TypeError: Type error.
PASS "caller" in function(){"use strict"} is true
PASS (function(){"use strict";}).hasOwnProperty("caller") is true
PASS "arguments" in function(){"use strict"} is true
PASS (function(){"use strict";}).hasOwnProperty("arguments") is true
PASS 'use strict'; (function (){with(1){};}) threw exception SyntaxError: 'with' statements are not valid in strict mode.
PASS (function(){'use strict'; (function (){with(1){};})}) threw exception SyntaxError: 'with' statements are not valid in strict mode.
PASS 'use strict'; (function (){var a; delete a;}) threw exception SyntaxError: Cannot delete unqualified property 'a' in strict mode.
......
......@@ -74,6 +74,13 @@ shouldThrow("(function f(arg){'use strict'; f.arguments; })()");
shouldThrow("(function f(arg){'use strict'; f.caller; })()");
shouldThrow("(function f(arg){'use strict'; f.arguments=5; })()");
shouldThrow("(function f(arg){'use strict'; f.caller=5; })()");
// arguments/caller poisoning should be visible but not throw with 'in' & 'hasOwnProperty'.
shouldBeTrue('"caller" in function(){"use strict"}');
shouldBeTrue('(function(){"use strict";}).hasOwnProperty("caller")');
shouldBeTrue('"arguments" in function(){"use strict"}');
shouldBeTrue('(function(){"use strict";}).hasOwnProperty("arguments")');
shouldBeSyntaxError("'use strict'; (function (){with(1){};})");
shouldBeSyntaxError("'use strict'; (function (){var a; delete a;})");
shouldBeSyntaxError("'use strict'; var a; (function (){ delete a;})");
......
2011-10-19 Gavin Barraclough <barraclough@apple.com>
Poisoning of strict caller,arguments inappropriately poisoning "in"
https://bugs.webkit.org/show_bug.cgi?id=63398
Reviewed by Oliver Hunt.
This fixes the problem by correctly implementing the spec -
the error should actually be being thrown from a standard JS getter/setter.
This implements spec correct behaviour for strict mode JS functions & bound
functions, I'll follow up with a patch to do the same for arguments.
* runtime/JSBoundFunction.cpp:
(JSC::JSBoundFunction::finishCreation):
- Add the poisoned caller/arguments properties.
* runtime/JSBoundFunction.h:
* runtime/JSFunction.cpp:
(JSC::JSFunction::finishCreation):
(JSC::JSFunction::getOwnPropertySlot):
(JSC::JSFunction::getOwnPropertyDescriptor):
(JSC::JSFunction::put):
- If the caller/arguments are accessed on a strict mode function, lazily add the ThrowTypeError getter.
* runtime/JSFunction.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::createThrowTypeError):
(JSC::JSGlobalObject::visitChildren):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::throwTypeErrorGetterSetter):
- Add a ThrowTypeError type, per ES5 13.2.3.
* runtime/JSGlobalObjectFunctions.cpp:
(JSC::globalFuncThrowTypeError):
* runtime/JSGlobalObjectFunctions.h:
- Implementation of ThrowTypeError.
* runtime/JSObject.cpp:
(JSC::JSObject::initializeGetterSetterProperty):
* runtime/JSObject.h:
- This function adds a new property (must not exist already) that is an initialized getter/setter.
2011-10-19 Yuqiang Xian <yuqiang.xian@intel.com>
DFG JIT 32_64 - improve double boxing/unboxing
......@@ -96,43 +96,6 @@ bool JSBoundFunction::hasInstance(ExecState* exec, JSValue value, JSValue)
return m_targetFunction->hasInstance(exec, value, proto);
}
bool JSBoundFunction::getOwnPropertySlotVirtual(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
{
return getOwnPropertySlot(this, exec, propertyName, slot);
}
bool JSBoundFunction::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
{
if (propertyName == exec->propertyNames().arguments) {
throwTypeError(exec, StrictModeArgumentsAccessError);
slot.setValue(jsNull());
return true;
}
if (propertyName == exec->propertyNames().caller) {
throwTypeError(exec, StrictModeCallerAccessError);
slot.setValue(jsNull());
return true;
}
return Base::getOwnPropertySlot(cell, exec, propertyName, slot);
}
bool JSBoundFunction::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
{
if (propertyName == exec->propertyNames().arguments) {
createDescriptorForThrowingProperty(exec, descriptor, StrictModeArgumentsAccessError);
return true;
}
if (propertyName == exec->propertyNames().caller) {
createDescriptorForThrowingProperty(exec, descriptor, StrictModeCallerAccessError);
return true;
}
return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
}
JSBoundFunction::JSBoundFunction(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs)
: Base(exec, globalObject, structure)
, m_targetFunction(exec->globalData(), this, targetFunction)
......@@ -145,6 +108,9 @@ void JSBoundFunction::finishCreation(ExecState* exec, NativeExecutable* executab
{
Base::finishCreation(exec, executable, length, name);
ASSERT(inherits(&s_info));
initializeGetterSetterProperty(exec, exec->propertyNames().arguments, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Getter | Setter);
initializeGetterSetterProperty(exec, exec->propertyNames().caller, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Getter | Setter);
}
void JSBoundFunction::visitChildren(JSCell* cell, SlotVisitor& visitor)
......
......@@ -39,10 +39,6 @@ public:
static JSBoundFunction* create(ExecState*, JSGlobalObject*, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs, int, const Identifier&);
virtual bool getOwnPropertySlotVirtual(ExecState*, const Identifier&, PropertySlot&);
static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier&, PropertySlot&);
virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&);
virtual bool hasInstance(ExecState*, JSValue value, JSValue proto);
JSObject* targetFunction() { return m_targetFunction.get(); }
......
......@@ -97,7 +97,8 @@ void JSFunction::finishCreation(ExecState* exec, NativeExecutable* executable, i
Base::finishCreation(exec->globalData());
ASSERT(inherits(&s_info));
m_executable.set(exec->globalData(), this, executable);
putDirect(exec->globalData(), exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum);
if (!name.isNull())
putDirect(exec->globalData(), exec->globalData().propertyNames->name, jsString(exec, name.ustring()), DontDelete | ReadOnly | DontEnum);
putDirect(exec->globalData(), exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum);
}
......@@ -117,9 +118,6 @@ JSFunction::~JSFunction()
ASSERT(vptr() == JSGlobalData::jsFunctionVPtr);
}
const char* StrictModeCallerAccessError = "Cannot access caller property of a strict mode function";
const char* StrictModeArgumentsAccessError = "Cannot access arguments property of a strict mode function";
void createDescriptorForThrowingProperty(ExecState* exec, PropertyDescriptor& descriptor, const char* message)
{
JSValue thrower = createTypeErrorFunction(exec, message);
......@@ -224,11 +222,14 @@ bool JSFunction::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identif
if (propertyName == exec->propertyNames().arguments) {
if (thisObject->jsExecutable()->isStrictMode()) {
throwTypeError(exec, StrictModeArgumentsAccessError);
slot.setValue(jsNull());
return true;
bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
if (!result) {
thisObject->initializeGetterSetterProperty(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Getter | Setter);
result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
ASSERT(result);
}
return result;
}
slot.setCacheableCustom(thisObject, argumentsGetter);
return true;
}
......@@ -240,9 +241,13 @@ bool JSFunction::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identif
if (propertyName == exec->propertyNames().caller) {
if (thisObject->jsExecutable()->isStrictMode()) {
throwTypeError(exec, StrictModeCallerAccessError);
slot.setValue(jsNull());
return true;
bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
if (!result) {
thisObject->initializeGetterSetterProperty(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Getter | Setter);
result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
ASSERT(result);
}
return result;
}
slot.setCacheableCustom(thisObject, callerGetter);
return true;
......@@ -263,10 +268,16 @@ bool JSFunction::getOwnPropertyDescriptor(ExecState* exec, const Identifier& pro
}
if (propertyName == exec->propertyNames().arguments) {
if (jsExecutable()->isStrictMode())
createDescriptorForThrowingProperty(exec, descriptor, StrictModeArgumentsAccessError);
else
descriptor.setDescriptor(exec->interpreter()->retrieveArguments(exec, this), ReadOnly | DontEnum | DontDelete);
if (jsExecutable()->isStrictMode()) {
bool result = Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
if (!result) {
initializeGetterSetterProperty(exec, propertyName, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Getter | Setter);
result = Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
ASSERT(result);
}
return result;
}
descriptor.setDescriptor(exec->interpreter()->retrieveArguments(exec, this), ReadOnly | DontEnum | DontDelete);
return true;
}
......@@ -276,10 +287,16 @@ bool JSFunction::getOwnPropertyDescriptor(ExecState* exec, const Identifier& pro
}
if (propertyName == exec->propertyNames().caller) {
if (jsExecutable()->isStrictMode())
createDescriptorForThrowingProperty(exec, descriptor, StrictModeCallerAccessError);
else
descriptor.setDescriptor(exec->interpreter()->retrieveCaller(exec, this), ReadOnly | DontEnum | DontDelete);
if (jsExecutable()->isStrictMode()) {
bool result = Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
if (!result) {
initializeGetterSetterProperty(exec, propertyName, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Getter | Setter);
result = Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
ASSERT(result);
}
return result;
}
descriptor.setDescriptor(exec->interpreter()->retrieveCaller(exec, this), ReadOnly | DontEnum | DontDelete);
return true;
}
......@@ -318,15 +335,12 @@ void JSFunction::put(JSCell* cell, ExecState* exec, const Identifier& propertyNa
PropertySlot slot;
thisObject->getOwnPropertySlotVirtual(exec, propertyName, slot);
}
if (thisObject->jsExecutable()->isStrictMode()) {
if (propertyName == exec->propertyNames().arguments) {
throwTypeError(exec, StrictModeArgumentsAccessError);
return;
}
if (propertyName == exec->propertyNames().caller) {
throwTypeError(exec, StrictModeCallerAccessError);
return;
}
if (thisObject->jsExecutable()->isStrictMode() && (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().caller)) {
// This will trigger the property to be reified, if this is not already the case!
bool okay = thisObject->hasProperty(exec, propertyName);
ASSERT_UNUSED(okay, okay);
Base::put(thisObject, exec, propertyName, value, slot);
return;
}
if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length)
return;
......
......@@ -41,9 +41,6 @@ namespace JSC {
EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState*);
extern const char* StrictModeCallerAccessError;
extern const char* StrictModeArgumentsAccessError;
void createDescriptorForThrowingProperty(ExecState*, PropertyDescriptor&, const char* message);
class JSFunction : public JSNonFinalObject {
......
......@@ -47,6 +47,7 @@
#include "ErrorPrototype.h"
#include "FunctionConstructor.h"
#include "FunctionPrototype.h"
#include "GetterSetter.h"
#include "JSBoundFunction.h"
#include "JSFunction.h"
#include "JSGlobalObjectFunctions.h"
......@@ -305,6 +306,15 @@ void JSGlobalObject::reset(JSValue prototype)
resetPrototype(exec->globalData(), prototype);
}
void JSGlobalObject::createThrowTypeError(ExecState* exec)
{
JSFunction* thrower = JSFunction::create(exec, this, 0, Identifier(), globalFuncThrowTypeError);
GetterSetter* getterSetter = GetterSetter::create(exec);
getterSetter->setGetter(exec->globalData(), thrower);
getterSetter->setSetter(exec->globalData(), thrower);
m_throwTypeErrorGetterSetter.set(exec->globalData(), this, getterSetter);
}
// Set prototype, and also insert the object prototype at the end of the chain.
void JSGlobalObject::resetPrototype(JSGlobalData& globalData, JSValue prototype)
{
......@@ -339,6 +349,7 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
visitIfNeeded(visitor, &thisObject->m_evalFunction);
visitIfNeeded(visitor, &thisObject->m_callFunction);
visitIfNeeded(visitor, &thisObject->m_applyFunction);
visitIfNeeded(visitor, &thisObject->m_throwTypeErrorGetterSetter);
visitIfNeeded(visitor, &thisObject->m_objectPrototype);
visitIfNeeded(visitor, &thisObject->m_functionPrototype);
......
......@@ -42,6 +42,7 @@ namespace JSC {
class Debugger;
class ErrorConstructor;
class FunctionPrototype;
class GetterSetter;
class GlobalCodeBlock;
class NativeErrorConstructor;
class ProgramCodeBlock;
......@@ -90,6 +91,7 @@ namespace JSC {
WriteBarrier<JSFunction> m_evalFunction;
WriteBarrier<JSFunction> m_callFunction;
WriteBarrier<JSFunction> m_applyFunction;
WriteBarrier<GetterSetter> m_throwTypeErrorGetterSetter;
WriteBarrier<ObjectPrototype> m_objectPrototype;
WriteBarrier<FunctionPrototype> m_functionPrototype;
......@@ -212,6 +214,12 @@ namespace JSC {
JSFunction* evalFunction() const { return m_evalFunction.get(); }
JSFunction* callFunction() const { return m_callFunction.get(); }
JSFunction* applyFunction() const { return m_applyFunction.get(); }
GetterSetter* throwTypeErrorGetterSetter(ExecState* exec)
{
if (!m_throwTypeErrorGetterSetter)
createThrowTypeError(exec);
return m_throwTypeErrorGetterSetter.get();
}
ObjectPrototype* objectPrototype() const { return m_objectPrototype.get(); }
FunctionPrototype* functionPrototype() const { return m_functionPrototype.get(); }
......@@ -321,6 +329,8 @@ namespace JSC {
void init(JSObject* thisValue);
void reset(JSValue prototype);
void createThrowTypeError(ExecState*);
void setRegisters(WriteBarrier<Unknown>* registers, PassOwnArrayPtr<WriteBarrier<Unknown> > registerArray, size_t count);
static void clearRareData(JSCell*);
};
......
......@@ -594,4 +594,9 @@ EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec)
return JSValue::encode(jsString(exec, builder.toUString()));
}
EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeError(ExecState* exec)
{
return throwVMTypeError(exec);
}
} // namespace JSC
......@@ -47,6 +47,7 @@ namespace JSC {
EncodedJSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState*);
EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState*);
EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState*);
EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeError(ExecState*);
static const double mantissaOverflowLowerBound = 9007199254740992.0;
double parseIntOverflow(const char*, int length, int radix);
......
......@@ -396,6 +396,28 @@ void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSO
getterSetter->setGetter(globalData, getterFunction);
}
void JSObject::initializeGetterSetterProperty(ExecState* exec, const Identifier& propertyName, GetterSetter* getterSetter, unsigned attributes)
{
// Set an inital property on an object; the property must not already exist & the attribute flags must be set correctly.
ASSERT(structure()->get(exec->globalData(), propertyName) == WTF::notFound);
ASSERT(static_cast<bool>(getterSetter->getter()) == static_cast<bool>(attributes & Getter));
ASSERT(static_cast<bool>(getterSetter->setter()) == static_cast<bool>(attributes & Setter));
JSGlobalData& globalData = exec->globalData();
PutPropertySlot slot;
putDirectInternal(globalData, propertyName, getterSetter, attributes | Getter, true, slot, 0);
// putDirect will change our Structure if we add a new property. For
// getters and setters, though, we also need to change our Structure
// if we override an existing non-getter or non-setter.
if (slot.type() != PutPropertySlot::NewProperty) {
if (!structure()->isDictionary())
setStructure(exec->globalData(), Structure::getterSetterTransition(globalData, structure()));
}
structure()->setHasGetterSetterProperties(true);
}
void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes)
{
if (propertyName == exec->propertyNames().underscoreProto) {
......
......@@ -45,7 +45,8 @@ namespace JSC {
return value.asCell();
return 0;
}
class GetterSetter;
class HashEntry;
class InternalFunction;
class MarkedBlock;
......@@ -190,6 +191,7 @@ namespace JSC {
void putUndefinedAtDirectOffset(size_t offset) { propertyStorage()[offset].setUndefined(); }
void fillGetterPropertySlot(PropertySlot&, WriteBarrierBase<Unknown>* location);
void initializeGetterSetterProperty(ExecState*, const Identifier&, GetterSetter*, unsigned attributes);
virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes = 0);
virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes = 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment