From 31385ffceba1ab2f8672893bbef6c8ef8a32eb07 Mon Sep 17 00:00:00 2001 From: iphydf Date: Wed, 8 Jan 2025 11:34:22 +0000 Subject: [PATCH] test: Add the kimageformats fuzz test to third_party. Moving it out of qtox, because it's independent of that code. --- third_party/.clang-format | 82 ++++++++++++++ third_party/kimageformats.BUILD | 12 +- third_party/kimageformats/BUILD.bazel | 27 +++++ .../kimageformats/test/qimage_fuzz_test.cpp | 63 +++++++++++ .../kimageformats/test/qimage_test.cpp | 105 ++++++++++++++++++ third_party/qt/build_defs.bzl | 8 +- 6 files changed, 288 insertions(+), 9 deletions(-) create mode 100644 third_party/.clang-format create mode 100644 third_party/kimageformats/BUILD.bazel create mode 100644 third_party/kimageformats/test/qimage_fuzz_test.cpp create mode 100644 third_party/kimageformats/test/qimage_test.cpp diff --git a/third_party/.clang-format b/third_party/.clang-format new file mode 100644 index 00000000..9198e43d --- /dev/null +++ b/third_party/.clang-format @@ -0,0 +1,82 @@ +--- +# Language +Standard: Cpp11 + +# Indentation +IndentWidth: 4 +ContinuationIndentWidth: 4 +AccessModifierOffset: -4 +IndentCaseLabels: false +NamespaceIndentation: None + +# Spacing +UseTab: Never +SpaceBeforeParens: ControlStatements +SpacesBeforeTrailingComments: 1 +SpaceInEmptyParentheses: false +SpacesInAngles: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpacesInCStyleCastParentheses: false +SpaceBeforeAssignmentOperators: true +MaxEmptyLinesToKeep: 2 + +# Alignment +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: true +AlignOperands: true +AlignTrailingComments: true + +# Argument Packing +BinPackArguments: true +BinPackParameters: true + +# Break handling +ColumnLimit: 100 +BreakBeforeBraces: Mozilla +BreakBeforeBinaryOperators: NonAssignment +BreakConstructorInitializersBeforeComma: true +AlwaysBreakTemplateDeclarations: true +ConstructorInitializerAllOnOneLineOrOnePerLine: false +Cpp11BracedListStyle: true + +# Break penalities +PenaltyBreakBeforeFirstCallParameter: 200 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 5 +PenaltyReturnTypeOnItsOwnLine: 60 + +# Includes +SortIncludes: true +IncludeCategories: + # Match local headers + - Regex: '^"[[:alnum:]_]+\.h"$' + Priority: 1 + # Match project headers + - Regex: '^"[[:alnum:]_]+/.+\.h"$' + Priority: 2 + # Match Qt headers + - Regex: '^$' + Priority: 3 + # Match other headers + - Regex: '.*' + Priority: 4 + +# Short blocks +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty + +# Set pointer format +DerivePointerAlignment: false +PointerAlignment: Left + +# "const X" instead of "X const" +QualifierAlignment: Left +... diff --git a/third_party/kimageformats.BUILD b/third_party/kimageformats.BUILD index 99d54c52..eb122dd4 100644 --- a/third_party/kimageformats.BUILD +++ b/third_party/kimageformats.BUILD @@ -3,16 +3,18 @@ load("@toktok//third_party/qt:build_defs.bzl", "qt_moc") qt_moc( name = "kimageformats_moc", - srcs = glob( - [ - "src/imageformats/*.cpp", - "src/imageformats/*_p.h", - ], + srcs = ["src/imageformats/xcf.cpp"] + glob( + ["src/imageformats/*_p.h"], exclude = [ "src/imageformats/avif_p.h", "src/imageformats/exr_p.h", + "src/imageformats/fastmath_p.h", + "src/imageformats/gimp_p.h", "src/imageformats/jxl_p.h", "src/imageformats/ora_p.h", + "src/imageformats/rle_p.h", + "src/imageformats/scanlineconverter_p.h", + "src/imageformats/util_p.h", ], ), hdrs = glob(["src/imageformats/*.json"]), diff --git a/third_party/kimageformats/BUILD.bazel b/third_party/kimageformats/BUILD.bazel new file mode 100644 index 00000000..ed07e687 --- /dev/null +++ b/third_party/kimageformats/BUILD.bazel @@ -0,0 +1,27 @@ +load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test") +load("//third_party/qt:build_defs.bzl", "qt_test") + +cc_fuzz_test( + name = "qimage_fuzz_test", + size = "small", + testonly = True, + srcs = ["test/qimage_fuzz_test.cpp"], + tags = ["qt"], + deps = [ + "@kimageformats", + "@qt//:qt_core", + "@qt//:qt_gui", + ], +) + +qt_test( + name = "qimage_test", + size = "small", + src = "test/qimage_test.cpp", + tags = ["manual"], + deps = [ + "@kimageformats", + "@qt//:qt_core", + "@qt//:qt_gui", + ], +) diff --git a/third_party/kimageformats/test/qimage_fuzz_test.cpp b/third_party/kimageformats/test/qimage_fuzz_test.cpp new file mode 100644 index 00000000..1b873660 --- /dev/null +++ b/third_party/kimageformats/test/qimage_fuzz_test.cpp @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace { +void test_fromData(const uint8_t* data, size_t size) +{ + if (size == 0) { + return; + } + + const auto& plugins = QPluginLoader::staticInstances(); + assert(plugins.size() < 256); + if (data[0] >= plugins.size()) { + return; + } + + if (auto* imagePlugin = qobject_cast(plugins[data[0]])) { + qInstallMessageHandler([](QtMsgType, const QMessageLogContext&, const QString&) { + // Ignore messages + }); + + QByteArray ba(reinterpret_cast(data + 1), size - 1); + QBuffer device(&ba); + device.open(QIODevice::ReadOnly); + + QImage image; + + auto* handler = imagePlugin->create(&device); + if (handler && handler->canRead()) { + handler->read(&image); + } + delete handler; + } +} +} // namespace + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + test_fromData(data, size); + return 0; +} + +Q_IMPORT_PLUGIN(ANIPlugin) +Q_IMPORT_PLUGIN(EPSPlugin) +Q_IMPORT_PLUGIN(HDRPlugin) +Q_IMPORT_PLUGIN(PCXPlugin) +Q_IMPORT_PLUGIN(PFMPlugin) +Q_IMPORT_PLUGIN(PSDPlugin) +Q_IMPORT_PLUGIN(PXRPlugin) +Q_IMPORT_PLUGIN(QOIPlugin) +Q_IMPORT_PLUGIN(RASPlugin) +Q_IMPORT_PLUGIN(RGBPlugin) +Q_IMPORT_PLUGIN(TGAPlugin) +Q_IMPORT_PLUGIN(XCFPlugin) +Q_IMPORT_PLUGIN(SoftimagePICPlugin) diff --git a/third_party/kimageformats/test/qimage_test.cpp b/third_party/kimageformats/test/qimage_test.cpp new file mode 100644 index 00000000..27a30de4 --- /dev/null +++ b/third_party/kimageformats/test/qimage_test.cpp @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2025 The TokTok team. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// Check if allocation size isn't too big, then call glibc's malloc. +static void* (*libc_malloc)(size_t); +static bool malloc_failed = false; + +void* malloc(size_t size) +{ + if (!malloc_failed && size < 100 * 1024 * 1024) { + malloc_failed = true; + } + if (!libc_malloc) { + libc_malloc = reinterpret_cast(dlsym(RTLD_NEXT, "malloc")); + } + return libc_malloc(size); +} + +class TestQImage : public QObject +{ + Q_OBJECT + +private slots: + void testANI(); + void testXCF(); + void testXCFSlow(); +}; + +// https://bugs.kde.org/show_bug.cgi?id=498368 +#define BUG_498368_FIXED 1 +void TestQImage::testANI() +{ +#if BUG_498368_FIXED + malloc_failed = false; + + const QByteArray data = QByteArray::fromBase64( // + "AFJJRkYOAACAQUNPTgB+YAAAAAAAUklGRg4AAIBBQ09OAH5gAAAAAABzZXEgANra2tra2tra2tra2t" + "ra2tra2tra2tra2tra2tra2tra2traAAAAAAAAAAAAAAAAAF0="); + + QImage::fromData(data.mid(1), "ANI"); + QVERIFY(!malloc_failed); +#endif +} + +// https://bugs.kde.org/show_bug.cgi?id=498380 +#define BUG_498380_FIXED 1 +void TestQImage::testXCF() +{ +#if BUG_498380_FIXED + const QByteArray data = QByteArray::fromBase64( + "AWdpbXAgeGNmAAAwAAoAAABbAAAAAzMAAAAAAAAAAAAAAAAAAAAgCHAAAC0AAAAAAAgAAAAAAAAg" + "8AAAAAAAAAAACAAAAAAgAPAAAAAACgCAAAAAAAAAAAAJ2/AAAAAAAAAAACMAAAAACHAAAAAAAAAA" + "AAgAAAAAAAAg8AAAAAAAAAAACAAAAAAgXPAAAAAACgCAAAAAAAAAAAAJ2/AAAAAAAAAAACIAAAAI" + "cAAALQAAAAAACAAAAAAAIAhwAAAtAAAAAAAIAAAAAAAAIPAAAAAAAAAAAAgAAAAAIADwAAAAAAoA" + "gAAAAAAAAAAACdvwAAAAAAAAAAAjAAAKAIAAAAAAAAAAAAnb8AAAAAAAAAAAIgAAAAhwAAAtAAAA" + "AAAIAAAAAAAgCHAAAAAAAAAg8AAAAAAAAAAACAAAAAAgAPAAPwAACgCAAAAAAAAAAAAJ2/AAAAAA" + "AAAAAPAAIABQSApg"); + + QImage::fromData(data.mid(1), "XCF"); +#endif +} + +// https://bugs.kde.org/show_bug.cgi?id=498381 +#define BUG_498381_FIXED 1 +void TestQImage::testXCFSlow() +{ +#if BUG_498381_FIXED + const QByteArray data = QByteArray::fromBase64( + "AWdpbXAgeGNmAAAwAAoAAABbAAAAAzMAAAAAAAAAAAAAAAYAcAEAAAAAAwAAAAAAAf//////bW1t" + "bW1tbW1tbW1tbW1tbW3/////////////bW1tnZ2dnZ2dnZ2dJSFQUy1BZG9iZZ2dnZ2dnZ2dnZ2d" + "nZ2dnZ2dnZ2dnXJycnJycnJycnJycnJycnJycnJycnJycnJtfm1tbW1tbW1tAAAAAAAAAAABMQAA" + "7wYAAAAAAAAAAQAAAAAAAAAAAAAAAAkAAAAJ22M/"); + + QImage::fromData(data.mid(1), "XCF"); +#endif +} + +QTEST_GUILESS_MAIN(TestQImage) +#include "qimage_test.moc" + +Q_IMPORT_PLUGIN(ANIPlugin) +// Q_IMPORT_PLUGIN(EPSPlugin) +// Q_IMPORT_PLUGIN(HDRPlugin) +// Q_IMPORT_PLUGIN(PCXPlugin) +// Q_IMPORT_PLUGIN(PFMPlugin) +// Q_IMPORT_PLUGIN(PSDPlugin) +// Q_IMPORT_PLUGIN(PXRPlugin) +// Q_IMPORT_PLUGIN(QOIPlugin) +// Q_IMPORT_PLUGIN(RASPlugin) +// Q_IMPORT_PLUGIN(RGBPlugin) +// Q_IMPORT_PLUGIN(TGAPlugin) +Q_IMPORT_PLUGIN(XCFPlugin) +// Q_IMPORT_PLUGIN(SoftimagePICPlugin) diff --git a/third_party/qt/build_defs.bzl b/third_party/qt/build_defs.bzl index ef1b1b5d..2fdc2c7b 100644 --- a/third_party/qt/build_defs.bzl +++ b/third_party/qt/build_defs.bzl @@ -293,20 +293,20 @@ def qt_binary(name, tags=[], **kwargs): # Qt test with MOC for the test .cpp file. # ========================================================= -def qt_test(name, src, deps, copts = [], mocopts = [], size = None, **kwargs): +def qt_test(name, src, deps, copts = [], mocopts = [], size = None, tags = [], **kwargs): qt_moc( name = "%s_moc_src" % name, testonly = True, srcs = [src], mocopts = mocopts, - tags = ["qt"], + tags = ["qt"] + tags, deps = deps, ) cc_library( name = "%s_moc" % name, testonly = True, hdrs = [":%s_moc_src" % name], - tags = ["qt"], + tags = ["qt"] + tags, **kwargs ) cc_test( @@ -333,7 +333,7 @@ def qt_test(name, src, deps, copts = [], mocopts = [], size = None, **kwargs): ":%s_moc" % name, "@qt//:qt_test", ], - tags = ["qt"], + tags = ["qt"] + tags, **kwargs )