From 507ea027e47d3e1dc7ddbd9994621215eae7ebb9 Mon Sep 17 00:00:00 2001
From: Michal Maciola <m.maciola@samsung.com>
Date: Thu, 15 May 2025 20:51:09 +0200
Subject: [PATCH] Fix security vulnerabilities in Lottie file handling Add
 validation checks to address potential vulnerabilities in case of a malicious
 Lottie file

Adds:
- Type check for CompLayer() before casting to model::Layer
- Bounds checking for Gradient::populate()
- Frames vector empty check
- Rejection of outliers
---
 src/lottie/lottieitem.cpp           |  1 +
 src/lottie/lottiemodel.cpp          | 15 ++++++++++++---
 src/lottie/lottiemodel.h            | 21 +++++++++++++--------
 src/vector/freetype/v_ft_raster.cpp |  5 +++++
 4 files changed, 31 insertions(+), 11 deletions(-)

diff --git a/src/lottie/lottieitem.cpp b/src/lottie/lottieitem.cpp
index c5540f3f..c70f3273 100644
--- a/src/lottie/lottieitem.cpp
+++ b/src/lottie/lottieitem.cpp
@@ -479,6 +479,7 @@ renderer::CompLayer::CompLayer(model::Layer *layerModel, VArenaAlloc *allocator)
     // as lottie model keeps the data in front-toback-order.
     for (auto it = mLayerData->mChildren.crbegin();
          it != mLayerData->mChildren.rend(); ++it) {
+        if ((*it)->type() != model::Object::Type::Layer) continue;
         auto model = static_cast<model::Layer *>(*it);
         auto item = createLayerItem(model, allocator);
         if (item) mLayers.push_back(item);
diff --git a/src/lottie/lottiemodel.cpp b/src/lottie/lottiemodel.cpp
index 1bca99d2..0f816f6f 100644
--- a/src/lottie/lottiemodel.cpp
+++ b/src/lottie/lottiemodel.cpp
@@ -250,11 +250,16 @@ void model::Gradient::populate(VGradientStops &stops, int frameNo)
     auto                  size = gradData.mGradient.size();
     float *               ptr = gradData.mGradient.data();
     int                   colorPoints = mColorPoints;
-    if (colorPoints == -1) {  // for legacy bodymovin (ref: lottie-android)
+    size_t                colorPointsSize = colorPoints * 4;
+    if (!ptr) return;
+    if (colorPoints < 0 || colorPointsSize > size) {  // for legacy bodymovin (ref: lottie-android)
         colorPoints = int(size / 4);
     }
-    auto   opacityArraySize = size - colorPoints * 4;
-    float *opacityPtr = ptr + (colorPoints * 4);
+    auto   opacityArraySize = size - colorPointsSize;
+    if (opacityArraySize % 2 != 0) {
+        opacityArraySize = 0;
+    }
+    float *opacityPtr = ptr + colorPointsSize;
     stops.clear();
     for (int i = 0; i < colorPoints; i++) {
         float        colorStop = ptr[0];
@@ -267,6 +272,10 @@ void model::Gradient::populate(VGradientStops &stops, int frameNo)
         }
         ptr += 4;
     }
+
+    if (stops.empty()) {
+        stops.push_back(std::make_pair(0.0f, VColor(255, 255, 255, 255)));
+    }
 }
 
 float model::Gradient::getOpacityAtPosition(float *opacities, size_t opacityArraySize, float position)
diff --git a/src/lottie/lottiemodel.h b/src/lottie/lottiemodel.h
index 1e66ff15..c51cc4a2 100644
--- a/src/lottie/lottiemodel.h
+++ b/src/lottie/lottiemodel.h
@@ -239,20 +239,23 @@ class KeyFrames {
 
     T value(int frameNo) const
     {
-        if (frames_.front().start_ >= frameNo)
-            return frames_.front().value_.start_;
-        if (frames_.back().end_ <= frameNo) return frames_.back().value_.end_;
-
-        for (const auto &keyFrame : frames_) {
-            if (frameNo >= keyFrame.start_ && frameNo < keyFrame.end_)
-                return keyFrame.value(frameNo);
+        if (!frames_.empty()) {
+            if (frames_.front().start_ >= frameNo)
+                return frames_.front().value_.start_;
+            if (frames_.back().end_ <= frameNo) return frames_.back().value_.end_;
+
+            for (const auto &keyFrame : frames_) {
+                if (frameNo >= keyFrame.start_ && frameNo < keyFrame.end_)
+                    return keyFrame.value(frameNo);
+            }
         }
         return {};
     }
 
     float angle(int frameNo) const
     {
-        if ((frames_.front().start_ >= frameNo) ||
+        if (frames_.empty() ||
+            (frames_.front().start_ >= frameNo) ||
             (frames_.back().end_ <= frameNo))
             return 0;
 
@@ -265,6 +268,8 @@ class KeyFrames {
 
     bool changed(int prevFrame, int curFrame) const
     {
+        if (frames_.empty()) return false;
+
         auto first = frames_.front().start_;
         auto last = frames_.back().end_;
 
diff --git a/src/vector/freetype/v_ft_raster.cpp b/src/vector/freetype/v_ft_raster.cpp
index e48ad1b1..24b0d427 100644
--- a/src/vector/freetype/v_ft_raster.cpp
+++ b/src/vector/freetype/v_ft_raster.cpp
@@ -537,6 +537,10 @@ static void gray_render_line(RAS_ARG_ TPos to_x, TPos to_y)
     dx = to_x - ras.x;
     dy = to_y - ras.y;
 
+    if (SW_FT_ABS(dx) > 10000000 || SW_FT_ABS(dy) > 10000000) {
+         goto End;
+    }
+
     fx1 = ras.x - SUBPIXELS(ex1);
     fy1 = ras.y - SUBPIXELS(ey1);
 
@@ -707,6 +711,7 @@ static void gray_render_conic(RAS_ARG_ const SW_FT_Vector* control,
             gray_split_conic(arc);
             arc += 2;
             top++;
+            if (top >= 32) return; // levels size is 32
             levels[top] = levels[top - 1] = level - 1;
             continue;
         }
