diff options
author | Lars Knoll <lars.knoll@digia.com> | 2014-06-12 14:35:53 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@digia.com> | 2014-09-01 12:23:46 +0200 |
commit | 210475565969ca5381174016b47cd32ddc96eaed (patch) | |
tree | 59d76471cc7d602c3a8bf3893fa87adfd3383097 | |
parent | cf44ee7761f2e4175f9193b42ee7296a2f3b694a (diff) |
Fix crashes when calling Array.sort with imperfect sort functions
We can't use std::sort to implement Array.sort. The reason is that
std::sort expects a conformant compare function, and can do weird
things (esp. crash) when the sort function isn't conformant.
Falling back to qSort is not possible, as the method has been
deprecated. So add a copy of the qSort implementation here, and
use that one instead.
Fix the sortint test in tst_qqmlecmascript to have a consistent
sort function for strings, as the result of calling sort is
otherwise undefined according to the ecma standard.
Task-number: QTBUG-39072
Change-Id: I0602b3aa1ffa4de5006da58396f166805cf4a5e2
Reviewed-by: Robin Burchell <robin.burchell@viroteck.net>
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
-rw-r--r-- | src/qml/jsruntime/qv4arraydata.cpp | 56 | ||||
-rw-r--r-- | tests/auto/qml/qjsengine/tst_qjsengine.cpp | 19 | ||||
-rw-r--r-- | tests/auto/qml/qqmlecmascript/data/sequenceSort.qml | 2 |
3 files changed, 75 insertions, 2 deletions
diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index 7d76a10ad6..9627848102 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -674,6 +674,60 @@ bool ArrayElementLessThan::operator()(Value v1, Value v2) const return p1s->toQString() < p2s->toQString(); } +template <typename RandomAccessIterator, typename T, typename LessThan> +void sortHelper(RandomAccessIterator start, RandomAccessIterator end, const T &t, LessThan lessThan) +{ +top: + int span = int(end - start); + if (span < 2) + return; + + --end; + RandomAccessIterator low = start, high = end - 1; + RandomAccessIterator pivot = start + span / 2; + + if (lessThan(*end, *start)) + qSwap(*end, *start); + if (span == 2) + return; + + if (lessThan(*pivot, *start)) + qSwap(*pivot, *start); + if (lessThan(*end, *pivot)) + qSwap(*end, *pivot); + if (span == 3) + return; + + qSwap(*pivot, *end); + + while (low < high) { + while (low < high && lessThan(*low, *end)) + ++low; + + while (high > low && lessThan(*end, *high)) + --high; + + if (low < high) { + qSwap(*low, *high); + ++low; + --high; + } else { + break; + } + } + + if (lessThan(*low, *end)) + ++low; + + qSwap(*end, *low); + sortHelper(start, low, t, lessThan); + + start = low + 1; + ++end; + goto top; +} + + void ArrayData::sort(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn, uint len) { if (!len) @@ -765,7 +819,7 @@ void ArrayData::sort(ExecutionContext *context, ObjectRef thisObject, const Valu ArrayElementLessThan lessThan(context, thisObject, comparefn); Value *begin = thisObject->arrayData->data; - std::sort(begin, begin + len, lessThan); + sortHelper(begin, begin + len, *begin, lessThan); #ifdef CHECK_SPARSE_ARRAYS thisObject->initSparseArray(); diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 51cd69998a..4b47e55b55 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -135,6 +135,7 @@ private slots: void reentrancy_objectCreation(); void jsIncDecNonObjectProperty(); void JSONparse(); + void arraySort(); void qRegExpInport_data(); void qRegExpInport(); @@ -2729,6 +2730,24 @@ void tst_QJSEngine::JSONparse() QVERIFY(ret.isObject()); } +void tst_QJSEngine::arraySort() +{ + // tests that calling Array.sort with a bad sort function doesn't cause issues + // Using std::sort is e.g. not safe when used with a bad sort function and causes + // crashes + QJSEngine eng; + eng.evaluate("function crashMe() {" + " var data = [];" + " for (var i = 0; i < 50; i++) {" + " data[i] = 'whatever';" + " }" + " data.sort(function(a, b) {" + " return -1;" + " });" + "}" + "crashMe();"); +} + static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; } void tst_QJSEngine::qRegExpInport_data() diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceSort.qml b/tests/auto/qml/qqmlecmascript/data/sequenceSort.qml index 5e2892a31d..b130408c18 100644 --- a/tests/auto/qml/qqmlecmascript/data/sequenceSort.qml +++ b/tests/auto/qml/qqmlecmascript/data/sequenceSort.qml @@ -23,7 +23,7 @@ Item { } function compareStrings(a, b) { - return (a < b) ? 1 : -1; + return (a == b) ? 0 : ((a < b) ? 1 : -1); } function compareNumbers(a, b) { |