Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qv4arraybuffer.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5// Security note: ArrayBuffer can be used with arbitrary binary data stored in QBA
6// and requires some care (we e.g. don't want to create a corrupted QBA in asByteArray)
7
10#include "qv4dataview_p.h"
11#include "qv4symbol_p.h"
12
13using namespace QV4;
14
19
20void Heap::SharedArrayBufferCtor::init(QV4::ExecutionEngine *engine)
21{
22 Heap::FunctionObject::init(engine, QStringLiteral("SharedArrayBuffer"));
23}
24
25void Heap::ArrayBufferCtor::init(QV4::ExecutionEngine *engine)
26{
27 Heap::FunctionObject::init(engine, QStringLiteral("ArrayBuffer"));
28}
29
30ReturnedValue SharedArrayBufferCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
31{
32 Scope scope(f);
33 if (newTarget->isUndefined())
34 return scope.engine->throwTypeError();
35
36 const double len = argc ? argv[0].toInteger() : 0;
37 if (scope.hasException())
38 return Encode::undefined();
39 if (len < 0 || len >= std::numeric_limits<int>::max())
40 return scope.engine->throwRangeError(QStringLiteral("SharedArrayBuffer: Invalid length."));
41
42 Scoped<SharedArrayBuffer> a(
43 scope, scope.engine->memoryManager->allocate<SharedArrayBuffer>(size_t(len)));
44 if (scope.hasException())
45 return Encode::undefined();
46
47 return a->asReturnedValue();
48}
49
50ReturnedValue SharedArrayBufferCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
51{
52 return f->engine()->throwTypeError();
53}
54
55
56ReturnedValue ArrayBufferCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
57{
58 ExecutionEngine *v4 = f->engine();
59 Scope scope(v4);
60
61 ScopedValue l(scope, argc ? argv[0] : Value::undefinedValue());
62 double dl = l->toInteger();
63 if (v4->hasException)
64 return Encode::undefined();
65 uint len = (uint)qBound(0., dl, (double)UINT_MAX);
66 if (len != dl)
67 return v4->throwRangeError(QLatin1String("ArrayBuffer constructor: invalid length"));
68
69 Scoped<ArrayBuffer> a(scope, v4->newArrayBuffer(len));
70 if (newTarget->heapObject() != f->heapObject() && newTarget->isFunctionObject()) {
71 const FunctionObject *nt = static_cast<const FunctionObject *>(newTarget);
72 ScopedObject o(scope, nt->protoProperty());
73 if (o)
74 a->setPrototypeOf(o);
75 }
76 if (scope.hasException())
77 return Encode::undefined();
78
79 return a->asReturnedValue();
80}
81
82ReturnedValue ArrayBufferCtor::method_isView(const FunctionObject *, const Value *, const Value *argv, int argc)
83{
84 if (argc < 1)
85 return Encode(false);
86
87 if (argv[0].as<TypedArray>() ||
88 argv[0].as<DataView>())
89 return Encode(true);
90
91 return Encode(false);
92}
93
94void Heap::SharedArrayBuffer::init(size_t length)
95{
96 auto adapt = [](auto maybePair) {
97 auto [header, ptr] = maybePair;
98 return std::pair(header, ptr);
99 };
100 Object::init();
101 std::pair<QTypedArrayData<char> *, char *> pair;
102 if (length < UINT_MAX)
103 pair = adapt(QTypedArrayData<char>::allocate(length + 1));
104 if (!pair.first) {
105 new (&arrayDataPointerStorage) QArrayDataPointer<char>();
106 internalClass->engine->throwRangeError(QStringLiteral("ArrayBuffer: out of memory"));
107 return;
108 }
109 auto data = new (&arrayDataPointerStorage) QArrayDataPointer<char>{
110 pair.first, pair.second, qsizetype(length) };
111
112 // can't use appendInitialize() because we want to set the terminating '\0'
113 memset(data->data(), 0, length + 1);
114 isShared = true;
115}
116
117void Heap::SharedArrayBuffer::init(const QByteArray& array)
118{
119 Object::init();
120 new (&arrayDataPointerStorage) QArrayDataPointer<char>(*const_cast<QByteArray &>(array).data_ptr());
121 isShared = true;
122}
123
124void Heap::SharedArrayBuffer::destroy()
125{
126 arrayDataPointer().~QArrayDataPointer();
127 Object::destroy();
128}
129
130QByteArray ArrayBuffer::asByteArray() const
131{
132 return QByteArray(constArrayData(), arrayDataLength());
133}
134
135void ArrayBuffer::detach()
136{
137 detachArrayData();
138}
139
140
141void SharedArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor)
142{
143 Scope scope(engine);
144 ScopedObject o(scope);
145 ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
146 ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
147 ctor->addSymbolSpecies();
148
149 defineDefaultProperty(engine->id_constructor(), (o = ctor));
150 defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
151 defineDefaultProperty(QStringLiteral("slice"), method_slice, 2);
152 ScopedString name(scope, engine->newString(QStringLiteral("SharedArrayBuffer")));
153 defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
154}
155
156ReturnedValue SharedArrayBufferPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
157{
158 const SharedArrayBuffer *a = thisObject->as<SharedArrayBuffer>();
159 if (!a || a->hasDetachedArrayData() || !a->isSharedArrayBuffer())
160 return b->engine()->throwTypeError();
161
162 return Encode(a->arrayDataLength());
163}
164
165ReturnedValue SharedArrayBufferPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
166{
167 return slice(b, thisObject, argv, argc, true);
168}
169
170ReturnedValue SharedArrayBufferPrototype::slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc, bool shared)
171{
172 Scope scope(b);
173 const SharedArrayBuffer *a = thisObject->as<SharedArrayBuffer>();
174 if (!a || a->hasDetachedArrayData() || (a->isSharedArrayBuffer() != shared))
175 return scope.engine->throwTypeError();
176
177 const uint aDataLength = a->arrayDataLength();
178
179 double start = argc > 0 ? argv[0].toInteger() : 0;
180 double end = (argc < 2 || argv[1].isUndefined()) ? aDataLength : argv[1].toInteger();
181 if (scope.hasException())
182 return QV4::Encode::undefined();
183
184 double first = (start < 0) ? qMax(aDataLength + start, 0.) : qMin(start, double(aDataLength));
185 double final = (end < 0) ? qMax(aDataLength + end, 0.) : qMin(end, double(aDataLength));
186
187 const FunctionObject *constructor = a->speciesConstructor(scope, shared ? scope.engine->sharedArrayBufferCtor() : scope.engine->arrayBufferCtor());
188 if (!constructor)
189 return scope.engine->throwTypeError();
190
191 double newLen = qMax(final - first, 0.);
192 ScopedValue argument(scope, QV4::Encode(newLen));
193 QV4::Scoped<SharedArrayBuffer> newBuffer(scope, constructor->callAsConstructor(argument, 1));
194 if (!newBuffer || newBuffer->arrayDataLength() < newLen ||
195 newBuffer->hasDetachedArrayData() || (newBuffer->isSharedArrayBuffer() != shared) ||
196 newBuffer->sameValue(*a) ||
197 a->hasDetachedArrayData())
198 return scope.engine->throwTypeError();
199
200 memcpy(newBuffer->arrayData(), a->constArrayData() + (uint)first, newLen);
201 return newBuffer->asReturnedValue();
202}
203
204
205void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor)
206{
207 Scope scope(engine);
208 ScopedObject o(scope);
209 ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
210 ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
211 ctor->defineDefaultProperty(QStringLiteral("isView"), ArrayBufferCtor::method_isView, 1);
212 ctor->addSymbolSpecies();
213
214 defineDefaultProperty(engine->id_constructor(), (o = ctor));
215 defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
216 defineDefaultProperty(QStringLiteral("slice"), method_slice, 2);
217 defineDefaultProperty(QStringLiteral("toString"), method_toString, 0);
218 ScopedString name(scope, engine->newString(QStringLiteral("ArrayBuffer")));
219 defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
220}
221
222ReturnedValue ArrayBufferPrototype::method_get_byteLength(const FunctionObject *f, const Value *thisObject, const Value *, int)
223{
224 const ArrayBuffer *a = thisObject->as<ArrayBuffer>();
225 if (!a || a->isSharedArrayBuffer())
226 return f->engine()->throwTypeError();
227
228 if (a->hasDetachedArrayData())
229 return Encode(0);
230
231 return Encode(a->arrayDataLength());
232}
233
234ReturnedValue ArrayBufferPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
235{
236 return slice(b, thisObject, argv, argc, false);
237}
238
239ReturnedValue ArrayBufferPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
240{
241 ExecutionEngine *v4 = b->engine();
242 const ArrayBuffer *a = thisObject->as<ArrayBuffer>();
243 if (!a)
245 return Encode(v4->newString(QString::fromUtf8(a->asByteArray())));
246}
DEFINE_OBJECT_VTABLE(SharedArrayBufferCtor)
DEFINE_OBJECT_VTABLE(ArrayBufferCtor)
DEFINE_OBJECT_VTABLE(ArrayBuffer)
DEFINE_OBJECT_VTABLE(SharedArrayBuffer)
#define RETURN_UNDEFINED()