summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFilip Pizlo <fpizlo@apple.com>2014-09-25 11:29:50 +0200
committerAllan Sandfeld Jensen <allan.jensen@digia.com>2014-09-26 14:02:28 +0200
commitcf26dce826a7a6a2d14735c193c53b9103c4a369 (patch)
treeed8b4e6bf5ba782c2afe3d710f2b8f421b4edfd0
parente869050a9ea37662847811fb0f67c33b25cf2d1f (diff)
compileMakeRope does not emit necessary bounds checks
https://bugs.webkit.org/show_bug.cgi?id=130684 <rdar://problem/16398388> Reviewed by Oliver Hunt. Add string length bounds checks in a bunch of places. We should never allow a string to have a length greater than 2^31-1 because it's not clear that the language has semantics for it and because there is code that assumes that this cannot happen. Also add a bunch of tests to that effect to cover the various ways in which this was previously allowed to happen. * dfg/DFGOperations.cpp: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileMakeRope): * ftl/FTLLowerDFGToLLVM.cpp: (JSC::FTL::LowerDFGToLLVM::compileMakeRope): * runtime/JSString.cpp: (JSC::JSRopeString::RopeBuilder::expand): * runtime/JSString.h: (JSC::JSString::create): (JSC::JSRopeString::RopeBuilder::append): (JSC::JSRopeString::RopeBuilder::release): (JSC::JSRopeString::append): * runtime/Operations.h: (JSC::jsString): (JSC::jsStringFromRegisterArray): (JSC::jsStringFromArguments): * runtime/StringPrototype.cpp: (JSC::stringProtoFuncIndexOf): (JSC::stringProtoFuncSlice): (JSC::stringProtoFuncSubstring): (JSC::stringProtoFuncToLowerCase): * tests/stress/make-large-string-jit-strcat.js: Added. (foo): * tests/stress/make-large-string-jit.js: Added. (foo): * tests/stress/make-large-string-strcat.js: Added. * tests/stress/make-large-string.js: Added. Change-Id: If01dd2a2d2daa3d209eddf0213d2b391e94f54a0 git-svn-id: http://svn.webkit.org/repository/webkit/trunk@167336 268f45cc-cd09-0410-ab3c-d52691b4dbfc Reviewed-by: Jocelyn Turcotte <jocelyn.turcotte@digia.com>
-rw-r--r--Source/JavaScriptCore/dfg/DFGOperations.cpp13
-rw-r--r--Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp18
-rw-r--r--Source/JavaScriptCore/runtime/JSString.cpp1
-rw-r--r--Source/JavaScriptCore/runtime/JSString.h14
-rw-r--r--Source/JavaScriptCore/runtime/Operations.h33
-rw-r--r--Source/JavaScriptCore/runtime/StringPrototype.cpp4
6 files changed, 60 insertions, 23 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp
index 29a0b2b61..1305c0a5d 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp
@@ -1644,6 +1644,11 @@ JSCell* DFG_OPERATION operationMakeRope2(ExecState* exec, JSString* left, JSStri
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
+ if (static_cast<int32_t>(left->length() + right->length()) < 0) {
+ throwOutOfMemoryError(exec);
+ return 0;
+ }
+
return JSRopeString::create(vm, left, right);
}
@@ -1652,6 +1657,14 @@ JSCell* DFG_OPERATION operationMakeRope3(ExecState* exec, JSString* a, JSString*
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
+ Checked<int32_t, RecordOverflow> length = a->length();
+ length += b->length();
+ length += c->length();
+ if (length.hasOverflowed()) {
+ throwOutOfMemoryError(exec);
+ return 0;
+ }
+
return JSRopeString::create(vm, a, b, c);
}
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index 1348f94be..57a338e03 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -3204,12 +3204,28 @@ void SpeculativeJIT::compileMakeRope(Node* node)
m_jit.storePtr(TrustedImmPtr(0), JITCompiler::Address(resultGPR, JSRopeString::offsetOfFibers() + sizeof(WriteBarrier<JSString>) * i));
m_jit.load32(JITCompiler::Address(opGPRs[0], JSString::offsetOfFlags()), scratchGPR);
m_jit.load32(JITCompiler::Address(opGPRs[0], JSString::offsetOfLength()), allocatorGPR);
+ if (!ASSERT_DISABLED) {
+ JITCompiler::Jump ok = m_jit.branch32(
+ JITCompiler::GreaterThanOrEqual, allocatorGPR, TrustedImm32(0));
+ m_jit.breakpoint();
+ ok.link(&m_jit);
+ }
for (unsigned i = 1; i < numOpGPRs; ++i) {
m_jit.and32(JITCompiler::Address(opGPRs[i], JSString::offsetOfFlags()), scratchGPR);
- m_jit.add32(JITCompiler::Address(opGPRs[i], JSString::offsetOfLength()), allocatorGPR);
+ speculationCheck(
+ Uncountable, JSValueSource(), 0,
+ m_jit.branchAdd32(
+ JITCompiler::Overflow,
+ JITCompiler::Address(opGPRs[i], JSString::offsetOfLength()), allocatorGPR));
}
m_jit.and32(JITCompiler::TrustedImm32(JSString::Is8Bit), scratchGPR);
m_jit.store32(scratchGPR, JITCompiler::Address(resultGPR, JSString::offsetOfFlags()));
+ if (!ASSERT_DISABLED) {
+ JITCompiler::Jump ok = m_jit.branch32(
+ JITCompiler::GreaterThanOrEqual, allocatorGPR, TrustedImm32(0));
+ m_jit.breakpoint();
+ ok.link(&m_jit);
+ }
m_jit.store32(allocatorGPR, JITCompiler::Address(resultGPR, JSString::offsetOfLength()));
switch (numOpGPRs) {
diff --git a/Source/JavaScriptCore/runtime/JSString.cpp b/Source/JavaScriptCore/runtime/JSString.cpp
index 86704d715..6f0b09d13 100644
--- a/Source/JavaScriptCore/runtime/JSString.cpp
+++ b/Source/JavaScriptCore/runtime/JSString.cpp
@@ -40,6 +40,7 @@ void JSRopeString::RopeBuilder::expand()
{
ASSERT(m_index == JSRopeString::s_maxInternalRopeLength);
JSString* jsString = m_jsString;
+ RELEASE_ASSERT(jsString);
m_jsString = jsStringBuilder(&m_vm);
m_index = 0;
append(jsString);
diff --git a/Source/JavaScriptCore/runtime/JSString.h b/Source/JavaScriptCore/runtime/JSString.h
index 855de974d..fc383b2f4 100644
--- a/Source/JavaScriptCore/runtime/JSString.h
+++ b/Source/JavaScriptCore/runtime/JSString.h
@@ -1,7 +1,7 @@
/*
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
- * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2014 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -121,7 +121,8 @@ public:
static JSString* create(VM& vm, PassRefPtr<StringImpl> value)
{
ASSERT(value);
- size_t length = value->length();
+ int32_t length = value->length();
+ RELEASE_ASSERT(length >= 0);
size_t cost = value->cost();
JSString* newString = new (NotNull, allocateCell<JSString>(vm.heap)) JSString(vm, value);
newString->finishCreation(vm, length, cost);
@@ -226,15 +227,21 @@ class JSRopeString : public JSString {
{
}
- void append(JSString* jsString)
+ bool append(JSString* jsString)
{
if (m_index == JSRopeString::s_maxInternalRopeLength)
expand();
+ if (static_cast<int32_t>(m_jsString->length() + jsString->length()) < 0) {
+ m_jsString = 0;
+ return false;
+ }
m_jsString->append(m_vm, m_index++, jsString);
+ return true;
}
JSRopeString* release()
{
+ RELEASE_ASSERT(m_jsString);
JSRopeString* tmp = m_jsString;
m_jsString = 0;
return tmp;
@@ -284,6 +291,7 @@ private:
{
m_fibers[index].set(vm, this, jsString);
m_length += jsString->m_length;
+ RELEASE_ASSERT(static_cast<int32_t>(m_length) >= 0);
setIs8Bit(is8Bit() && jsString->is8Bit());
}
diff --git a/Source/JavaScriptCore/runtime/Operations.h b/Source/JavaScriptCore/runtime/Operations.h
index afac13000..e628662e0 100644
--- a/Source/JavaScriptCore/runtime/Operations.h
+++ b/Source/JavaScriptCore/runtime/Operations.h
@@ -42,13 +42,13 @@ ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2)
{
VM& vm = exec->vm();
- unsigned length1 = s1->length();
+ int32_t length1 = s1->length();
if (!length1)
return s2;
- unsigned length2 = s2->length();
+ int32_t length2 = s2->length();
if (!length2)
return s1;
- if ((length1 + length2) < length1)
+ if ((length1 + length2) < 0)
return throwOutOfMemoryError(exec);
return JSRopeString::create(vm, s1, s2);
@@ -58,9 +58,13 @@ ALWAYS_INLINE JSValue jsString(ExecState* exec, const String& u1, const String&
{
VM* vm = &exec->vm();
- unsigned length1 = u1.length();
- unsigned length2 = u2.length();
- unsigned length3 = u3.length();
+ int32_t length1 = u1.length();
+ int32_t length2 = u2.length();
+ int32_t length3 = u3.length();
+
+ if (length1 < 0 || length2 < 0 || length3 < 0)
+ return throwOutOfMemoryError(exec);
+
if (!length1)
return jsString(exec, jsString(vm, u2), jsString(vm, u3));
if (!length2)
@@ -68,9 +72,9 @@ ALWAYS_INLINE JSValue jsString(ExecState* exec, const String& u1, const String&
if (!length3)
return jsString(exec, jsString(vm, u1), jsString(vm, u2));
- if ((length1 + length2) < length1)
+ if ((length1 + length2) < 0)
return throwOutOfMemoryError(exec);
- if ((length1 + length2 + length3) < length3)
+ if ((length1 + length2 + length3) < 0)
return throwOutOfMemoryError(exec);
return JSRopeString::create(exec->vm(), jsString(vm, u1), jsString(vm, u2), jsString(vm, u3));
@@ -81,15 +85,11 @@ ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned coun
VM* vm = &exec->vm();
JSRopeString::RopeBuilder ropeBuilder(*vm);
- unsigned oldLength = 0;
-
for (unsigned i = 0; i < count; ++i) {
JSValue v = strings[i].jsValue();
- ropeBuilder.append(v.toString(exec));
- if (ropeBuilder.length() < oldLength) // True for overflow
+ if (!ropeBuilder.append(v.toString(exec)))
return throwOutOfMemoryError(exec);
- oldLength = ropeBuilder.length();
}
return ropeBuilder.release();
@@ -101,15 +101,10 @@ ALWAYS_INLINE JSValue jsStringFromArguments(ExecState* exec, JSValue thisValue)
JSRopeString::RopeBuilder ropeBuilder(*vm);
ropeBuilder.append(thisValue.toString(exec));
- unsigned oldLength = 0;
-
for (unsigned i = 0; i < exec->argumentCount(); ++i) {
JSValue v = exec->argument(i);
- ropeBuilder.append(v.toString(exec));
-
- if (ropeBuilder.length() < oldLength) // True for overflow
+ if (!ropeBuilder.append(v.toString(exec)))
return throwOutOfMemoryError(exec);
- oldLength = ropeBuilder.length();
}
return ropeBuilder.release();
diff --git a/Source/JavaScriptCore/runtime/StringPrototype.cpp b/Source/JavaScriptCore/runtime/StringPrototype.cpp
index c422fd17b..2e9baba73 100644
--- a/Source/JavaScriptCore/runtime/StringPrototype.cpp
+++ b/Source/JavaScriptCore/runtime/StringPrototype.cpp
@@ -761,6 +761,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
else {
unsigned pos;
int len = s.length();
+ RELEASE_ASSERT(len >= 0);
if (a1.isUInt32())
pos = std::min<uint32_t>(a1.asUInt32(), len);
else {
@@ -904,6 +905,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec)
return throwVMTypeError(exec);
String s = thisValue.toString(exec)->value(exec);
int len = s.length();
+ RELEASE_ASSERT(len >= 0);
JSValue a0 = exec->argument(0);
JSValue a1 = exec->argument(1);
@@ -1216,6 +1218,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
JSValue a0 = exec->argument(0);
JSValue a1 = exec->argument(1);
int len = jsString->length();
+ RELEASE_ASSERT(len >= 0);
double start = a0.toNumber(exec);
double end;
@@ -1253,6 +1256,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec)
int sSize = s.length();
if (!sSize)
return JSValue::encode(sVal);
+ RELEASE_ASSERT(sSize >= 0);
StringImpl* ourImpl = s.impl();
RefPtr<StringImpl> lower = ourImpl->lower();