1// Copyright (C) 2020 Intel Corporation.
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#ifndef QCBORVALUE_P_H
6#define QCBORVALUE_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API.
13// This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
19#include "qcborvalue.h"
20
21#if QT_CONFIG(cborstreamreader)
22# include "qcborstreamreader.h"
23#endif
24
25#include <private/qglobal_p.h>
26#include <private/qstringconverter_p.h>
27
28#include <math.h>
29
30QT_BEGIN_NAMESPACE
31
32namespace QtCbor {
33enum class Comparison {
34 ForEquality,
35 ForOrdering,
36};
37
38struct Undefined {};
39struct Element
40{
41 enum ValueFlag : quint32 {
42 IsContainer = 0x0001,
43 HasByteData = 0x0002,
44 StringIsUtf16 = 0x0004,
45 StringIsAscii = 0x0008
46 };
47 Q_DECLARE_FLAGS(ValueFlags, ValueFlag)
48
49 union {
50 qint64 value;
51 QCborContainerPrivate *container;
52 };
53 QCborValue::Type type;
54 ValueFlags flags = {};
55
56 Element(qint64 v = 0, QCborValue::Type t = QCborValue::Undefined, ValueFlags f = {})
57 : value(v), type(t), flags(f)
58 {}
59
60 Element(QCborContainerPrivate *d, QCborValue::Type t, ValueFlags f = {})
61 : container(d), type(t), flags(f | IsContainer)
62 {}
63
64 double fpvalue() const
65 {
66 double d;
67 memcpy(dest: &d, src: &value, n: sizeof(d));
68 return d;
69 }
70};
71Q_DECLARE_OPERATORS_FOR_FLAGS(Element::ValueFlags)
72static_assert(sizeof(Element) == 16);
73
74struct ByteData
75{
76 QByteArray::size_type len;
77
78 const char *byte() const { return reinterpret_cast<const char *>(this + 1); }
79 char *byte() { return reinterpret_cast<char *>(this + 1); }
80 const QChar *utf16() const { return reinterpret_cast<const QChar *>(this + 1); }
81 QChar *utf16() { return reinterpret_cast<QChar *>(this + 1); }
82
83 QByteArray toByteArray() const { return QByteArray(byte(), len); }
84 QString toString() const { return QString(utf16(), len / 2); }
85 QString toUtf8String() const { return QString::fromUtf8(utf8: byte(), size: len); }
86
87 QByteArray asByteArrayView() const { return QByteArray::fromRawData(data: byte(), size: len); }
88 QLatin1StringView asLatin1() const { return {byte(), len}; }
89 QUtf8StringView asUtf8StringView() const { return QUtf8StringView(byte(), len); }
90 QStringView asStringView() const{ return QStringView(utf16(), len / 2); }
91 QString asQStringRaw() const { return QString::fromRawData(utf16(), size: len / 2); }
92};
93static_assert(std::is_trivially_default_constructible<ByteData>::value);
94static_assert(std::is_trivially_copyable<ByteData>::value);
95static_assert(std::is_standard_layout<ByteData>::value);
96} // namespace QtCbor
97
98Q_DECLARE_TYPEINFO(QtCbor::Element, Q_PRIMITIVE_TYPE);
99
100class QCborContainerPrivate : public QSharedData
101{
102 friend class QExplicitlySharedDataPointer<QCborContainerPrivate>;
103 ~QCborContainerPrivate();
104
105public:
106 QCborContainerPrivate() = default;
107 QCborContainerPrivate(const QCborContainerPrivate &) = default;
108 QCborContainerPrivate(QCborContainerPrivate &&) = default;
109 QCborContainerPrivate &operator=(const QCborContainerPrivate &) = delete;
110 QCborContainerPrivate &operator=(QCborContainerPrivate &&) = delete;
111
112 enum ContainerDisposition { CopyContainer, MoveContainer };
113
114 QByteArray::size_type usedData = 0;
115 QByteArray data;
116 QList<QtCbor::Element> elements;
117
118 void deref() { if (!ref.deref()) delete this; }
119 void compact();
120 static QCborContainerPrivate *clone(QCborContainerPrivate *d, qsizetype reserved = -1);
121 static QCborContainerPrivate *detach(QCborContainerPrivate *d, qsizetype reserved);
122 static QCborContainerPrivate *grow(QCborContainerPrivate *d, qsizetype index);
123
124 static qptrdiff addByteDataImpl(QByteArray &target, QByteArray::size_type &targetUsed,
125 const char *block, qsizetype len)
126 {
127 // This function does not do overflow checking, since the len parameter
128 // is expected to be trusted. There's another version of this function
129 // in decodeStringFromCbor(), which checks.
130
131 qptrdiff offset = target.size();
132
133 // align offset
134 offset += alignof(QtCbor::ByteData) - 1;
135 offset &= ~(alignof(QtCbor::ByteData) - 1);
136
137 qptrdiff increment = qptrdiff(sizeof(QtCbor::ByteData)) + len;
138
139 targetUsed += increment;
140 target.resize(size: offset + increment);
141
142 char *ptr = target.begin() + offset;
143 auto b = new (ptr) QtCbor::ByteData;
144 b->len = len;
145 if (block)
146 memcpy(dest: b->byte(), src: block, n: len);
147
148 return offset;
149 }
150
151 qptrdiff addByteData(const char *block, qsizetype len)
152 {
153 return addByteDataImpl(target&: data, targetUsed&: usedData, block, len);
154 }
155
156 const QtCbor::ByteData *byteData(QtCbor::Element e) const
157 {
158 if ((e.flags & QtCbor::Element::HasByteData) == 0)
159 return nullptr;
160
161 size_t offset = size_t(e.value);
162 Q_ASSERT((offset % alignof(QtCbor::ByteData)) == 0);
163 Q_ASSERT(offset + sizeof(QtCbor::ByteData) <= size_t(data.size()));
164
165 auto b = reinterpret_cast<const QtCbor::ByteData *>(data.constData() + offset);
166 Q_ASSERT(offset + sizeof(*b) + size_t(b->len) <= size_t(data.size()));
167 return b;
168 }
169 const QtCbor::ByteData *byteData(qsizetype idx) const
170 {
171 return byteData(e: elements.at(i: idx));
172 }
173
174 QCborContainerPrivate *containerAt(qsizetype idx, QCborValue::Type type) const
175 {
176 const QtCbor::Element &e = elements.at(i: idx);
177 if (e.type != type || (e.flags & QtCbor::Element::IsContainer) == 0)
178 return nullptr;
179 return e.container;
180 }
181
182 void replaceAt_complex(QtCbor::Element &e, const QCborValue &value, ContainerDisposition disp);
183 void replaceAt_internal(QtCbor::Element &e, const QCborValue &value, ContainerDisposition disp)
184 {
185 if (value.container)
186 return replaceAt_complex(e, value, disp);
187
188 e = { value.value_helper(), value.type() };
189 if (value.isContainer())
190 e.container = nullptr;
191 }
192 void replaceAt(qsizetype idx, const QCborValue &value, ContainerDisposition disp = CopyContainer)
193 {
194 QtCbor::Element &e = elements[idx];
195 if (e.flags & QtCbor::Element::IsContainer) {
196 e.container->deref();
197 e.container = nullptr;
198 e.flags = {};
199 } else if (auto b = byteData(e)) {
200 usedData -= b->len + sizeof(QtCbor::ByteData);
201 }
202 replaceAt_internal(e, value, disp);
203 }
204 void insertAt(qsizetype idx, const QCborValue &value, ContainerDisposition disp = CopyContainer)
205 {
206 replaceAt_internal(e&: *elements.insert(i: idx, t: {}), value, disp);
207 }
208
209 void append(QtCbor::Undefined)
210 {
211 elements.append(t: QtCbor::Element());
212 }
213 void append(qint64 value)
214 {
215 elements.append(t: QtCbor::Element(value , QCborValue::Integer));
216 }
217 void append(QCborTag tag)
218 {
219 elements.append(t: QtCbor::Element(qint64(tag), QCborValue::Tag));
220 }
221 void appendByteData(const char *data, qsizetype len, QCborValue::Type type,
222 QtCbor::Element::ValueFlags extraFlags = {})
223 {
224 elements.append(t: QtCbor::Element(addByteData(block: data, len), type,
225 QtCbor::Element::HasByteData | extraFlags));
226 }
227 void appendAsciiString(const QString &s);
228 void appendAsciiString(const char *str, qsizetype len)
229 {
230 appendByteData(data: str, len, type: QCborValue::String, extraFlags: QtCbor::Element::StringIsAscii);
231 }
232 void appendUtf8String(const char *str, qsizetype len)
233 {
234 appendByteData(data: str, len, type: QCborValue::String);
235 }
236 void append(QLatin1StringView s)
237 {
238 if (!QtPrivate::isAscii(s))
239 return appendNonAsciiString(s: QString(s));
240
241 // US-ASCII is a subset of UTF-8, so we can keep in 8-bit
242 appendByteData(data: s.latin1(), len: s.size(), type: QCborValue::String,
243 extraFlags: QtCbor::Element::StringIsAscii);
244 }
245 void appendAsciiString(QStringView s);
246 void appendNonAsciiString(QStringView s);
247
248 void append(const QString &s)
249 {
250 append(s: qToStringViewIgnoringNull(s));
251 }
252
253 void append(QStringView s)
254 {
255 if (QtPrivate::isAscii(s))
256 appendAsciiString(s);
257 else
258 appendNonAsciiString(s);
259 }
260 void append(const QCborValue &v)
261 {
262 insertAt(idx: elements.size(), value: v);
263 }
264 void append(QCborValue &&v)
265 {
266 insertAt(idx: elements.size(), value: v, disp: MoveContainer);
267 v.container = nullptr;
268 v.t = QCborValue::Undefined;
269 }
270
271 QByteArray byteArrayAt(qsizetype idx) const
272 {
273 const auto &e = elements.at(i: idx);
274 const auto data = byteData(e);
275 if (!data)
276 return QByteArray();
277 return data->toByteArray();
278 }
279 QString stringAt(qsizetype idx) const
280 {
281 const auto &e = elements.at(i: idx);
282 const auto data = byteData(e);
283 if (!data)
284 return QString();
285 if (e.flags & QtCbor::Element::StringIsUtf16)
286 return data->toString();
287 if (e.flags & QtCbor::Element::StringIsAscii)
288 return data->asLatin1();
289 return data->toUtf8String();
290 }
291 QAnyStringView anyStringViewAt(qsizetype idx) const
292 {
293 const auto &e = elements.at(i: idx);
294 const auto data = byteData(e);
295 if (!data)
296 return nullptr;
297 if (e.flags & QtCbor::Element::StringIsUtf16)
298 return data->asStringView();
299 if (e.flags & QtCbor::Element::StringIsAscii)
300 return data->asLatin1();
301 return data->asUtf8StringView();
302 }
303
304 static void resetValue(QCborValue &v)
305 {
306 v.container = nullptr;
307 }
308
309 static QCborValue makeValue(QCborValue::Type type, qint64 n, QCborContainerPrivate *d = nullptr,
310 ContainerDisposition disp = CopyContainer)
311 {
312 QCborValue result(type);
313 result.n = n;
314 result.container = d;
315 if (d && disp == CopyContainer)
316 d->ref.ref();
317 return result;
318 }
319
320 QCborValue valueAt(qsizetype idx) const
321 {
322 const auto &e = elements.at(i: idx);
323
324 if (e.flags & QtCbor::Element::IsContainer) {
325 if (e.type == QCborValue::Tag && e.container->elements.size() != 2) {
326 // invalid tags can be created due to incomplete parsing
327 return makeValue(type: QCborValue::Invalid, n: 0, d: nullptr);
328 }
329 return makeValue(type: e.type, n: -1, d: e.container);
330 } else if (e.flags & QtCbor::Element::HasByteData) {
331 return makeValue(type: e.type, n: idx, d: const_cast<QCborContainerPrivate *>(this));
332 }
333 return makeValue(type: e.type, n: e.value);
334 }
335 QCborValue extractAt_complex(QtCbor::Element e);
336 QCborValue extractAt(qsizetype idx)
337 {
338 QtCbor::Element e;
339 qSwap(value1&: e, value2&: elements[idx]);
340
341 if (e.flags & QtCbor::Element::IsContainer) {
342 if (e.type == QCborValue::Tag && e.container->elements.size() != 2) {
343 // invalid tags can be created due to incomplete parsing
344 e.container->deref();
345 return makeValue(type: QCborValue::Invalid, n: 0, d: nullptr);
346 }
347 return makeValue(type: e.type, n: -1, d: e.container, disp: MoveContainer);
348 } else if (e.flags & QtCbor::Element::HasByteData) {
349 return extractAt_complex(e);
350 }
351 return makeValue(type: e.type, n: e.value);
352 }
353
354 static QtCbor::Element elementFromValue(const QCborValue &value)
355 {
356 if (value.n >= 0 && value.container)
357 return value.container->elements.at(i: value.n);
358
359 QtCbor::Element e;
360 e.value = value.n;
361 e.type = value.t;
362 if (value.container) {
363 e.container = value.container;
364 e.flags = QtCbor::Element::IsContainer;
365 }
366 return e;
367 }
368
369 static int compareUtf8(const QtCbor::ByteData *b, QLatin1StringView s)
370 {
371 return QUtf8::compareUtf8(utf8: QByteArrayView(b->byte(), b->len), s);
372 }
373
374 static int compareUtf8(const QtCbor::ByteData *b, QStringView s)
375 {
376 return QUtf8::compareUtf8(utf8: QByteArrayView(b->byte(), b->len), utf16: s);
377 }
378
379 template<typename String>
380 int stringCompareElement(const QtCbor::Element &e, String s, QtCbor::Comparison mode) const
381 {
382 if (e.type != QCborValue::String)
383 return int(e.type) - int(QCborValue::String);
384
385 const QtCbor::ByteData *b = byteData(e);
386 if (!b)
387 return s.isEmpty() ? 0 : -1;
388
389 if (e.flags & QtCbor::Element::StringIsUtf16) {
390 if (mode == QtCbor::Comparison::ForEquality)
391 return b->asStringView() == s ? 0 : 1;
392 return b->asStringView().compare(s);
393 }
394 return compareUtf8(b, s);
395 }
396
397 template<typename String>
398 bool stringEqualsElement(const QtCbor::Element &e, String s) const
399 {
400 return stringCompareElement(e, s, QtCbor::Comparison::ForEquality) == 0;
401 }
402
403 template<typename String>
404 bool stringEqualsElement(qsizetype idx, String s) const
405 {
406 return stringEqualsElement(elements.at(i: idx), s);
407 }
408
409 static int compareElement_helper(const QCborContainerPrivate *c1, QtCbor::Element e1,
410 const QCborContainerPrivate *c2, QtCbor::Element e2,
411 QtCbor::Comparison mode) noexcept;
412 int compareElement(qsizetype idx, const QCborValue &value, QtCbor::Comparison mode) const
413 {
414 auto &e1 = elements.at(i: idx);
415 auto e2 = elementFromValue(value);
416 return compareElement_helper(c1: this, e1, c2: value.container, e2, mode);
417 }
418
419 void removeAt(qsizetype idx)
420 {
421 replaceAt(idx, value: {});
422 elements.remove(i: idx);
423 }
424
425 // doesn't apply to JSON
426 template <typename KeyType> QCborValueConstRef findCborMapKey(KeyType key)
427 {
428 qsizetype i = 0;
429 for ( ; i < elements.size(); i += 2) {
430 const auto &e = elements.at(i);
431 bool equals;
432 if constexpr (std::is_same_v<std::decay_t<KeyType>, QCborValue>) {
433 equals = (compareElement(idx: i, value: key, mode: QtCbor::Comparison::ForEquality) == 0);
434 } else if constexpr (std::is_integral_v<KeyType>) {
435 equals = (e.type == QCborValue::Integer && e.value == key);
436 } else {
437 // assume it's a string
438 equals = stringEqualsElement(i, key);
439 }
440 if (equals)
441 break;
442 }
443 return { this, i + 1 };
444 }
445 template <typename KeyType> static QCborValue findCborMapKey(const QCborValue &self, KeyType key)
446 {
447 if (self.isMap() && self.container) {
448 qsizetype idx = self.container->findCborMapKey(key).i;
449 if (idx < self.container->elements.size())
450 return self.container->valueAt(idx);
451 }
452 return QCborValue();
453 }
454 template <typename KeyType> static QCborValueRef
455 findOrAddMapKey(QCborContainerPrivate *container, KeyType key)
456 {
457 qsizetype size = 0;
458 qsizetype index = size + 1;
459 if (container) {
460 size = container->elements.size();
461 index = container->findCborMapKey<KeyType>(key).i; // returns size + 1 if not found
462 }
463 Q_ASSERT(index & 1);
464 Q_ASSERT((size & 1) == 0);
465
466 container = detach(d: container, reserved: qMax(a: index + 1, b: size));
467 Q_ASSERT(container);
468 Q_ASSERT((container->elements.size() & 1) == 0);
469
470 if (index >= size) {
471 container->append(key);
472 container->append(v: QCborValue());
473 }
474 Q_ASSERT(index < container->elements.size());
475 return { container, index };
476 }
477 template <typename KeyType> static QCborValueRef findOrAddMapKey(QCborMap &map, KeyType key);
478 template <typename KeyType> static QCborValueRef findOrAddMapKey(QCborValue &self, KeyType key);
479 template <typename KeyType> static QCborValueRef findOrAddMapKey(QCborValueRef self, KeyType key);
480
481#if QT_CONFIG(cborstreamreader)
482 void decodeValueFromCbor(QCborStreamReader &reader, int remainingStackDepth);
483 void decodeStringFromCbor(QCborStreamReader &reader);
484 static inline void setErrorInReader(QCborStreamReader &reader, QCborError error);
485#endif
486};
487
488QT_END_NAMESPACE
489
490#endif // QCBORVALUE_P_H
491

source code of qtbase/src/corelib/serialization/qcborvalue_p.h