Dear LTS Team, Several flaws caused by C++ undefined behaviour in the rlottie package that I maintain were reported as CVEs. rLottie is a platform-independent library for rendering vector-based animations and art. Its format is used by Telegram Desktop, the only reverse dependency in Debian.
I uploaded the fixes to these issues into unstable almost month ago. Afterwards, I prepared the updates to trixie and bookworm. And now I propose to apply the fixes to bullseye, the current LTS. Along with the fixes I have backported other patches from newer releases that improve overall stability. I pushed the changes to Salsa and, when CI built the package [1], tested it in Debian 11.11 booted from Live ISO image in VirtualBox. No visual glitches were seen on animated stickers in Telegram Desktop 4.6.5 from bullseye-backports. You will find the complete debdiff attached to this email. Though, I think it is difficult to read disparate patches from the debian/ folder, so I am also attaching the difference in the upstream source tree with all patches applied. [1]: https://salsa.debian.org/debian/rlottie/-/jobs/9923623/artifacts/browse/debian/output/
diffstat for rlottie-0.1+dfsg rlottie-0.1+dfsg changelog | 27 +++ patches/Atomic-render.patch | 46 +++++ patches/Avoid-assertion-failures.patch | 46 +++++ patches/Check-buffer-length.patch | 8 patches/Check-empty-frames.patch | 16 + patches/Empty-animation-data.patch | 27 +++ patches/Finite-loop-in-VBezier-tAtLength.patch | 31 +++ patches/Fix-crash-on-invalid-data.patch | 12 - patches/Fix-heap-buffer-overflow-from-short-truncation.patch | 31 +++ patches/Fixed-signed-shift-issue.patch | 64 +++++++ patches/Fixed-vpath-potential-issue.patch | 29 +++ patches/Fortify-FreeType-raster.patch | 84 +++++++++ patches/Freetype-raster.patch | 56 ------ patches/Ignore-unspecified-type.patch | 19 ++ patches/Init-keyframe.patch | 18 ++ patches/Limit-recursion-in-LOTLayerItem.patch | 39 ++++ patches/No-cyclic-structures.patch | 97 +++++++++++ patches/No-deadlock.patch | 15 + patches/Positive-points.patch | 20 ++ patches/Reject-reversed-frames.patch | 19 ++ patches/Stop-VBezier-length-overflow.patch | 65 +++++++ patches/Zero-corrupt-point.patch | 19 -- patches/series | 19 +- 23 files changed, 715 insertions(+), 92 deletions(-) diff -Nru rlottie-0.1+dfsg/debian/changelog rlottie-0.1+dfsg/debian/changelog --- rlottie-0.1+dfsg/debian/changelog 2026-02-08 12:05:10.000000000 +0300 +++ rlottie-0.1+dfsg/debian/changelog 2026-07-07 16:48:09.000000000 +0300 @@ -1,3 +1,30 @@ +rlottie (0.1+dfsg-2+deb11u2) bullseye-security; urgency=medium + + * Apply fixes and improve stability. + * Backported patches: + - Atomic-render.patch + - Avoid-assertion-failures.patch (closes: #1138916, fixes: CVE-2026-8916) + - Check-empty-frames.patch + - Finite-loop-in-VBezier-tAtLength.patch + - Fix-heap-buffer-overflow-from-short-truncation.patch + - Fixed-signed-shift-issue.patch (closes: #1139179, fixes: CVE-2026-10305) + - Fixed-vpath-potential-issue.patch (closes: #1138919, fixes: CVE-2026-47319) + - Ignore-unspecified-type.patch + - Init-keyframe.patch + - Limit-recursion-in-LOTLayerItem.patch (closes: #1138920, fixes: CVE-2026-47320) + - No-cyclic-structures.patch (closes: #1138917, fixes: CVE-2026-47306) + - No-deadlock.patch + - Positive-points.patch + - Reject-reversed-frames.patch + - Stop-VBezier-length-overflow.patch + * Amended patches: + - Check-buffer-length.patch + - Fortify-FreeType-raster.patch (closes: #1138918, fixes: CVE-2026-47318) + * Replace Zero-corrupt-point.patch with Empty-animation-data.patch based on + Subhransu Mohanty's commit (closes: #994614). + + -- Nicholas Guriev <[email protected]> Tue, 07 Jul 2026 16:48:09 +0300 + rlottie (0.1+dfsg-2+deb11u1) bullseye-security; urgency=high * Non-maintainer upload by the LTS Team. diff -Nru rlottie-0.1+dfsg/debian/patches/Atomic-render.patch rlottie-0.1+dfsg/debian/patches/Atomic-render.patch --- rlottie-0.1+dfsg/debian/patches/Atomic-render.patch 1970-01-01 03:00:00.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Atomic-render.patch 2026-07-07 15:10:19.000000000 +0300 @@ -0,0 +1,46 @@ +Description: Make the render() method really atomic + Value of the mRenderInProgress flag should be checked and updated in a single + operation or its atomic characteristics will not work. +Author: Nicholas Guriev <[email protected]> +Last-Update: Sat, 10 Sep 2022 18:41:59 +0300 + +--- a/src/lottie/lottieanimation.cpp ++++ b/src/lottie/lottieanimation.cpp +@@ -74,7 +74,7 @@ private: + std::shared_ptr<LOTModel> mModel; + std::unique_ptr<LOTCompItem> mCompItem; + SharedRenderTask mTask; +- std::atomic<bool> mRenderInProgress; ++ std::atomic_flag mRenderInProgress; + }; + + void AnimationImpl::setValue(const std::string &keypath, LOTVariant &&value) +@@ -104,17 +104,15 @@ bool AnimationImpl::update(size_t frameN + + Surface AnimationImpl::render(size_t frameNo, const Surface &surface, bool keepAspectRatio) + { +- bool renderInProgress = mRenderInProgress.load(); +- if (renderInProgress) { ++ if (mRenderInProgress.test_and_set()) { + vCritical << "Already Rendering Scheduled for this Animation"; + return surface; + } + +- mRenderInProgress.store(true); + update(frameNo, + VSize(int(surface.drawRegionWidth()), int(surface.drawRegionHeight())), keepAspectRatio); + mCompItem->render(surface); +- mRenderInProgress.store(false); ++ mRenderInProgress.clear(); + + return surface; + } +@@ -123,7 +121,7 @@ void AnimationImpl::init(const std::shar + { + mModel = model; + mCompItem = std::make_unique<LOTCompItem>(mModel.get()); +- mRenderInProgress = false; ++ mRenderInProgress.clear(); + } + + #ifdef LOTTIE_THREAD_SUPPORT diff -Nru rlottie-0.1+dfsg/debian/patches/Avoid-assertion-failures.patch rlottie-0.1+dfsg/debian/patches/Avoid-assertion-failures.patch --- rlottie-0.1+dfsg/debian/patches/Avoid-assertion-failures.patch 1970-01-01 03:00:00.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Avoid-assertion-failures.patch 2026-07-07 15:09:36.000000000 +0300 @@ -0,0 +1,46 @@ +Description: Touch up some incorrect values that result to assertion violations +Author: Nicholas Guriev <[email protected]> +Last-Modified: Fri, 04 Mar 2022 18:11:48 +0300 + +--- a/src/lottie/lottiemodel.h ++++ b/src/lottie/lottiemodel.h +@@ -1025,16 +1025,16 @@ public: + LOTTrimData::TrimType type() const {return mTrimType;} + private: + Segment noloop(float start, float end) const{ +- assert(start >= 0); +- assert(end >= 0); ++ if (!(start >= 0)) start = 0; ++ if (!(end >= 0)) end = 0; + Segment s; + s.start = std::min(start, end); + s.end = std::max(start, end); + return s; + } + Segment loop(float start, float end) const{ +- assert(start >= 0); +- assert(end >= 0); ++ if (!(start >= 0)) start = 0; ++ if (!(end >= 0)) end = 0; + Segment s; + s.start = std::max(start, end); + s.end = std::min(start, end); +--- a/src/vector/freetype/v_ft_stroker.cpp ++++ b/src/vector/freetype/v_ft_stroker.cpp +@@ -18,6 +18,7 @@ + + #include "v_ft_stroker.h" + #include <assert.h> ++#include <limits.h> + #include <stdlib.h> + #include <string.h> + #include "v_ft_math.h" +@@ -631,6 +632,8 @@ Fail: + static void ft_stroke_border_export(SW_FT_StrokeBorder border, + SW_FT_Outline* outline) + { ++ if ((ulong)outline->n_points + border->num_points > SHRT_MAX) return; ++ + /* copy point locations */ + memcpy(outline->points + outline->n_points, border->points, + border->num_points * sizeof(SW_FT_Vector)); diff -Nru rlottie-0.1+dfsg/debian/patches/Check-buffer-length.patch rlottie-0.1+dfsg/debian/patches/Check-buffer-length.patch --- rlottie-0.1+dfsg/debian/patches/Check-buffer-length.patch 2021-05-22 19:07:06.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Check-buffer-length.patch 2026-07-07 15:08:38.000000000 +0300 @@ -19,7 +19,7 @@ while (count--) { int x = spans->x + offsetX; int l = spans->len; -+ if (x > bufferLen || l > bufferLen || x + l > bufferLen) { ++ if (x < 0 || x + l > bufferLen) { + return; + } uchar *ptr = buffer + x; @@ -36,7 +36,7 @@ while (count--) { int x = spans->x + offsetX; int l = spans->len; -+ if (x > bufferLen || l > bufferLen || x + l > bufferLen) { ++ if (x < 0 || x + l > bufferLen) { + return; + } uchar *ptr = buffer + x; @@ -52,7 +52,7 @@ while (count--) { int x = spans->x + offsetX; int l = spans->len; -+ if (x > bufferLen || l > bufferLen || x + l > bufferLen) { ++ if (x < 0 || x + l > bufferLen) { + return; + } uchar *ptr = buffer + x; @@ -68,7 +68,7 @@ while (count--) { int x = spans->x + offsetX; int l = spans->len; -+ if (x > bufferLen || l > bufferLen || x + l > bufferLen || x + l < 0) { ++ if (x < 0 || x + l > bufferLen) { + return; + } uchar *ptr = buffer + x; diff -Nru rlottie-0.1+dfsg/debian/patches/Check-empty-frames.patch rlottie-0.1+dfsg/debian/patches/Check-empty-frames.patch --- rlottie-0.1+dfsg/debian/patches/Check-empty-frames.patch 1970-01-01 03:00:00.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Check-empty-frames.patch 2026-07-07 15:08:38.000000000 +0300 @@ -0,0 +1,16 @@ +Description: Check that animation has frames before accessing +Bug: https://github.com/Samsung/rlottie/issues/522 +Origin: https://github.com/Samsung/rlottie/pull/523/commits/7446363b482978746bd6e7b5dbc4f49553f9f50f +Author: Nicholas Guriev <[email protected]> +Last-Update: Wed, 02 Mar 2022 19:12:30 +0300 + +--- a/src/lottie/lottiemodel.h ++++ b/src/lottie/lottiemodel.h +@@ -380,6 +380,7 @@ public: + value().toPath(path); + } else { + const auto &vec = animation().mKeyFrames; ++ if (vec.empty()) return; + if (vec.front().mStartFrame >= frameNo) + return vec.front().mValue.mStartValue.toPath(path); + if(vec.back().mEndFrame <= frameNo) diff -Nru rlottie-0.1+dfsg/debian/patches/Empty-animation-data.patch rlottie-0.1+dfsg/debian/patches/Empty-animation-data.patch --- rlottie-0.1+dfsg/debian/patches/Empty-animation-data.patch 1970-01-01 03:00:00.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Empty-animation-data.patch 2026-07-07 15:07:10.000000000 +0300 @@ -0,0 +1,27 @@ +Subject: Fix crash when path animation data is empty +From: Subhransu Mohanty <[email protected]> +Date: Mon, 25 Jan 2021 10:47:25 +0900 +Bug: https://github.com/Samsung/rlottie/issues/460 +Bug-Debian: https://bugs.debian.org/974095 +Bug-Debian: https://bugs.debian.org/994614 +Origin: https://github.com/Samsung/rlottie/commit/1cb2021d6883ebe41c17e710fc90a225f038cb51 +Last-Update: Sat, 27 Nov 2021 16:41:51 +0300 + +--- + src/lottie/lottiemodel.h | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/src/lottie/lottiemodel.h ++++ b/src/lottie/lottiemodel.h +@@ -124,6 +124,11 @@ public: + static void lerp(const LottieShapeData& start, const LottieShapeData& end, float t, VPath& result) + { + result.reset(); ++ // test for empty animation data. ++ if (start.mPoints.empty() || end.mPoints.empty()) ++ { ++ return; ++ } + auto size = std::min(start.mPoints.size(), end.mPoints.size()); + /* reserve exact memory requirement at once + * ptSize = size + 1(size + close) diff -Nru rlottie-0.1+dfsg/debian/patches/Finite-loop-in-VBezier-tAtLength.patch rlottie-0.1+dfsg/debian/patches/Finite-loop-in-VBezier-tAtLength.patch --- rlottie-0.1+dfsg/debian/patches/Finite-loop-in-VBezier-tAtLength.patch 1970-01-01 03:00:00.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Finite-loop-in-VBezier-tAtLength.patch 2026-07-07 15:07:41.000000000 +0300 @@ -0,0 +1,31 @@ +Description: Finite loop in VBezier::tAtLength() +Origin: https://github.com/Samsung/rlottie/commit/625bc4c48a2d5d45661fd56202f8a1af78b01195 +Forwarded: https://github.com/Samsung/rlottie/pull/483 +Author: Nicholas Guriev <[email protected]> +Last-Update: Wed, 16 Feb 2022 23:14:47 +0300 + +--- a/src/vector/vbezier.cpp ++++ b/src/vector/vbezier.cpp +@@ -85,12 +85,12 @@ float VBezier::tAtLength(float l) const + t *= 0.5; + + float lastBigger = 1.0; +- while (1) { ++ for (int num = 0; num < 100500; num++) { + VBezier right = *this; + VBezier left; + right.parameterSplitLeft(t, &left); + float lLen = left.length(); +- if (fabs(lLen - l) < error) break; ++ if (fabs(lLen - l) < error) return t; + + if (lLen < l) { + t += (lastBigger - t) * 0.5f; +@@ -99,6 +99,7 @@ float VBezier::tAtLength(float l) const + t -= t * 0.5f; + } + } ++ vWarning << "no convergence"; + return t; + } + diff -Nru rlottie-0.1+dfsg/debian/patches/Fix-crash-on-invalid-data.patch rlottie-0.1+dfsg/debian/patches/Fix-crash-on-invalid-data.patch --- rlottie-0.1+dfsg/debian/patches/Fix-crash-on-invalid-data.patch 2021-05-22 19:07:06.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Fix-crash-on-invalid-data.patch 2026-07-07 15:16:09.000000000 +0300 @@ -52,17 +52,7 @@ void LOTGradient::update(std::unique_ptr<VGradient> &grad, int frameNo) --- a/src/vector/vdrawhelper.cpp +++ b/src/vector/vdrawhelper.cpp -@@ -146,6 +146,9 @@ bool VGradientCache::generateGradientCol - float opacity, - uint32_t *colorTable, int size) - { -+ if (stops.empty()) { -+ return false; -+ } - int dist, idist, pos = 0; - size_t i; - bool alpha = false; -@@ -165,7 +168,7 @@ bool VGradientCache::generateGradientCol +@@ -165,7 +165,7 @@ bool VGradientCache::generateGradientCol colorTable[pos++] = curColor; diff -Nru rlottie-0.1+dfsg/debian/patches/Fixed-signed-shift-issue.patch rlottie-0.1+dfsg/debian/patches/Fixed-signed-shift-issue.patch --- rlottie-0.1+dfsg/debian/patches/Fixed-signed-shift-issue.patch 1970-01-01 03:00:00.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Fixed-signed-shift-issue.patch 2026-07-07 15:16:09.000000000 +0300 @@ -0,0 +1,64 @@ +Description: Fixed signed shift issue, CVE-2026-10305 +Author: Michal Szczecinski <[email protected]> +Origin: https://github.com/Samsung/rlottie/commit/b4f5101a4d1a8da60cc14cfd05608551b3448c77 +Forwarded: https://github.com/Samsung/rlottie/pull/587 +Bug-Debian: https://bugs.debian.org/1139179 +Acked-By: Nicholas Guriev <[email protected]> +Last-Update: Tue, 09 Jun 2026 16:16:06 +0300 + +--- a/src/vector/freetype/v_ft_raster.cpp ++++ b/src/vector/freetype/v_ft_raster.cpp +@@ -198,17 +198,17 @@ typedef struct SW_FT_Outline_Funcs_ { + #define ONE_PIXEL (1L << PIXEL_BITS) + #define PIXEL_MASK (-1L << PIXEL_BITS) + #define TRUNC(x) ((TCoord)((x) >> PIXEL_BITS)) +-#define SUBPIXELS(x) ((TPos)(x) << PIXEL_BITS) ++#define SUBPIXELS(x) ((TPos)((unsigned long)(x) << PIXEL_BITS)) + #define FLOOR(x) ((x) & -ONE_PIXEL) + #define CEILING(x) (((x) + ONE_PIXEL - 1) & -ONE_PIXEL) + #define ROUND(x) (((x) + ONE_PIXEL / 2) & -ONE_PIXEL) + + #if PIXEL_BITS >= 6 +-#define UPSCALE(x) ((x) << (PIXEL_BITS - 6)) ++#define UPSCALE(x) ((TPos)((unsigned long)(x) << (PIXEL_BITS - 6))) + #define DOWNSCALE(x) ((x) >> (PIXEL_BITS - 6)) + #else +-#define UPSCALE(x) ((x) >> (6 - PIXEL_BITS)) +-#define DOWNSCALE(x) ((x) << (6 - PIXEL_BITS)) ++#define UPSCALE(x) ((x) >> (6 - PIXEL_BITS)) ++#define DOWNSCALE(x) ((TPos)((unsigned long)(x) << (6 - PIXEL_BITS))) + #endif + + /* Compute `dividend / divisor' and return both its quotient and */ +@@ -1072,7 +1072,7 @@ static int SW_FT_Outline_Decompose(const + void* user) + { + #undef SCALED +-#define SCALED(x) (((x) << shift) - delta) ++#define SCALED(x) ((TPos)((unsigned long)(x) << shift) - delta) + + SW_FT_Vector v_last; + SW_FT_Vector v_control; +--- a/src/vector/vdrawhelper.cpp ++++ b/src/vector/vdrawhelper.cpp +@@ -156,6 +156,11 @@ bool VGradientCache::generateGradientCol + + if (!vCompare(opacity, 1.0f)) alpha = true; + ++ if (stopCount == 0) { ++ for (int j = 0; j < size; ++j) colorTable[j] = 0; ++ return alpha; ++ } ++ + start = stops.data(); + curr = start; + if (!curr->second.isOpaque()) alpha = true; +@@ -171,7 +176,7 @@ bool VGradientCache::generateGradientCol + fpos += incr; + } + +- for (i = 0; i < stopCount - 1; ++i) { ++ for (i = 0; i + 1 < stopCount; ++i) { + curr = (start + i); + next = (start + i + 1); + delta = 1 / (next->first - curr->first); diff -Nru rlottie-0.1+dfsg/debian/patches/Fixed-vpath-potential-issue.patch rlottie-0.1+dfsg/debian/patches/Fixed-vpath-potential-issue.patch --- rlottie-0.1+dfsg/debian/patches/Fixed-vpath-potential-issue.patch 1970-01-01 03:00:00.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Fixed-vpath-potential-issue.patch 2026-07-07 15:16:04.000000000 +0300 @@ -0,0 +1,29 @@ +Description: Fix potential huge memory allocation in VPath + Fixes CVE-2026-47319 +Author: Michal Szczecinski <[email protected]> +Origin: https://github.com/Samsung/rlottie/commit/5def9f402b1cb5b09f52655e414f0afba4ffd959 +Forwarded: https://github.com/Samsung/rlottie/pull/588 +Bug-Debian: https://bugs.debian.org/1138919 +Acked-By: Nicholas Guriev <[email protected]> +Last-Update: Tue, 09 Jun 2026 16:16:04 +0300 + +--- a/src/vector/vpath.cpp ++++ b/src/vector/vpath.cpp +@@ -514,6 +514,8 @@ void VPath::VPathData::addPolystar(float + float outerRoundness, float startAngle, + float cx, float cy, VPath::Direction dir) + { ++ constexpr float MAX_POLY_POINTS = 1024.0f; ++ if (points > MAX_POLY_POINTS) points = MAX_POLY_POINTS; + const static float POLYSTAR_MAGIC_NUMBER = 0.47829f / 0.28f; + float currentAngle = (startAngle - 90.0f) * K_PI / 180.0f; + float x; +@@ -619,6 +621,8 @@ void VPath::VPathData::addPolygon(float + VPath::Direction dir) + { + // TODO: Need to support floating point number for number of points ++ constexpr float MAX_POLY_POINTS = 1024.0f; ++ if (points > MAX_POLY_POINTS) points = MAX_POLY_POINTS; + const static float POLYGON_MAGIC_NUMBER = 0.25; + float currentAngle = (startAngle - 90.0f) * K_PI / 180.0f; + float x; diff -Nru rlottie-0.1+dfsg/debian/patches/Fix-heap-buffer-overflow-from-short-truncation.patch rlottie-0.1+dfsg/debian/patches/Fix-heap-buffer-overflow-from-short-truncation.patch --- rlottie-0.1+dfsg/debian/patches/Fix-heap-buffer-overflow-from-short-truncation.patch 1970-01-01 03:00:00.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Fix-heap-buffer-overflow-from-short-truncation.patch 2026-07-07 15:16:09.000000000 +0300 @@ -0,0 +1,31 @@ +Description: Fix heap buffer overflow from short truncation of span Y coordinate + SW_FT_Span.y is a 16-bit short, so limit our coordinates appropriately. The + clamp (and its "is an integer" comment) was inherited from upstream FreeType's + ftgrays.c, where FT_Span has no y field and the scanline y is carried + separately as an int. rLottie added a `short y` to the span struct without + adapting the clamp, so the INT_MAX bound never prevented the (short) narrowing + at the store site. + . + This patch based on Michal Maciola's pull request with the numeral literals + replaced by the standard macro from the <limits.h> header. +Forwarded: https://github.com/Samsung/rlottie/pull/595 +Author: Michal Maciola <[email protected]> +Acked-By: Nicholas Guriev <[email protected]> +Last-Updated: Thu, 02 Jul 2026 07:55:03 +0300 + +--- a/src/vector/freetype/v_ft_raster.cpp ++++ b/src/vector/freetype/v_ft_raster.cpp +@@ -935,10 +935,10 @@ static void gray_hline(RAS_ARG_ TCoord x + x += (TCoord)ras.min_ex; + + /* SW_FT_Span.x is a 16-bit short, so limit our coordinates appropriately */ +- if (x >= 32767) x = 32767; ++ if (x >= SHRT_MAX) x = SHRT_MAX; + +- /* SW_FT_Span.y is an integer, so limit our coordinates appropriately */ +- if (y >= SW_FT_INT_MAX) y = SW_FT_INT_MAX; ++ /* SW_FT_Span.y is a 16-bit short, so limit our coordinates appropriately */ ++ if (y >= SHRT_MAX) y = SHRT_MAX; + + if (coverage) { + SW_FT_Span* span; diff -Nru rlottie-0.1+dfsg/debian/patches/Fortify-FreeType-raster.patch rlottie-0.1+dfsg/debian/patches/Fortify-FreeType-raster.patch --- rlottie-0.1+dfsg/debian/patches/Fortify-FreeType-raster.patch 1970-01-01 03:00:00.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Fortify-FreeType-raster.patch 2026-07-07 15:13:24.000000000 +0300 @@ -0,0 +1,84 @@ +Description: Verify array length before writing in Freetype + An attempt to fix CVE-2021-31321 based on John Preston's commit. + https://github.com/desktop-app/rlottie/commit/d369d84e868352886cee48eecb60b462f6dfe067 + . + Also check the number of ycells in gray_find_cell(). +Author: Nicholas Guriev <[email protected]> +Last-Update: Thu, 23 Apr 2026 13:06:37 +0300 + +--- a/src/vector/freetype/v_ft_raster.cpp ++++ b/src/vector/freetype/v_ft_raster.cpp +@@ -67,6 +67,17 @@ + } \ + while (0) + ++#include <iterator> ++#if 201103L <= __cplusplus && __cplusplus < 201703L ++namespace std { ++ ++// Taken from https://stackoverflow.com/a/18078435/5000805 ++template<class T, size_t N> ++constexpr size_t size(T (&)[N]) { return N; } ++ ++} ++#endif ++ + #include <limits.h> + #include <setjmp.h> + #include <stddef.h> +@@ -360,6 +371,7 @@ static void gray_init_cells(RAS_ARG_ voi + ras.buffer_size = byte_size; + + ras.ycells = (PCell*)buffer; ++ ras.ycount = byte_size / sizeof(PCell); + ras.cells = NULL; + ras.max_cells = 0; + ras.num_cells = 0; +@@ -421,6 +433,7 @@ static PCell gray_find_cell(RAS_ARG) + TPos x = ras.ex; + + if (x > ras.count_ex) x = ras.count_ex; ++ if (ras.ey < 0 || ras.ey >= ras.ycount) ft_longjmp(ras.jump_buffer, 1); + + pcell = &ras.ycells[ras.ey]; + for (;;) { +@@ -707,6 +720,9 @@ static void gray_render_conic(RAS_ARG_ c + gray_split_conic(arc); + arc += 2; + top++; ++ ++ if (top >= static_cast<int>(std::size(ras.lev_stack))) return; ++ + levels[top] = levels[top - 1] = level - 1; + continue; + } +@@ -833,6 +849,8 @@ static void gray_render_cubic(RAS_ARG_ c + } + + Split: ++ if (arc + 6 >= std::end(ras.bez_stack)) return; ++ + gray_split_cubic(arc); + arc += 3; + continue; +@@ -840,9 +858,8 @@ static void gray_render_cubic(RAS_ARG_ c + Draw: + gray_render_line(RAS_VAR_ arc[0].x, arc[0].y); + +- if (arc == ras.bez_stack) return; +- + arc -= 3; ++ if (arc < std::begin(ras.bez_stack)) return; + } + } + +@@ -1378,7 +1395,9 @@ static int gray_raster_render(gray_PRast + SW_FT_UNUSED(raster); + const SW_FT_Outline* outline = (const SW_FT_Outline*)params->source; + ++#ifndef SW_FT_STATIC_RASTER + gray_TWorker worker[1]; ++#endif + + TCell buffer[SW_FT_RENDER_POOL_SIZE / sizeof(TCell)]; + long buffer_size = sizeof(buffer); diff -Nru rlottie-0.1+dfsg/debian/patches/Freetype-raster.patch rlottie-0.1+dfsg/debian/patches/Freetype-raster.patch --- rlottie-0.1+dfsg/debian/patches/Freetype-raster.patch 2021-05-29 11:13:17.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Freetype-raster.patch 1970-01-01 03:00:00.000000000 +0300 @@ -1,56 +0,0 @@ -Description: Verify array length before writing in Freetype - An attempt to fix CVE-2021-31321 based on John Preston's commit. - https://github.com/desktop-app/rlottie/commit/d369d84e868352886cee48eecb60b462f6dfe067 -Author: Nicholas Guriev <[email protected]> -Last-Update: Sat, 22 May 2021 19:07:06 +0300 - ---- a/src/vector/freetype/v_ft_raster.cpp -+++ b/src/vector/freetype/v_ft_raster.cpp -@@ -67,6 +67,17 @@ - } \ - while (0) - -+#include <iterator> -+#if 201103L <= __cplusplus && __cplusplus < 201703L -+namespace std { -+ -+// Taken from https://stackoverflow.com/a/18078435/5000805 -+template<class T, size_t N> -+constexpr size_t size(T (&)[N]) { return N; } -+ -+} -+#endif -+ - #include <limits.h> - #include <setjmp.h> - #include <stddef.h> -@@ -707,6 +718,9 @@ static void gray_render_conic(RAS_ARG_ c - gray_split_conic(arc); - arc += 2; - top++; -+ -+ if (top >= static_cast<int>(std::size(ras.lev_stack))) return; -+ - levels[top] = levels[top - 1] = level - 1; - continue; - } -@@ -835,6 +849,9 @@ static void gray_render_cubic(RAS_ARG_ c - Split: - gray_split_cubic(arc); - arc += 3; -+ -+ if (arc + 4 > std::end(ras.bez_stack)) return; -+ - continue; - - Draw: -@@ -1378,7 +1395,9 @@ static int gray_raster_render(gray_PRast - SW_FT_UNUSED(raster); - const SW_FT_Outline* outline = (const SW_FT_Outline*)params->source; - -+#ifndef SW_FT_STATIC_RASTER - gray_TWorker worker[1]; -+#endif - - TCell buffer[SW_FT_RENDER_POOL_SIZE / sizeof(TCell)]; - long buffer_size = sizeof(buffer); diff -Nru rlottie-0.1+dfsg/debian/patches/Ignore-unspecified-type.patch rlottie-0.1+dfsg/debian/patches/Ignore-unspecified-type.patch --- rlottie-0.1+dfsg/debian/patches/Ignore-unspecified-type.patch 1970-01-01 03:00:00.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Ignore-unspecified-type.patch 2026-07-07 15:07:41.000000000 +0300 @@ -0,0 +1,19 @@ +Description: Ignore animations with objects of unspecified type +Origin: https://github.com/Samsung/rlottie/commit/d1636c7b47481e69d4100f3bff2a7a3be7680286 +Forwarded: https://github.com/Samsung/rlottie/pull/515 +Author: Nicholas Guriev <[email protected]> +Last-Update: Wed, 16 Feb 2022 23:14:47 +0300 + +--- a/src/lottie/lottieparser.cpp ++++ b/src/lottie/lottieparser.cpp +@@ -1140,6 +1140,10 @@ LOTData* LottieParserImpl::parseObjectTy + { + RAPIDJSON_ASSERT(PeekType() == kStringType); + const char *type = GetString(); ++ if (!type) { ++ vWarning << "No object type specified"; ++ return nullptr; ++ } + if (0 == strcmp(type, "gr")) { + return parseGroupObject(); + } else if (0 == strcmp(type, "rc")) { diff -Nru rlottie-0.1+dfsg/debian/patches/Init-keyframe.patch rlottie-0.1+dfsg/debian/patches/Init-keyframe.patch --- rlottie-0.1+dfsg/debian/patches/Init-keyframe.patch 1970-01-01 03:00:00.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Init-keyframe.patch 2026-07-07 15:08:38.000000000 +0300 @@ -0,0 +1,18 @@ +Description: Do default initialization of key frame values +Bug: https://github.com/Samsung/rlottie/issues/522 +Author: Nicholas Guriev <[email protected]> +Last-Update: Wed, 02 Mar 2022 19:12:30 +0300 + +--- a/src/lottie/lottiemodel.h ++++ b/src/lottie/lottiemodel.h +@@ -178,8 +178,8 @@ inline T lerp(const T& start, const T& e + template <typename T> + struct LOTKeyFrameValue + { +- T mStartValue; +- T mEndValue; ++ T mStartValue{}; ++ T mEndValue{}; + T value(float t) const { + return lerp(mStartValue, mEndValue, t); + } diff -Nru rlottie-0.1+dfsg/debian/patches/Limit-recursion-in-LOTLayerItem.patch rlottie-0.1+dfsg/debian/patches/Limit-recursion-in-LOTLayerItem.patch --- rlottie-0.1+dfsg/debian/patches/Limit-recursion-in-LOTLayerItem.patch 1970-01-01 03:00:00.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Limit-recursion-in-LOTLayerItem.patch 2026-07-07 15:16:09.000000000 +0300 @@ -0,0 +1,39 @@ +Description: Limit recurion depth in LOTLayerItem + This patch is based on Michal Szczecinski's commit and adapted to version 0.1 + before Lottie model refactoring. Should fix CVE-2026-47320. + . + https://github.com/Samsung/rlottie/commit/bf689b72b8482c5ea674235854bd11b6d1b42588 +Author: Nicholas Guriev <[email protected]> +Forwarded: not-needed +Bug-Debian: https://bugs.debian.org/1138920 +Last-Update: Tue, 09 Jun 2026 16:16:05 +0300 + +--- a/src/lottie/lottieitem.cpp ++++ b/src/lottie/lottieitem.cpp +@@ -419,8 +419,13 @@ void LOTLayerItem::update(int frameNumbe + + VMatrix LOTLayerItem::matrix(int frameNo) const + { +- return mParentLayer +- ? (mLayerData->matrix(frameNo) * mParentLayer->matrix(frameNo)) ++ return matrix(frameNo, 0); ++} ++ ++VMatrix LOTLayerItem::matrix(int frameNo, int depth) const ++{ ++ return mParentLayer && depth < 64 ++ ? mLayerData->matrix(frameNo) * mParentLayer->matrix(frameNo, depth + 1) + : mLayerData->matrix(frameNo); + } + +--- a/src/lottie/lottieitem.h ++++ b/src/lottie/lottieitem.h +@@ -180,6 +180,8 @@ protected: + float opacity(int frameNo) const {return mLayerData->opacity(frameNo);} + inline DirtyFlag flag() const {return mDirtyFlag;} + bool skipRendering() const {return (!visible() || vIsZero(combinedAlpha()));} ++private: ++ VMatrix matrix(int frameNo, int depth) const; + protected: + std::unique_ptr<LOTLayerMaskItem> mLayerMask; + LOTLayerData *mLayerData{nullptr}; diff -Nru rlottie-0.1+dfsg/debian/patches/No-cyclic-structures.patch rlottie-0.1+dfsg/debian/patches/No-cyclic-structures.patch --- rlottie-0.1+dfsg/debian/patches/No-cyclic-structures.patch 1970-01-01 03:00:00.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/No-cyclic-structures.patch 2026-07-07 15:10:19.000000000 +0300 @@ -0,0 +1,97 @@ +Description: Deal with cyclic structures, avoid stack overflow in Lottie model +Bug: https://github.com/Samsung/rlottie/issues/522 +Author: Nicholas Guriev <[email protected]> +Last-Update: Sat, 10 Sep 2022 18:26:32 +0300 + +--- a/src/lottie/lottiemodel.cpp ++++ b/src/lottie/lottiemodel.cpp +@@ -19,10 +19,26 @@ + #include "lottiemodel.h" + #include <cassert> + #include <iterator> ++#include <set> + #include <stack> + #include "vimageloader.h" + #include "vline.h" + ++bool LOTData::includes(const LOTData *pointer) const { ++ if (this == pointer) { ++ return true; ++ } ++ if (type() != Type::ShapeGroup && type() != Type::Layer) { ++ return false; ++ } ++ for (LOTData *child : static_cast<const LOTGroupData *>(this)->mChildren) { ++ if (child->includes(pointer)) { ++ return true; ++ } ++ } ++ return false; ++} ++ + /* + * We process the iterator objects in the children list + * by iterating from back to front. when we find a repeater object +@@ -73,6 +89,8 @@ public: + + void visit(LOTData *obj) + { ++ if (!mVisited.insert(obj).second) return; ++ + switch (obj->type()) { + case LOTData::Type::ShapeGroup: + case LOTData::Type::Layer: { +@@ -83,6 +101,9 @@ public: + break; + } + } ++ ++private: ++ std::set<LOTData*> mVisited; + }; + + class LottieUpdateStatVisitor { +@@ -120,6 +141,8 @@ public: + } + void visit(LOTData *obj) + { ++ if (!mVisited.insert(obj).second) return; ++ + switch (obj->type()) { + case LOTData::Type::Layer: { + visitLayer(static_cast<LOTLayerData *>(obj)); +@@ -138,6 +161,8 @@ public: + } + } + ++private: ++ std::set<LOTData*> mVisited; + }; + + void LOTCompositionData::processRepeaterObjects() +--- a/src/lottie/lottiemodel.h ++++ b/src/lottie/lottiemodel.h +@@ -460,6 +460,7 @@ public: + } + } + const char* name() const {return shortString() ? mData._buffer : mPtr;} ++ bool includes(const LOTData *) const; + private: + static constexpr unsigned char maxShortStringLength = 14; + void setShortString(bool value) {mData._shortString = value;} +--- a/src/lottie/lottieparser.cpp ++++ b/src/lottie/lottieparser.cpp +@@ -608,7 +608,12 @@ void LottieParserImpl::resolveLayerRefs( + if (layer->mLayerType == LayerType::Image) { + layer->extra()->mAsset = search->second; + } else if (layer->mLayerType == LayerType::Precomp) { +- layer->mChildren = search->second->mLayers; ++ layer->mChildren.clear(); ++ layer->mChildren.reserve(search->second->mLayers.size()); ++ for (LOTData *candidate : search->second->mLayers) { ++ if (candidate->includes(layer)) continue; ++ layer->mChildren.push_back(candidate); ++ } + layer->setStatic(layer->isStatic() && + search->second->isStatic()); + } diff -Nru rlottie-0.1+dfsg/debian/patches/No-deadlock.patch rlottie-0.1+dfsg/debian/patches/No-deadlock.patch --- rlottie-0.1+dfsg/debian/patches/No-deadlock.patch 1970-01-01 03:00:00.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/No-deadlock.patch 2026-07-07 15:10:17.000000000 +0300 @@ -0,0 +1,15 @@ +Description: Realize a lock at any end of VRleTask +Forwarded: https://github.com/Samsung/rlottie/pull/523/commits/0b7d6354c3f76848d87fac4a15e12c7b3401a17f +Author: Nicholas Guriev <[email protected]> +Last-Update: Sun, 29 May 2022 14:24:41 +0300 + +--- a/src/vector/vraster.cpp ++++ b/src/vector/vraster.cpp +@@ -370,6 +370,7 @@ struct VRleTask { + { + if (mPath.points().size() > SHRT_MAX || + mPath.points().size() + mPath.segments() > SHRT_MAX) { ++ mRle.notify(); + return; + } + diff -Nru rlottie-0.1+dfsg/debian/patches/Positive-points.patch rlottie-0.1+dfsg/debian/patches/Positive-points.patch --- rlottie-0.1+dfsg/debian/patches/Positive-points.patch 1970-01-01 03:00:00.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Positive-points.patch 2026-07-07 15:08:38.000000000 +0300 @@ -0,0 +1,20 @@ +Description: Points counter must be positive +Bug: https://github.com/Samsung/rlottie/issues/522 +Origin: https://github.com/Samsung/rlottie/pull/523/commits/19fc8e9c2804ccfba5bee3eb18bc952a87684490 +Author: Nicholas Guriev <[email protected]> +Last-Update: Wed, 02 Mar 2022 19:12:30 +0300 + +--- a/src/lottie/lottieitem.cpp ++++ b/src/lottie/lottieitem.cpp +@@ -1151,6 +1151,11 @@ void LOTPolystarItem::updatePath(VPath & + path.reset(); + VMatrix m; + ++ if (!(points > 0)) { ++ vWarning << "The number of path points is below zero or NaN at all"; ++ return; ++ } ++ + if (mData->mPolyType == LOTPolystarData::PolyType::Star) { + path.addPolystar(points, innerRadius, outerRadius, innerRoundness, + outerRoundness, 0.0, 0.0, 0.0, mData->direction()); diff -Nru rlottie-0.1+dfsg/debian/patches/Reject-reversed-frames.patch rlottie-0.1+dfsg/debian/patches/Reject-reversed-frames.patch --- rlottie-0.1+dfsg/debian/patches/Reject-reversed-frames.patch 1970-01-01 03:00:00.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Reject-reversed-frames.patch 2026-07-07 15:07:41.000000000 +0300 @@ -0,0 +1,19 @@ +Description: Reject reversed frames +Origin: https://github.com/Samsung/rlottie/commit/327fb7dbaad225555d5ca567b9adee9ce5f879f4 +Forwarded: https://github.com/Samsung/rlottie/pull/483 +Author: Nicholas Guriev <[email protected]> +Last-Update: Wed, 16 Feb 2022 23:14:47 +0300 + +--- a/src/lottie/lottieparser.cpp ++++ b/src/lottie/lottieparser.cpp +@@ -661,6 +661,10 @@ void LottieParserImpl::parseComposition( + // don't have a valid bodymovin header + return; + } ++ if (comp->mStartFrame > comp->mEndFrame) { ++ // reversed animation? missing data? ++ return; ++ } + if (!IsValid()) { + return; + } diff -Nru rlottie-0.1+dfsg/debian/patches/series rlottie-0.1+dfsg/debian/patches/series --- rlottie-0.1+dfsg/debian/patches/series 2025-11-18 21:23:20.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/series 2026-07-07 15:16:09.000000000 +0300 @@ -6,11 +6,26 @@ Check-buffer-length.patch Fix-crash-in-malformed-animations.patch Fix-crash-on-invalid-data.patch -Freetype-raster.patch +Fortify-FreeType-raster.patch Fortify-lottie-parser.patch Extend-mDash-array.patch Include-limits-header.patch -Zero-corrupt-point.patch Avoid-nullptr-in-solidColor.patch +Empty-animation-data.patch +Finite-loop-in-VBezier-tAtLength.patch +Reject-reversed-frames.patch +Ignore-unspecified-type.patch +Init-keyframe.patch +Check-empty-frames.patch +No-cyclic-structures.patch +Positive-points.patch +Stop-VBezier-length-overflow.patch +Avoid-assertion-failures.patch +No-deadlock.patch +Atomic-render.patch CVE-2025-0634-CVE-2025-53074-CVE-2025-53075.patch +Fixed-vpath-potential-issue.patch +Limit-recursion-in-LOTLayerItem.patch +Fixed-signed-shift-issue.patch +Fix-heap-buffer-overflow-from-short-truncation.patch diff -Nru rlottie-0.1+dfsg/debian/patches/Stop-VBezier-length-overflow.patch rlottie-0.1+dfsg/debian/patches/Stop-VBezier-length-overflow.patch --- rlottie-0.1+dfsg/debian/patches/Stop-VBezier-length-overflow.patch 1970-01-01 03:00:00.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Stop-VBezier-length-overflow.patch 2026-07-07 15:08:38.000000000 +0300 @@ -0,0 +1,65 @@ +Description: Stop computing Bézier curve length when float overflow occurs after splitting +Bug: https://github.com/Samsung/rlottie/issues/522 +Origin: https://github.com/Samsung/rlottie/pull/523/commits/f7b72f1ed133882f1d5a75ede5fe6b6fc4cbea1d +Author: Nicholas Guriev <[email protected]> +Last-Update: Wed, 02 Mar 2022 19:12:30 +0300 + +--- a/src/vector/vbezier.cpp ++++ b/src/vector/vbezier.cpp +@@ -42,7 +42,7 @@ float VBezier::length() const + VBezier left, right; /* bez poly splits */ + float len = 0.0; /* arc length */ + float chord; /* chord length */ +- float length; ++ float length = 0; + + len = len + VLine::length(x1, y1, x2, y2); + len = len + VLine::length(x2, y2, x3, y3); +@@ -52,9 +52,10 @@ float VBezier::length() const + + if ((len - chord) > 0.01) { + split(&left, &right); /* split in two */ +- length = left.length() + /* try left side */ +- right.length(); /* try right side */ +- ++ if (*this != left) ++ length += left.length(); /* try left side */ ++ if (*this != right) ++ length += right.length(); /* try right side */ + return length; + } + +--- a/src/vector/vbezier.h ++++ b/src/vector/vbezier.h +@@ -19,11 +19,14 @@ + #ifndef VBEZIER_H + #define VBEZIER_H + ++#include <tuple> + #include <vpoint.h> + + V_BEGIN_NAMESPACE + + class VBezier { ++ friend bool operator == (const VBezier &l, const VBezier &r); ++ + public: + VBezier() = default; + VPointF pointAt(float t) const; +@@ -129,6 +132,16 @@ inline void VBezier::split(VBezier *firs + firstHalf->y4 = secondHalf->y1 = (firstHalf->y3 + secondHalf->y2) * 0.5f; + } + ++inline bool operator == (const VBezier &l, const VBezier &r) ++{ ++ return std::tie(l.x1, l.y1, l.x2, l.y2, l.x3, l.y3, l.x4, l.y4) ++ == std::tie(r.x1, r.y1, r.x2, r.y2, r.x3, r.y3, r.x4, r.y4); ++} ++inline bool operator != (const VBezier &l, const VBezier &r) ++{ ++ return !(l == r); ++} ++ + V_END_NAMESPACE + + #endif // VBEZIER_H diff -Nru rlottie-0.1+dfsg/debian/patches/Zero-corrupt-point.patch rlottie-0.1+dfsg/debian/patches/Zero-corrupt-point.patch --- rlottie-0.1+dfsg/debian/patches/Zero-corrupt-point.patch 2021-05-29 11:13:17.000000000 +0300 +++ rlottie-0.1+dfsg/debian/patches/Zero-corrupt-point.patch 1970-01-01 03:00:00.000000000 +0300 @@ -1,19 +0,0 @@ -Description: Put zero point in case of corrupted shape data - Upstream rlottie at the master branch seems to be not affected by the crash. - They refactored the corresponding code. -Bug-Debian: https://bugs.debian.org/974095 -Forwarded: not-needed -Author: Nicholas Guriev <[email protected]> -Last-Update: Mon, 24 May 2021 18:09:20 +0300 - ---- a/src/lottie/lottieparser.cpp -+++ b/src/lottie/lottieparser.cpp -@@ -1914,7 +1914,7 @@ void LottieParserImpl::getValue(LottieSh - if (mInPoint.size() != mOutPoint.size() || - mInPoint.size() != mVertices.size()) { - vCritical << "The Shape data are corrupted"; -- points = std::vector<VPointF>(); -+ points.emplace_back(); - } else { - auto size = mVertices.size(); - points.reserve(3 * size + 4);
diffstat for rlottie_0.1+dfsg-2+deb11u1 rlottie_0.1+dfsg-2+deb11u2
lottie/lottieanimation.cpp | 10 ++++------
lottie/lottieitem.cpp | 14 ++++++++++++--
lottie/lottieitem.h | 2 ++
lottie/lottiemodel.cpp | 25 +++++++++++++++++++++++++
lottie/lottiemodel.h | 19 +++++++++++++------
lottie/lottieparser.cpp | 17 +++++++++++++++--
vector/freetype/v_ft_raster.cpp | 26 +++++++++++++-------------
vector/freetype/v_ft_stroker.cpp | 3 +++
vector/vbezier.cpp | 14 ++++++++------
vector/vbezier.h | 13 +++++++++++++
vector/vdrawhelper.cpp | 10 ++++++----
vector/vpath.cpp | 4 ++++
vector/vraster.cpp | 1 +
vector/vrle.cpp | 8 ++++----
14 files changed, 123 insertions(+), 43 deletions(-)
diff --unified -r -p -x .git -x .pc -x debian rlottie_0.1+dfsg-2+deb11u1/src/lottie/lottieanimation.cpp rlottie_0.1+dfsg-2+deb11u2/src/lottie/lottieanimation.cpp
--- rlottie_0.1+dfsg-2+deb11u1/src/lottie/lottieanimation.cpp 2026-07-07 16:14:58.510885381 +0300
+++ rlottie_0.1+dfsg-2+deb11u2/src/lottie/lottieanimation.cpp 2026-07-07 16:15:06.111042839 +0300
@@ -74,7 +74,7 @@ private:
std::shared_ptr<LOTModel> mModel;
std::unique_ptr<LOTCompItem> mCompItem;
SharedRenderTask mTask;
- std::atomic<bool> mRenderInProgress;
+ std::atomic_flag mRenderInProgress;
};
void AnimationImpl::setValue(const std::string &keypath, LOTVariant &&value)
@@ -104,17 +104,15 @@ bool AnimationImpl::update(size_t frameN
Surface AnimationImpl::render(size_t frameNo, const Surface &surface, bool keepAspectRatio)
{
- bool renderInProgress = mRenderInProgress.load();
- if (renderInProgress) {
+ if (mRenderInProgress.test_and_set()) {
vCritical << "Already Rendering Scheduled for this Animation";
return surface;
}
- mRenderInProgress.store(true);
update(frameNo,
VSize(int(surface.drawRegionWidth()), int(surface.drawRegionHeight())), keepAspectRatio);
mCompItem->render(surface);
- mRenderInProgress.store(false);
+ mRenderInProgress.clear();
return surface;
}
@@ -123,7 +121,7 @@ void AnimationImpl::init(const std::shar
{
mModel = model;
mCompItem = std::make_unique<LOTCompItem>(mModel.get());
- mRenderInProgress = false;
+ mRenderInProgress.clear();
}
#ifdef LOTTIE_THREAD_SUPPORT
diff --unified -r -p -x .git -x .pc -x debian rlottie_0.1+dfsg-2+deb11u1/src/lottie/lottieitem.cpp rlottie_0.1+dfsg-2+deb11u2/src/lottie/lottieitem.cpp
--- rlottie_0.1+dfsg-2+deb11u1/src/lottie/lottieitem.cpp 2026-07-07 16:14:58.642888116 +0300
+++ rlottie_0.1+dfsg-2+deb11u2/src/lottie/lottieitem.cpp 2026-07-07 16:15:06.191044497 +0300
@@ -419,8 +419,13 @@ void LOTLayerItem::update(int frameNumbe
VMatrix LOTLayerItem::matrix(int frameNo) const
{
- return mParentLayer
- ? (mLayerData->matrix(frameNo) * mParentLayer->matrix(frameNo))
+ return matrix(frameNo, 0);
+}
+
+VMatrix LOTLayerItem::matrix(int frameNo, int depth) const
+{
+ return mParentLayer && depth < 64
+ ? mLayerData->matrix(frameNo) * mParentLayer->matrix(frameNo, depth + 1)
: mLayerData->matrix(frameNo);
}
@@ -1137,6 +1142,11 @@ void LOTPolystarItem::updatePath(VPath &
path.reset();
VMatrix m;
+ if (!(points > 0)) {
+ vWarning << "The number of path points is below zero or NaN at all";
+ return;
+ }
+
if (mData->mPolyType == LOTPolystarData::PolyType::Star) {
path.addPolystar(points, innerRadius, outerRadius, innerRoundness,
outerRoundness, 0.0, 0.0, 0.0, mData->direction());
diff --unified -r -p -x .git -x .pc -x debian rlottie_0.1+dfsg-2+deb11u1/src/lottie/lottieitem.h rlottie_0.1+dfsg-2+deb11u2/src/lottie/lottieitem.h
--- rlottie_0.1+dfsg-2+deb11u1/src/lottie/lottieitem.h 2026-07-07 16:14:58.562886459 +0300
+++ rlottie_0.1+dfsg-2+deb11u2/src/lottie/lottieitem.h 2026-07-07 16:15:06.191044497 +0300
@@ -180,6 +180,8 @@ protected:
float opacity(int frameNo) const {return mLayerData->opacity(frameNo);}
inline DirtyFlag flag() const {return mDirtyFlag;}
bool skipRendering() const {return (!visible() || vIsZero(combinedAlpha()));}
+private:
+ VMatrix matrix(int frameNo, int depth) const;
protected:
std::unique_ptr<LOTLayerMaskItem> mLayerMask;
LOTLayerData *mLayerData{nullptr};
diff --unified -r -p -x .git -x .pc -x debian rlottie_0.1+dfsg-2+deb11u1/src/lottie/lottiemodel.cpp rlottie_0.1+dfsg-2+deb11u2/src/lottie/lottiemodel.cpp
--- rlottie_0.1+dfsg-2+deb11u1/src/lottie/lottiemodel.cpp 2026-07-07 16:14:58.722889773 +0300
+++ rlottie_0.1+dfsg-2+deb11u2/src/lottie/lottiemodel.cpp 2026-07-07 16:15:05.975040022 +0300
@@ -19,10 +19,26 @@
#include "lottiemodel.h"
#include <cassert>
#include <iterator>
+#include <set>
#include <stack>
#include "vimageloader.h"
#include "vline.h"
+bool LOTData::includes(const LOTData *pointer) const {
+ if (this == pointer) {
+ return true;
+ }
+ if (type() != Type::ShapeGroup && type() != Type::Layer) {
+ return false;
+ }
+ for (LOTData *child : static_cast<const LOTGroupData *>(this)->mChildren) {
+ if (child->includes(pointer)) {
+ return true;
+ }
+ }
+ return false;
+}
+
/*
* We process the iterator objects in the children list
* by iterating from back to front. when we find a repeater object
@@ -73,6 +89,8 @@ public:
void visit(LOTData *obj)
{
+ if (!mVisited.insert(obj).second) return;
+
switch (obj->type()) {
case LOTData::Type::ShapeGroup:
case LOTData::Type::Layer: {
@@ -83,6 +101,9 @@ public:
break;
}
}
+
+private:
+ std::set<LOTData*> mVisited;
};
class LottieUpdateStatVisitor {
@@ -120,6 +141,8 @@ public:
}
void visit(LOTData *obj)
{
+ if (!mVisited.insert(obj).second) return;
+
switch (obj->type()) {
case LOTData::Type::Layer: {
visitLayer(static_cast<LOTLayerData *>(obj));
@@ -138,6 +161,8 @@ public:
}
}
+private:
+ std::set<LOTData*> mVisited;
};
void LOTCompositionData::processRepeaterObjects()
diff --unified -r -p -x .git -x .pc -x debian rlottie_0.1+dfsg-2+deb11u1/src/lottie/lottiemodel.h rlottie_0.1+dfsg-2+deb11u2/src/lottie/lottiemodel.h
--- rlottie_0.1+dfsg-2+deb11u1/src/lottie/lottiemodel.h 2026-07-07 16:14:58.802891431 +0300
+++ rlottie_0.1+dfsg-2+deb11u2/src/lottie/lottiemodel.h 2026-07-07 16:15:06.055041678 +0300
@@ -124,6 +124,11 @@ public:
static void lerp(const LottieShapeData& start, const LottieShapeData& end, float t, VPath& result)
{
result.reset();
+ // test for empty animation data.
+ if (start.mPoints.empty() || end.mPoints.empty())
+ {
+ return;
+ }
auto size = std::min(start.mPoints.size(), end.mPoints.size());
/* reserve exact memory requirement at once
* ptSize = size + 1(size + close)
@@ -173,8 +178,8 @@ inline T lerp(const T& start, const T& e
template <typename T>
struct LOTKeyFrameValue
{
- T mStartValue;
- T mEndValue;
+ T mStartValue{};
+ T mEndValue{};
T value(float t) const {
return lerp(mStartValue, mEndValue, t);
}
@@ -375,6 +380,7 @@ public:
value().toPath(path);
} else {
const auto &vec = animation().mKeyFrames;
+ if (vec.empty()) return;
if (vec.front().mStartFrame >= frameNo)
return vec.front().mValue.mStartValue.toPath(path);
if(vec.back().mEndFrame <= frameNo)
@@ -454,6 +460,7 @@ public:
}
}
const char* name() const {return shortString() ? mData._buffer : mPtr;}
+ bool includes(const LOTData *) const;
private:
static constexpr unsigned char maxShortStringLength = 14;
void setShortString(bool value) {mData._shortString = value;}
@@ -1018,16 +1025,16 @@ public:
LOTTrimData::TrimType type() const {return mTrimType;}
private:
Segment noloop(float start, float end) const{
- assert(start >= 0);
- assert(end >= 0);
+ if (!(start >= 0)) start = 0;
+ if (!(end >= 0)) end = 0;
Segment s;
s.start = std::min(start, end);
s.end = std::max(start, end);
return s;
}
Segment loop(float start, float end) const{
- assert(start >= 0);
- assert(end >= 0);
+ if (!(start >= 0)) start = 0;
+ if (!(end >= 0)) end = 0;
Segment s;
s.start = std::max(start, end);
s.end = std::min(start, end);
diff --unified -r -p -x .git -x .pc -x debian rlottie_0.1+dfsg-2+deb11u1/src/lottie/lottieparser.cpp rlottie_0.1+dfsg-2+deb11u2/src/lottie/lottieparser.cpp
--- rlottie_0.1+dfsg-2+deb11u1/src/lottie/lottieparser.cpp 2026-07-07 16:14:58.778890934 +0300
+++ rlottie_0.1+dfsg-2+deb11u2/src/lottie/lottieparser.cpp 2026-07-07 16:15:05.975040022 +0300
@@ -608,7 +608,12 @@ void LottieParserImpl::resolveLayerRefs(
if (layer->mLayerType == LayerType::Image) {
layer->extra()->mAsset = search->second;
} else if (layer->mLayerType == LayerType::Precomp) {
- layer->mChildren = search->second->mLayers;
+ layer->mChildren.clear();
+ layer->mChildren.reserve(search->second->mLayers.size());
+ for (LOTData *candidate : search->second->mLayers) {
+ if (candidate->includes(layer)) continue;
+ layer->mChildren.push_back(candidate);
+ }
layer->setStatic(layer->isStatic() &&
search->second->isStatic());
}
@@ -661,6 +666,10 @@ void LottieParserImpl::parseComposition(
// don't have a valid bodymovin header
return;
}
+ if (comp->mStartFrame > comp->mEndFrame) {
+ // reversed animation? missing data?
+ return;
+ }
if (!IsValid()) {
return;
}
@@ -1136,6 +1145,10 @@ LOTData* LottieParserImpl::parseObjectTy
{
RAPIDJSON_ASSERT(PeekType() == kStringType);
const char *type = GetString();
+ if (!type) {
+ vWarning << "No object type specified";
+ return nullptr;
+ }
if (0 == strcmp(type, "gr")) {
return parseGroupObject();
} else if (0 == strcmp(type, "rc")) {
@@ -1914,7 +1927,7 @@ void LottieParserImpl::getValue(LottieSh
if (mInPoint.size() != mOutPoint.size() ||
mInPoint.size() != mVertices.size()) {
vCritical << "The Shape data are corrupted";
- points.emplace_back();
+ points = std::vector<VPointF>();
} else {
auto size = mVertices.size();
points.reserve(3 * size + 4);
diff --unified -r -p -x .git -x .pc -x debian rlottie_0.1+dfsg-2+deb11u1/src/vector/freetype/v_ft_raster.cpp rlottie_0.1+dfsg-2+deb11u2/src/vector/freetype/v_ft_raster.cpp
--- rlottie_0.1+dfsg-2+deb11u1/src/vector/freetype/v_ft_raster.cpp 2026-07-07 16:14:58.830892011 +0300
+++ rlottie_0.1+dfsg-2+deb11u2/src/vector/freetype/v_ft_raster.cpp 2026-07-07 16:15:06.243045574 +0300
@@ -198,17 +198,17 @@ typedef struct SW_FT_Outline_Funcs_ {
#define ONE_PIXEL (1L << PIXEL_BITS)
#define PIXEL_MASK (-1L << PIXEL_BITS)
#define TRUNC(x) ((TCoord)((x) >> PIXEL_BITS))
-#define SUBPIXELS(x) ((TPos)(x) << PIXEL_BITS)
+#define SUBPIXELS(x) ((TPos)((unsigned long)(x) << PIXEL_BITS))
#define FLOOR(x) ((x) & -ONE_PIXEL)
#define CEILING(x) (((x) + ONE_PIXEL - 1) & -ONE_PIXEL)
#define ROUND(x) (((x) + ONE_PIXEL / 2) & -ONE_PIXEL)
#if PIXEL_BITS >= 6
-#define UPSCALE(x) ((x) << (PIXEL_BITS - 6))
+#define UPSCALE(x) ((TPos)((unsigned long)(x) << (PIXEL_BITS - 6)))
#define DOWNSCALE(x) ((x) >> (PIXEL_BITS - 6))
#else
-#define UPSCALE(x) ((x) >> (6 - PIXEL_BITS))
-#define DOWNSCALE(x) ((x) << (6 - PIXEL_BITS))
+#define UPSCALE(x) ((x) >> (6 - PIXEL_BITS))
+#define DOWNSCALE(x) ((TPos)((unsigned long)(x) << (6 - PIXEL_BITS)))
#endif
/* Compute `dividend / divisor' and return both its quotient and */
@@ -371,6 +371,7 @@ static void gray_init_cells(RAS_ARG_ voi
ras.buffer_size = byte_size;
ras.ycells = (PCell*)buffer;
+ ras.ycount = byte_size / sizeof(PCell);
ras.cells = NULL;
ras.max_cells = 0;
ras.num_cells = 0;
@@ -432,6 +433,7 @@ static PCell gray_find_cell(RAS_ARG)
TPos x = ras.ex;
if (x > ras.count_ex) x = ras.count_ex;
+ if (ras.ey < 0 || ras.ey >= ras.ycount) ft_longjmp(ras.jump_buffer, 1);
pcell = &ras.ycells[ras.ey];
for (;;) {
@@ -851,19 +853,17 @@ static void gray_render_cubic(RAS_ARG_ c
}
Split:
+ if (arc + 6 >= std::end(ras.bez_stack)) return;
+
gray_split_cubic(arc);
arc += 3;
-
- if (arc + 4 > std::end(ras.bez_stack)) return;
-
continue;
Draw:
gray_render_line(RAS_VAR_ arc[0].x, arc[0].y);
- if (arc == ras.bez_stack) return;
-
arc -= 3;
+ if (arc < std::begin(ras.bez_stack)) return;
}
}
@@ -935,10 +935,10 @@ static void gray_hline(RAS_ARG_ TCoord x
x += (TCoord)ras.min_ex;
/* SW_FT_Span.x is a 16-bit short, so limit our coordinates appropriately */
- if (x >= 32767) x = 32767;
+ if (x >= SHRT_MAX) x = SHRT_MAX;
- /* SW_FT_Span.y is an integer, so limit our coordinates appropriately */
- if (y >= SW_FT_INT_MAX) y = SW_FT_INT_MAX;
+ /* SW_FT_Span.y is a 16-bit short, so limit our coordinates appropriately */
+ if (y >= SHRT_MAX) y = SHRT_MAX;
if (coverage) {
SW_FT_Span* span;
@@ -1072,7 +1072,7 @@ static int SW_FT_Outline_Decompose(const
void* user)
{
#undef SCALED
-#define SCALED(x) (((x) << shift) - delta)
+#define SCALED(x) ((TPos)((unsigned long)(x) << shift) - delta)
SW_FT_Vector v_last;
SW_FT_Vector v_control;
diff --unified -r -p -x .git -x .pc -x debian rlottie_0.1+dfsg-2+deb11u1/src/vector/freetype/v_ft_stroker.cpp rlottie_0.1+dfsg-2+deb11u2/src/vector/freetype/v_ft_stroker.cpp
--- rlottie_0.1+dfsg-2+deb11u1/src/vector/freetype/v_ft_stroker.cpp 2026-07-07 16:14:43.662577759 +0300
+++ rlottie_0.1+dfsg-2+deb11u2/src/vector/freetype/v_ft_stroker.cpp 2026-07-07 16:15:06.059041762 +0300
@@ -18,6 +18,7 @@
#include "v_ft_stroker.h"
#include <assert.h>
+#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "v_ft_math.h"
@@ -631,6 +632,8 @@ Fail:
static void ft_stroke_border_export(SW_FT_StrokeBorder border,
SW_FT_Outline* outline)
{
+ if ((ulong)outline->n_points + border->num_points > SHRT_MAX) return;
+
/* copy point locations */
memcpy(outline->points + outline->n_points, border->points,
border->num_points * sizeof(SW_FT_Vector));
diff --unified -r -p -x .git -x .pc -x debian rlottie_0.1+dfsg-2+deb11u1/src/vector/vbezier.cpp rlottie_0.1+dfsg-2+deb11u2/src/vector/vbezier.cpp
--- rlottie_0.1+dfsg-2+deb11u1/src/vector/vbezier.cpp 2026-07-07 16:14:43.666577842 +0300
+++ rlottie_0.1+dfsg-2+deb11u2/src/vector/vbezier.cpp 2026-07-07 16:15:06.031041182 +0300
@@ -42,7 +42,7 @@ float VBezier::length() const
VBezier left, right; /* bez poly splits */
float len = 0.0; /* arc length */
float chord; /* chord length */
- float length;
+ float length = 0;
len = len + VLine::length(x1, y1, x2, y2);
len = len + VLine::length(x2, y2, x3, y3);
@@ -52,9 +52,10 @@ float VBezier::length() const
if ((len - chord) > 0.01) {
split(&left, &right); /* split in two */
- length = left.length() + /* try left side */
- right.length(); /* try right side */
-
+ if (*this != left)
+ length += left.length(); /* try left side */
+ if (*this != right)
+ length += right.length(); /* try right side */
return length;
}
@@ -85,12 +86,12 @@ float VBezier::tAtLength(float l) const
t *= 0.5;
float lastBigger = 1.0;
- while (1) {
+ for (int num = 0; num < 100500; num++) {
VBezier right = *this;
VBezier left;
right.parameterSplitLeft(t, &left);
float lLen = left.length();
- if (fabs(lLen - l) < error) break;
+ if (fabs(lLen - l) < error) return t;
if (lLen < l) {
t += (lastBigger - t) * 0.5f;
@@ -99,6 +100,7 @@ float VBezier::tAtLength(float l) const
t -= t * 0.5f;
}
}
+ vWarning << "no convergence";
return t;
}
diff --unified -r -p -x .git -x .pc -x debian rlottie_0.1+dfsg-2+deb11u1/src/vector/vbezier.h rlottie_0.1+dfsg-2+deb11u2/src/vector/vbezier.h
--- rlottie_0.1+dfsg-2+deb11u1/src/vector/vbezier.h 2026-07-07 16:14:43.666577842 +0300
+++ rlottie_0.1+dfsg-2+deb11u2/src/vector/vbezier.h 2026-07-07 16:15:06.031041182 +0300
@@ -19,11 +19,14 @@
#ifndef VBEZIER_H
#define VBEZIER_H
+#include <tuple>
#include <vpoint.h>
V_BEGIN_NAMESPACE
class VBezier {
+ friend bool operator == (const VBezier &l, const VBezier &r);
+
public:
VBezier() = default;
VPointF pointAt(float t) const;
@@ -129,6 +132,16 @@ inline void VBezier::split(VBezier *firs
firstHalf->y4 = secondHalf->y1 = (firstHalf->y3 + secondHalf->y2) * 0.5f;
}
+inline bool operator == (const VBezier &l, const VBezier &r)
+{
+ return std::tie(l.x1, l.y1, l.x2, l.y2, l.x3, l.y3, l.x4, l.y4)
+ == std::tie(r.x1, r.y1, r.x2, r.y2, r.x3, r.y3, r.x4, r.y4);
+}
+inline bool operator != (const VBezier &l, const VBezier &r)
+{
+ return !(l == r);
+}
+
V_END_NAMESPACE
#endif // VBEZIER_H
diff --unified -r -p -x .git -x .pc -x debian rlottie_0.1+dfsg-2+deb11u1/src/vector/vdrawhelper.cpp rlottie_0.1+dfsg-2+deb11u2/src/vector/vdrawhelper.cpp
--- rlottie_0.1+dfsg-2+deb11u1/src/vector/vdrawhelper.cpp 2026-07-07 16:14:58.642888116 +0300
+++ rlottie_0.1+dfsg-2+deb11u2/src/vector/vdrawhelper.cpp 2026-07-07 16:15:06.215044994 +0300
@@ -146,9 +146,6 @@ bool VGradientCache::generateGradientCol
float opacity,
uint32_t *colorTable, int size)
{
- if (stops.empty()) {
- return false;
- }
int dist, idist, pos = 0;
size_t i;
bool alpha = false;
@@ -159,6 +156,11 @@ bool VGradientCache::generateGradientCol
if (!vCompare(opacity, 1.0f)) alpha = true;
+ if (stopCount == 0) {
+ for (int j = 0; j < size; ++j) colorTable[j] = 0;
+ return alpha;
+ }
+
start = stops.data();
curr = start;
if (!curr->second.isOpaque()) alpha = true;
@@ -174,7 +176,7 @@ bool VGradientCache::generateGradientCol
fpos += incr;
}
- for (i = 0; i < stopCount - 1; ++i) {
+ for (i = 0; i + 1 < stopCount; ++i) {
curr = (start + i);
next = (start + i + 1);
delta = 1 / (next->first - curr->first);
diff --unified -r -p -x .git -x .pc -x debian rlottie_0.1+dfsg-2+deb11u1/src/vector/vpath.cpp rlottie_0.1+dfsg-2+deb11u2/src/vector/vpath.cpp
--- rlottie_0.1+dfsg-2+deb11u1/src/vector/vpath.cpp 2026-07-07 16:14:43.674578008 +0300
+++ rlottie_0.1+dfsg-2+deb11u2/src/vector/vpath.cpp 2026-07-07 16:15:06.163043917 +0300
@@ -514,6 +514,8 @@ void VPath::VPathData::addPolystar(float
float outerRoundness, float startAngle,
float cx, float cy, VPath::Direction dir)
{
+ constexpr float MAX_POLY_POINTS = 1024.0f;
+ if (points > MAX_POLY_POINTS) points = MAX_POLY_POINTS;
const static float POLYSTAR_MAGIC_NUMBER = 0.47829f / 0.28f;
float currentAngle = (startAngle - 90.0f) * K_PI / 180.0f;
float x;
@@ -619,6 +621,8 @@ void VPath::VPathData::addPolygon(float
VPath::Direction dir)
{
// TODO: Need to support floating point number for number of points
+ constexpr float MAX_POLY_POINTS = 1024.0f;
+ if (points > MAX_POLY_POINTS) points = MAX_POLY_POINTS;
const static float POLYGON_MAGIC_NUMBER = 0.25;
float currentAngle = (startAngle - 90.0f) * K_PI / 180.0f;
float x;
diff --unified -r -p -x .git -x .pc -x debian rlottie_0.1+dfsg-2+deb11u1/src/vector/vraster.cpp rlottie_0.1+dfsg-2+deb11u2/src/vector/vraster.cpp
--- rlottie_0.1+dfsg-2+deb11u1/src/vector/vraster.cpp 2026-07-07 16:14:43.674578008 +0300
+++ rlottie_0.1+dfsg-2+deb11u2/src/vector/vraster.cpp 2026-07-07 16:15:06.083042259 +0300
@@ -370,6 +370,7 @@ struct VRleTask {
{
if (mPath.points().size() > SHRT_MAX ||
mPath.points().size() + mPath.segments() > SHRT_MAX) {
+ mRle.notify();
return;
}
diff --unified -r -p -x .git -x .pc -x debian rlottie_0.1+dfsg-2+deb11u1/src/vector/vrle.cpp rlottie_0.1+dfsg-2+deb11u2/src/vector/vrle.cpp
--- rlottie_0.1+dfsg-2+deb11u1/src/vector/vrle.cpp 2026-07-07 16:14:58.750890354 +0300
+++ rlottie_0.1+dfsg-2+deb11u2/src/vector/vrle.cpp 2026-07-07 16:15:05.763035629 +0300
@@ -503,7 +503,7 @@ void blitXor(VRle::Span *spans, int coun
while (count--) {
int x = spans->x + offsetX;
int l = spans->len;
- if (x > bufferLen || l > bufferLen || x + l > bufferLen) {
+ if (x < 0 || x + l > bufferLen) {
return;
}
uchar *ptr = buffer + x;
@@ -523,7 +523,7 @@ void blitDestinationOut(VRle::Span *span
while (count--) {
int x = spans->x + offsetX;
int l = spans->len;
- if (x > bufferLen || l > bufferLen || x + l > bufferLen) {
+ if (x < 0 || x + l > bufferLen) {
return;
}
uchar *ptr = buffer + x;
@@ -540,7 +540,7 @@ void blitSrcOver(VRle::Span *spans, int
while (count--) {
int x = spans->x + offsetX;
int l = spans->len;
- if (x > bufferLen || l > bufferLen || x + l > bufferLen) {
+ if (x < 0 || x + l > bufferLen) {
return;
}
uchar *ptr = buffer + x;
@@ -557,7 +557,7 @@ void blit(VRle::Span *spans, int count,
while (count--) {
int x = spans->x + offsetX;
int l = spans->len;
- if (x > bufferLen || l > bufferLen || x + l > bufferLen || x + l < 0) {
+ if (x < 0 || x + l > bufferLen) {
return;
}
uchar *ptr = buffer + x;
signature.asc
Description: This is a digitally signed message part.
