Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:critical reason:data-parser
++
++ //
++ // W A R N I N G
++ // -------------
++ //
++ // This file is not part of the Qt API. It exists for the convenience
++ // of other Qt classes. This header file may change from version to
++ // version without notice, or even be removed.
++ //
++ // We mean it.
++ //
++
++ #include <QtCore/private/qglobal_p.h>
++ #include <qstringconverter.h>
++ #include <qxmlstream.h>
++ #include "qxmlstreamgrammar_p.h"
++ #include <QtCore/qhash.h>
++ #include <QCoreApplication> // Q_DECLARE_TR_FUNCTIONS
++ #include <QtCore/private/qtools_p.h>
++
++
++ #include <memory>
++ #include <optional>
++
++ #ifndef QXMLSTREAM_P_H
++ #define QXMLSTREAM_P_H
++
++ QT_BEGIN_NAMESPACE
++
++ class QByteArrayView;
++
++ namespace QtPrivate {
++
++ class XmlStringRef
++ {
++ public:
++ const QString *m_string = nullptr;
++ qsizetype m_pos = 0;
++ qsizetype m_size = 0;
++
++ constexpr XmlStringRef() = default;
++ constexpr inline XmlStringRef(const QString *string, qsizetype pos, qsizetype length)
++ : m_string(string), m_pos(pos), m_size((Q_ASSERT(length >= 0), length))
++ {
++ }
++ XmlStringRef(const QString *string)
++ : XmlStringRef(string, 0, string->size())
++ {
++ }
++
++ operator QXmlString() const {
++ if (!m_string)
++ return QXmlString();
++ QStringPrivate d = m_string->data_ptr();
++ d.setBegin(d.data() + m_pos);
++ d.size = m_size;
++ return QXmlString(std::move(d));
++ }
++
++ void clear() { m_string = nullptr; m_pos = 0; m_size= 0; }
++ QStringView view() const { return m_string ? QStringView(m_string->data() + m_pos, m_size) : QStringView(); }
++ bool isEmpty() const { return m_size == 0; }
++ bool isNull() const { return !m_string; }
++ QString toString() const { return view().toString(); }
++
++ XmlStringRef sliced(qsizetype pos) const
++ {
++ verify(pos, 0);
++ return XmlStringRef(m_string, m_pos + pos, m_size - pos);
++ }
++ XmlStringRef sliced(qsizetype pos, qsizetype n) const
++ {
++ verify(pos, n);
++ return XmlStringRef(m_string, m_pos + pos, n);
++ }
++
++ using value_type = QStringView::value_type;
++ using size_type = QStringView::size_type;
++ using difference_type = QStringView::difference_type;
++ using pointer = QStringView::pointer;
++ using const_pointer = QStringView::const_pointer;
++ using reference = QStringView::reference;
++ using const_reference = QStringView::const_reference;
++ using iterator = QStringView::iterator;
++ using const_iterator = QStringView::const_iterator;
++ using reverse_iterator = QStringView::reverse_iterator;
++ using const_reverse_iterator = QStringView::const_reverse_iterator;
++
++ #define MAKE_MEMBER(name) \
++ auto name () const noexcept { return view(). name (); }
++ MAKE_MEMBER(data)
++ MAKE_MEMBER(size)
++ MAKE_MEMBER(empty)
++ MAKE_MEMBER(begin)
++ MAKE_MEMBER(end)
++ MAKE_MEMBER(cbegin)
++ MAKE_MEMBER(cend)
++ MAKE_MEMBER(rbegin)
++ MAKE_MEMBER(rend)
++ MAKE_MEMBER(crbegin)
++ MAKE_MEMBER(crend)
++ #undef MAKE_MEMBER
++
++ #define MAKE_OP(op) \
++ friend auto operator op(const XmlStringRef &lhs, const XmlStringRef &rhs) noexcept { return lhs.view() op rhs.view(); } \
++ /*end*/
++ MAKE_OP(==)
++ MAKE_OP(!=)
++ MAKE_OP(<=)
++ MAKE_OP(>=)
++ MAKE_OP(<)
++ MAKE_OP(>)
++ #undef MAKE_OP
++ #define MAKE_OP(op) \
++ friend auto operator op(const XmlStringRef &lhs, QStringView rhs) noexcept { return lhs.view() op rhs; } \
++ friend auto operator op(QStringView lhs, const XmlStringRef &rhs) noexcept { return lhs op rhs.view(); } \
++ /*end*/
++ MAKE_OP(==)
++ MAKE_OP(!=)
++ MAKE_OP(<=)
++ MAKE_OP(>=)
++ MAKE_OP(<)
++ MAKE_OP(>)
++ #undef MAKE_OP
++
++ private:
++ Q_ALWAYS_INLINE
++ void verify([[maybe_unused]] qsizetype pos = 0,
++ [[maybe_unused]] qsizetype n = 1) const
++ {
++ Q_PRE(pos >= 0);
++ Q_PRE(pos <= size());
++ Q_PRE(n >= 0);
++ Q_PRE(n <= size() - pos);
++ }
++ };
++
++ }
++
++ template <typename T> class QXmlStreamSimpleStack
++ {
++ Q_DISABLE_COPY_MOVE(QXmlStreamSimpleStack)
++ static_assert(sizeof(T) > 1, "some operations may overflow with such small types");
++
++ T *data;
++ qsizetype tos, cap; // class invariants: cap ≥ 0, tos < cap
++ public:
++ inline QXmlStreamSimpleStack()
++ : data(nullptr), tos(-1), cap(0)
++ {}
++ inline ~QXmlStreamSimpleStack()
++ {
++ if (data) {
++ std::destroy_n(data, size());
++ free(data);
++ }
++ }
++
++ inline void reserve(qsizetype extraCapacity)
++ {
++ auto needed = extraCapacity;
++ if (add_overflow(needed, tos + 1, &needed)) // tos + 1 is <= cap, so doesn't overflow!
++ qBadAlloc();
++ if (needed > cap) {
++ // The following multiplication cannot overflow or we would have
++ // allocated at least half the address space successfully before
++ // (for sizeof(T) > 1, which we assert):
++ needed = (std::max)(needed, cap * 2);
++ void *ptr = qt_reallocarray(static_cast<void *>(data), needed, sizeof(T));
++ Q_CHECK_PTR(ptr);
++ data = reinterpret_cast<T *>(ptr);
++ cap = needed;
++ }
++ }
++
++ inline T &push() { reserve(1); return rawPush(); }
++ inline T &rawPush() { return *new (data + (++tos)) T; }
++ inline const T &top() const { return data[tos]; }
++ inline T &top() { return data[tos]; }
++ inline T pop() { T t = std::move(data[tos]); std::destroy_at(data + tos); --tos; return t; }
++ inline T &operator[](qsizetype index) { return data[index]; }
++ inline const T &at(qsizetype index) const { return data[index]; }
++ inline qsizetype size() const { return tos + 1; }
++ inline void resize(qsizetype s) { tos = s - 1; }
++ inline bool isEmpty() const { return tos < 0; }
++ inline void clear() { tos = -1; }
++
++ using const_iterator = const T*;
++ using iterator = T*;
++ T *begin() { return data; }
++ const T *begin() const { return data; }
++ const T *cbegin() const { return begin(); }
++ T *end() { return data + size(); }
++ const T *end() const { return data + size(); }
++ const T *cend() const { return end(); }
++ };
++
++ class QXmlStream
++ {
++ Q_DECLARE_TR_FUNCTIONS(QXmlStream)
++ };
++
++ class QXmlStreamPrivateTagStack {
++ Q_DISABLE_COPY_MOVE(QXmlStreamPrivateTagStack)
++ using XmlStringRef = QtPrivate::XmlStringRef;
++ public:
++ struct NamespaceDeclaration
++ {
++ XmlStringRef prefix;
++ XmlStringRef namespaceUri;
++ };
++
++ struct Tag
++ {
++ XmlStringRef name;
++ XmlStringRef qualifiedName;
++ NamespaceDeclaration namespaceDeclaration;
++ qsizetype tagStackStringStorageSize;
++ qsizetype namespaceDeclarationsSize;
++ };
++
++
++ QXmlStreamPrivateTagStack();
++ QXmlStreamSimpleStack<NamespaceDeclaration> namespaceDeclarations;
++ QString tagStackStringStorage;
++ qsizetype tagStackStringStorageSize;
++ qsizetype initialTagStackStringStorageSize;
++ bool tagsDone;
++
++ XmlStringRef addToStringStorage(QAnyStringView s)
++ {
++ qsizetype pos = tagStackStringStorageSize;
++ if (pos != tagStackStringStorage.size())
++ tagStackStringStorage.resize(pos);
++ s.visit([&](auto s) { tagStackStringStorage.append(s); });
++ qsizetype sz = (tagStackStringStorage.size() - pos);
++ tagStackStringStorageSize += sz;
++ return XmlStringRef(&tagStackStringStorage, pos, sz);
++ }
++
++ QXmlStreamSimpleStack<Tag> tagStack;
++
++
++ inline Tag tagStack_pop() {
++ Tag tag = tagStack.pop();
++ tagStackStringStorageSize = tag.tagStackStringStorageSize;
++ namespaceDeclarations.resize(tag.namespaceDeclarationsSize);
++ tagsDone = tagStack.isEmpty();
++ return tag;
++ }
++ inline Tag &tagStack_push() {
++ Tag &tag = tagStack.push();
++ tag.tagStackStringStorageSize = tagStackStringStorageSize;
++ tag.namespaceDeclarationsSize = namespaceDeclarations.size();
++ return tag;
++ }
++ };
++
++ #if QT_CONFIG(xmlstreamreader)
++ class QXmlStreamEntityResolver;
++ class QXmlStreamReaderPrivate : public QXmlStreamGrammar, public QXmlStreamPrivateTagStack
++ {
++ QXmlStreamReader *q_ptr;
++ Q_DECLARE_PUBLIC(QXmlStreamReader)
++ using XmlStringRef = QtPrivate::XmlStringRef;
++ public:
++ QXmlStreamReaderPrivate(QXmlStreamReader *q);
++ ~QXmlStreamReaderPrivate();
++ void init();
++
++ struct BufferAndEncoding
++ {
++ QByteArray buffer;
++ // System means that we didn't specify the encoding
++ QStringDecoder::Encoding encoding;
++
++ BufferAndEncoding()
++ : buffer(), encoding(QStringDecoder::System)
++ {}
++ BufferAndEncoding(const QByteArray &b, QStringDecoder::Encoding e)
++ : buffer(b), encoding(e)
++ {}
++ };
++ QList<BufferAndEncoding> dataInfo;
++ QStringDecoder chunkDecoder;
++ void appendDataWithEncoding(const QByteArray &data, QStringDecoder::Encoding enc);
++ bool tryAppendViewWithEncoding(QByteArrayView data, QStringDecoder::Encoding enc);
++ void appendViewWithEncoding(QByteArrayView data, QStringDecoder::Encoding enc);
++
++ QByteArray rawReadBuffer;
++ qint64 nbytesread;
++ QString readBuffer;
++ qsizetype readBufferPos;
++ QXmlStreamSimpleStack<uint> putStack;
++ struct Entity {
++ Entity() = default;
++ Entity(const QString &name, const QString &value)
++ : name(name), value(value), external(false), unparsed(false), literal(false),
++ hasBeenParsed(false), isCurrentlyReferenced(false){}
++ static inline Entity createLiteral(QLatin1StringView name, QLatin1StringView value)
++ { Entity result(name, value); result.literal = result.hasBeenParsed = true; return result; }
++ QString name, value;
++ uint external : 1;
++ uint unparsed : 1;
++ uint literal : 1;
++ uint hasBeenParsed : 1;
++ uint isCurrentlyReferenced : 1;
++ };
++ // these hash tables use a QStringView as a key to avoid creating QStrings
++ // just for lookup. The keys are usually views into Entity::name and thus
++ // are guaranteed to have the same lifetime as the referenced data:
++ QHash<QStringView, Entity> entityHash;
++ QHash<QStringView, Entity> parameterEntityHash;
++ struct QEntityReference
++ {
++ QHash<QStringView, Entity> *hash;
++ QStringView name;
++ };
++ QXmlStreamSimpleStack<QEntityReference> entityReferenceStack;
++ int entityExpansionLimit = 4096;
++ qsizetype entityLength = 0;
++ inline bool referenceEntity(QHash<QStringView, Entity> *hash, Entity &entity)
++ {
++ Q_ASSERT(hash);
++ if (entity.isCurrentlyReferenced) {
++ raiseWellFormedError(QXmlStream::tr("Self-referencing entity detected."));
++ return false;
++ }
++ const auto valLen = entity.value.size();
++ const auto refLen = entity.name.size() + 2; // 2 for `&` and `;`
++ // don't count the top-level reference (could turn the entityLength negative):
++ const auto effectiveRefLen = entityReferenceStack.isEmpty() ? 0 : refLen;
++ entityLength += valLen - effectiveRefLen;
++ if (entityLength > entityExpansionLimit) {
++ raiseWellFormedError(QXmlStream::tr("Entity expands to more characters than the entity expansion limit."));
++ return false;
++ }
++ entity.isCurrentlyReferenced = true;
++ entityReferenceStack.push() = { hash, entity.name };
++ injectToken(ENTITY_DONE);
++ return true;
++ }
++
++
++ QIODevice *device;
++ bool deleteDevice;
++ QStringDecoder decoder;
++ bool atEnd;
++
++ enum class XmlContext
++ {
++ Prolog,
++ Body,
++ };
++
++ XmlContext currentContext = XmlContext::Prolog;
++ bool foundDTD = false;
++ bool isValidToken(QXmlStreamReader::TokenType type);
++ void checkToken();
++
++ /*!
++ \sa setType()
++ */
++ QXmlStreamReader::TokenType type;
++ QXmlStreamReader::Error error;
++ QString errorString;
++ QString unresolvedEntity;
++
++ qint64 lineNumber, lastLineStart, characterOffset;
++
++
++ void write(const QString &);
++ void write(const char *);
++
++
++ QXmlStreamAttributes attributes;
++ XmlStringRef namespaceForPrefix(QStringView prefix);
++ void resolveTag();
++ void resolvePublicNamespaces();
++ void resolveDtd();
++ uint resolveCharRef(int symbolIndex);
++ bool checkStartDocument();
++ void startDocument();
++ void parseError();
++ void checkPublicLiteral(QStringView publicId);
++
++ bool scanDtd;
++ XmlStringRef lastAttributeValue;
++ bool lastAttributeIsCData;
++ struct DtdAttribute {
++ XmlStringRef tagName;
++ XmlStringRef attributeQualifiedName;
++ XmlStringRef attributePrefix;
++ XmlStringRef attributeName;
++ XmlStringRef defaultValue;
++ bool isCDATA;
++ bool isNamespaceAttribute;
++ };
++ QXmlStreamSimpleStack<DtdAttribute> dtdAttributes;
++ struct NotationDeclaration {
++ XmlStringRef name;
++ XmlStringRef publicId;
++ XmlStringRef systemId;
++ };
++ QXmlStreamSimpleStack<NotationDeclaration> notationDeclarations;
++ QXmlStreamNotationDeclarations publicNotationDeclarations;
++ QXmlStreamNamespaceDeclarations publicNamespaceDeclarations;
++
++ struct EntityDeclaration {
++ XmlStringRef name;
++ XmlStringRef notationName;
++ XmlStringRef publicId;
++ XmlStringRef systemId;
++ XmlStringRef value;
++ bool parameter;
++ bool external;
++ inline void clear() {
++ name.clear();
++ notationName.clear();
++ publicId.clear();
++ systemId.clear();
++ value.clear();
++ parameter = external = false;
++ }
++ };
++ QXmlStreamSimpleStack<EntityDeclaration> entityDeclarations;
++ QXmlStreamEntityDeclarations publicEntityDeclarations;
++
++ XmlStringRef text;
++
++ XmlStringRef prefix, namespaceUri, qualifiedName, name;
++ XmlStringRef processingInstructionTarget, processingInstructionData;
++ XmlStringRef dtdName, dtdPublicId, dtdSystemId;
++ XmlStringRef documentVersion, documentEncoding;
++ uint isEmptyElement : 1;
++ uint isWhitespace : 1;
++ uint isCDATA : 1;
++ uint standalone : 1;
++ uint hasCheckedStartDocument : 1;
++ uint normalizeLiterals : 1;
++ uint hasSeenTag : 1;
++ uint inParseEntity : 1;
++ uint referenceToUnparsedEntityDetected : 1;
++ uint referenceToParameterEntityDetected : 1;
++ uint hasExternalDtdSubset : 1;
++ uint lockEncoding : 1;
++ uint namespaceProcessing : 1;
++ uint hasStandalone : 1; // TODO: expose in public API
++
++ int resumeReduction;
++ void resume(int rule);
++
++ inline bool entitiesMustBeDeclared() const {
++ return (!inParseEntity
++ && (standalone
++ || (!referenceToUnparsedEntityDetected
++ && !referenceToParameterEntityDetected // Errata 13 as of 2006-04-25
++ && !hasExternalDtdSubset)));
++ }
++
++ // qlalr parser
++ qsizetype tos;
++ qsizetype stack_size;
++ struct Value {
++ qsizetype pos; // offset into textBuffer
++ qsizetype len; // length incl. prefix (if any)
++ qint16 prefix; // prefix of a name (as in "prefix:name") limited to 4k in fastScanName()
++ ushort c;
++ };
++
++ Value *sym_stack;
++ int *state_stack;
++ inline void reallocateStack();
++ inline Value &sym(int index) const
++ { return sym_stack[tos + index - 1]; }
++ QString textBuffer;
++ inline void clearTextBuffer() {
++ if (!scanDtd) {
++ textBuffer.resize(0);
++ textBuffer.reserve(256);
++ }
++ }
++ struct Attribute {
++ Value key;
++ Value value;
++ };
++ QXmlStreamSimpleStack<Attribute> attributeStack;
++
++ inline XmlStringRef symString(int index) {
++ return symString(sym(index));
++ }
++ QStringView symView(int index) const
++ {
++ const Value &symbol = sym(index);
++ return QStringView(textBuffer.data() + symbol.pos, symbol.len).mid(symbol.prefix);
++ }
++ inline XmlStringRef symName(int index) {
++ return symName(sym(index));
++ }
++ inline XmlStringRef symPrefix(int index) {
++ return symPrefix(sym(index));
++ }
++ auto symQName(int index) {
++ struct R {
++ XmlStringRef m_data;
++ qsizetype m_prefix;
++
++ XmlStringRef all() const { return m_data; }
++ XmlStringRef qname() const { return m_data; }
++ XmlStringRef prefix() const
++ { return m_prefix ? m_data.sliced(0, m_prefix - 1) : XmlStringRef(); }
++ XmlStringRef localPart() const { return m_data.sliced(m_prefix); }
++
++ void resetBackingStorage(XmlStringRef newData) { m_data = newData; } // m_prefix stays!
++ };
++ const Value &symbol = sym(index);
++ return R{{&textBuffer, symbol.pos, symbol.len}, symbol.prefix};
++ }
++ inline XmlStringRef symString(const Value &symbol) {
++ return XmlStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix);
++ }
++ inline XmlStringRef symName(const Value &symbol) {
++ return XmlStringRef(&textBuffer, symbol.pos, symbol.len);
++ }
++ inline XmlStringRef symPrefix(const Value &symbol) {
++ if (symbol.prefix)
++ return XmlStringRef(&textBuffer, symbol.pos, symbol.prefix - 1);
++ return XmlStringRef();
++ }
++
++ inline void clearSym() { Value &val = sym(1); val.pos = textBuffer.size(); val.len = 0; }
++
++
++ short token;
++ uint token_char;
++
++ uint filterCarriageReturn();
++ inline uint getChar();
++ inline uint peekChar();
++ inline void putChar(uint c) { putStack.push() = c; }
++ inline void putChar(QChar c) { putStack.push() = c.unicode(); }
++ void putString(QStringView s, qsizetype from = 0);
++ void putStringLiteral(QStringView s);
++ void putReplacement(QStringView s);
++ void putReplacementInAttributeValue(QStringView s);
++ uint getChar_helper();
++
++ bool scanUntil(const char *str, short tokenToInject = -1);
++ bool scanString(const char *str, short tokenToInject, bool requireSpace = true);
++ inline void injectToken(ushort tokenToInject) {
++ putChar(int(tokenToInject) << 16);
++ }
++
++ QString resolveUndeclaredEntity(const QString &name);
++ void parseEntity(const QString &value);
++ std::unique_ptr<QXmlStreamReaderPrivate> entityParser;
++
++ bool scanAfterLangleBang();
++ bool scanPublicOrSystem();
++ bool scanNData();
++ bool scanAfterDefaultDecl();
++ bool scanAttType();
++
++
++ // scan optimization functions. Not strictly necessary but LALR is
++ // not very well suited for scanning fast
++ qsizetype fastScanLiteralContent();
++ qsizetype fastScanSpace();
++ qsizetype fastScanContentCharList();
++ std::optional<qsizetype> fastScanName(Value *val = nullptr);
++ inline qsizetype fastScanNMTOKEN();
++
++
++ bool parse();
++ inline void consumeRule(int);
++
++ void raiseError(QXmlStreamReader::Error error, const QString& message = QString());
++ void raiseWellFormedError(const QString &message);
++ void raiseNamePrefixTooLongError();
++
++ QXmlStreamEntityResolver *entityResolver;
++
++ private:
++ /*! \internal
++ Never assign to variable type directly. Instead use this function.
++
++ This prevents errors from being ignored.
++ */
++ inline void setType(const QXmlStreamReader::TokenType t)
++ {
++ if (type != QXmlStreamReader::Invalid)
++ type = t;
++ }
++ };
++ #endif // feature xmlstreamreader
++
++ QT_END_NAMESPACE
++
++ #endif // QXMLSTREAM_P_H
++