QCssParser: statically verify QCssKnownValue arrays

Port from explicit array sizes to implicit ones, followed by an
explicit size check. This prevents mistakes where, due to missing
initializers, value-constructed trailing elements cause lower_bound()
to be called out-of-contract, such as the one
5d8f815e101da3ae9cd6a666cc097853f52b21da fixed.

Also statically verify that the arrays are sorted. This doesn't work
on GCC < 10, e.g. QNX, so guard it the same way we did in
qhttpheaders.cpp in e3fe3997ebd2baaafdfa7aa3c1eb95e8048268f8.

Task-number: QTBUG-103721
Pick-to: 6.8 6.5
Change-Id: Ia55149102013fa4e1619be68a69bc52b46e0f006
Reviewed-by: Axel Spoerl <axel.spoerl@qt.io>
(cherry picked from commit ddf69669d39919b20a2710211911587353fa3dce)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Marc Mutz 2025-03-11 09:41:10 +01:00 committed by Qt Cherry-pick Bot
parent 8909279d3e
commit 18f0dd9bdf

View File

@ -15,7 +15,7 @@
#include <qimagereader.h>
#include <qtextformat.h>
#include <algorithm>
#include <QtCore/q20algorithm.h>
#ifndef QT_NO_CSSPARSER
@ -35,10 +35,30 @@ struct QCssKnownValue
{
const char name[28];
quint64 id;
struct ByName;
};
struct QCssKnownValue::ByName
{
constexpr bool operator()(const QCssKnownValue &lhs, const QCssKnownValue &rhs) const noexcept
{ return std::string_view{lhs.name} < std::string_view{rhs.name}; }
};
#if !defined(Q_CC_GNU_ONLY) || Q_CC_GNU >= 1000
# define NOT_OLD_GCCs(...) __VA_ARGS__
#else
# define NOT_OLD_GCCs(...) /* nothing */
#endif
#define CHECK_ARRAY_IS_SORTED(array, Num) \
static_assert(std::size(array) == Num); \
NOT_OLD_GCCs( \
static_assert(q20::is_sorted(std::begin(array), std::end(array), \
QCssKnownValue::ByName{})); \
) /* NOT_OLD_GCCs */ \
/* end */
// This array is sorted alphabetically.
static const QCssKnownValue properties[NumProperties - 1] = {
static constexpr QCssKnownValue properties[] = {
{ "-qt-background-role", QtBackgroundRole },
{ "-qt-block-indent", QtBlockIndent },
{ "-qt-fg-texture-cachekey", QtForegroundTextureCacheKey },
@ -160,8 +180,9 @@ static const QCssKnownValue properties[NumProperties - 1] = {
{ "width", Width },
{ "word-spacing", WordSpacing }
};
CHECK_ARRAY_IS_SORTED(properties, size_t(NumProperties) - 1)
static const QCssKnownValue values[NumKnownValues - 1] = {
static constexpr QCssKnownValue values[] = {
{ "accent", Value_Accent },
{ "active", Value_Active },
{ "alternate-base", Value_AlternateBase },
@ -248,6 +269,7 @@ static const QCssKnownValue values[NumKnownValues - 1] = {
{ "x-large", Value_XLarge },
{ "xx-large", Value_XXLarge }
};
CHECK_ARRAY_IS_SORTED(values, size_t(NumKnownValues) - 1)
//Map id to strings as they appears in the 'values' array above
static constexpr uchar indexOfId[] = {
@ -267,7 +289,7 @@ QString Value::toString() const
}
}
static const QCssKnownValue pseudos[NumPseudos - 1] = {
static constexpr QCssKnownValue pseudos[] = {
{ "active", PseudoClass_Active },
{ "adjoins-item", PseudoClass_Item },
{ "alternate", PseudoClass_Alternate },
@ -313,44 +335,51 @@ static const QCssKnownValue pseudos[NumPseudos - 1] = {
{ "vertical", PseudoClass_Vertical },
{ "window", PseudoClass_Window }
};
CHECK_ARRAY_IS_SORTED(pseudos, size_t(NumPseudos) - 1)
static const QCssKnownValue origins[NumKnownOrigins - 1] = {
static constexpr QCssKnownValue origins[] = {
{ "border", Origin_Border },
{ "content", Origin_Content },
{ "margin", Origin_Margin }, // not in css
{ "padding", Origin_Padding }
};
CHECK_ARRAY_IS_SORTED(origins, size_t(NumKnownOrigins) - 1)
static const QCssKnownValue repeats[NumKnownRepeats - 1] = {
static constexpr QCssKnownValue repeats[] = {
{ "no-repeat", Repeat_None },
{ "repeat-x", Repeat_X },
{ "repeat-xy", Repeat_XY },
{ "repeat-y", Repeat_Y }
};
CHECK_ARRAY_IS_SORTED(repeats, size_t(NumKnownRepeats) - 1)
static const QCssKnownValue tileModes[NumKnownTileModes - 1] = {
static constexpr QCssKnownValue tileModes[] = {
{ "repeat", TileMode_Repeat },
{ "round", TileMode_Round },
{ "stretch", TileMode_Stretch },
};
CHECK_ARRAY_IS_SORTED(tileModes, size_t(NumKnownTileModes) - 1)
static const QCssKnownValue positions[NumKnownPositionModes - 1] = {
static constexpr QCssKnownValue positions[] = {
{ "absolute", PositionMode_Absolute },
{ "fixed", PositionMode_Fixed },
{ "relative", PositionMode_Relative },
{ "static", PositionMode_Static }
};
CHECK_ARRAY_IS_SORTED(positions, size_t(NumKnownPositionModes) - 1)
static const QCssKnownValue attachments[NumKnownAttachments - 1] = {
static constexpr QCssKnownValue attachments[] = {
{ "fixed", Attachment_Fixed },
{ "scroll", Attachment_Scroll }
};
CHECK_ARRAY_IS_SORTED(attachments, size_t(NumKnownAttachments) - 1)
static const QCssKnownValue styleFeatures[NumKnownStyleFeatures - 1] = {
static constexpr QCssKnownValue styleFeatures[] = {
{ "background-color", StyleFeature_BackgroundColor },
{ "background-gradient", StyleFeature_BackgroundGradient },
{ "none", StyleFeature_None }
};
CHECK_ARRAY_IS_SORTED(styleFeatures, size_t(NumKnownStyleFeatures) - 1)
static bool operator<(const QString &name, const QCssKnownValue &prop)
{
@ -362,6 +391,9 @@ static bool operator<(const QCssKnownValue &prop, const QString &name)
return QString::compare(QLatin1StringView(prop.name), name, Qt::CaseInsensitive) < 0;
}
#undef CHECK_ARRAY_IS_SORTED
#undef NOT_OLD_GCCs
static quint64 findKnownValue(const QString &name, const QCssKnownValue *start, int numValues)
{
const QCssKnownValue *end = start + (numValues - 1);