Chrome · Chromium
CVE-2026-79223
Integer Overflow in Chromium
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
DEF_TESTmodules/skottie/src/SkottieTest.cpp |
modified | |
TestMarkerObservermodules/skottie/src/SkottieTest.cpp |
modified | |
TestResourceProvidermodules/skottie/src/SkottieTest.cpp |
modified |
Files Changed
modules/skottie/BUILD.gnmodules/skottie/src/SkottieTest.cpp
Patch
From 36e509ddad66569e1a604816c1e47845950eee77 Mon Sep 17 00:00:00 2001 From: Florin Malita <[email protected]> Date: Mon, 13 Jul 2026 12:15:44 -0400 Subject: [PATCH] [skottie] Fix gradient count overflow in 32 bit builds Use SkSafeMath to detect size_t overflows. Also relocate SkottieTest.cpp from src/ to tests/. Bug: b/530816571 Change-Id: I861fe30b5223e2fb074c37217afa7d3bd48419ca Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1292156 Commit-Queue: Florin Malita <[email protected]> Reviewed-by: Brandon Wilson <[email protected]> --- diff --git a/modules/skottie/BUILD.gn b/modules/skottie/BUILD.gn index 91aeb77..fc95703 100644 --- a/modules/skottie/BUILD.gn +++ b/modules/skottie/BUILD.gn @@ -60,13 +60,13 @@ configs = [ "../..:skia_private" ] sources = [ - "src/SkottieTest.cpp", "tests/AudioLayer.cpp", "tests/Expression.cpp", "tests/Image.cpp", "tests/Keyframe.cpp", "tests/PropertyObserver.cpp", "tests/Shaper.cpp", + "tests/SkottieTest.cpp", "tests/Text.cpp", ] diff --git a/modules/skottie/src/SkottieTest.cpp b/modules/skottie/src/SkottieTest.cpp deleted file mode 100644 index 76bd5c3..0000000 --- a/modules/skottie/src/SkottieTest.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright 2018 Google LLC - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "include/core/SkStream.h" -#include "include/core/SkSurface.h" -#include "modules/skottie/include/Skottie.h" -#include "tests/Test.h" - -#include <cmath> -#include <string> -#include <tuple> -#include <vector> - -using namespace skottie; - -DEF_TEST(Skottie_OssFuzz8956, reporter) { - static constexpr char json[] = - "{\"v\":\" \",\"fr\":3,\"w\":4,\"h\":3,\"layers\":[{\"ty\": 1, \"sw\": 10, \"sh\": 10," - " \"sc\":\"#ffffff\", \"ks\":{\"o\":{\"a\": true, \"k\":" - " [{\"t\": 0, \"s\": 0, \"e\": 1, \"i\": {\"x\":[]}}]}}}]}"; - - SkMemoryStream stream(json, strlen(json)); - - // Passes if parsing doesn't crash. - auto animation = Animation::Make(&stream); -} - -DEF_TEST(Skottie_Annotations, reporter) { - static constexpr char json[] = R"({ - "v": "5.2.1", - "w": 100, - "h": 100, - "fr": 10, - "ip": 0, - "op": 100, - "layers": [ - { - "ty": 1, - "ind": 0, - "ip": 0, - "op": 1, - "ks": { - "o": { "a": 0, "k": 50 } - }, - "sw": 100, - "sh": 100, - "sc": "#ffffff" - } - ], - "markers": [ - { - "cm": "marker_1", - "dr": 25, - "tm": 25 - }, - { - "cm": "marker_2", - "dr": 0, - "tm": 75 - } - ] - })"; - - class TestMarkerObserver final : public MarkerObserver { - public: - void onMarker(const char name[], float t0, float t1) override { - fMarkers.push_back(std::make_tuple(name, t0, t1)); - } - - std::vector<std::tuple<std::string, float, float>> fMarkers; - }; - - SkMemoryStream stream(json, strlen(json)); - auto observer = sk_make_sp<TestMarkerObserver>(); - - auto animation = skottie::Animation::Builder() - .setMarkerObserver(observer) - .make(&stream); - - REPORTER_ASSERT(reporter, animation); - REPORTER_ASSERT(reporter, animation->duration() == 10); - REPORTER_ASSERT(reporter, animation->inPoint() == 0.0); - REPORTER_ASSERT(reporter, animation->outPoint() == 100.0); - - REPORTER_ASSERT(reporter, observer->fMarkers.size() == 2ul); - REPORTER_ASSERT(reporter, std::get<0>(observer->fMarkers[0]) == "marker_1"); - REPORTER_ASSERT(reporter, std::get<1>(observer->fMarkers[0]) == 0.25f); - REPORTER_ASSERT(reporter, std::get<2>(observer->fMarkers[0]) == 0.50f); - REPORTER_ASSERT(reporter, std::get<0>(observer->fMarkers[1]) == "marker_2"); - REPORTER_ASSERT(reporter, std::get<1>(observer->fMarkers[1]) == 0.75f); - REPORTER_ASSERT(reporter, std::get<2>(observer->fMarkers[1]) == 0.75f); -} - -DEF_TEST(Skottie_Image_Loading, reporter) { - class TestResourceProvider final : public skresources::ResourceProvider { - public: - TestResourceProvider(sk_sp<skresources::ImageAsset> single_asset, - sk_sp<skresources::ImageAsset> multi_asset) - : fSingleFrameAsset(std::move(single_asset)) - , fMultiFrameAsset (std::move( multi_asset)) {} - - private: - sk_sp<ImageAsset> loadImageAsset(const char path[], - const char name[], - const char id[]) const override { - return strcmp(id, "single_frame") - ? fMultiFrameAsset - : fSingleFrameAsset; - } - - const sk_sp<skresources::ImageAsset> fSingleFrameAsset, - fMultiFrameAsset; - }; - - auto make_animation = [&reporter] (sk_sp<skresources::ImageAsset> single_asset, - sk_sp<skresources::ImageAsset> multi_asset, - bool deferred_image_loading) { - static constexpr char json[] = R"({ - "v": "5.2.1", - "w": 100, - "h": 100, - "fr": 10, - "ip": 0, - "op": 100, - "assets": [ - { - "id": "single_frame", - "p" : "single_frame.png", - "u" : "images/", - "w" : 500, - "h" : 500 - }, - { - "id": "multi_frame", - "p" : "multi_frame.png", - "u" : "images/", - "w" : 500, - "h" : 500 - } - ], - "layers": [ - { - "ty": 2, - "refId": "single_frame", - "ind": 0, - "ip": 0, - "op": 100, - "ks": {} - }, - { - "ty": 2, - "refId": "multi_frame", - "ind": 1, - "ip": 0, - "op": 100,
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/modules/skottie/tests/SkottieTest.cpp b/modules/skottie/tests/SkottieTest.cpp
new file mode 100644
index 0000000..472c2bd
--- /dev/null
+++ b/modules/skottie/tests/SkottieTest.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkStream.h"
+#include "include/core/SkSurface.h"
+#include "modules/skottie/include/Skottie.h"
+#include "tests/Test.h"
+
+#include <string>
+#include <tuple>
+#include <vector>
+
+using namespace skottie;
+
+DEF_TEST(Skottie_OssFuzz8956, reporter) {
+ static constexpr char json[] =
+ "{\"v\":\" \",\"fr\":3,\"w\":4,\"h\":3,\"layers\":[{\"ty\": 1, \"sw\": 10, \"sh\": 10,"
+ " \"sc\":\"#ffffff\", \"ks\":{\"o\":{\"a\": true, \"k\":"
+ " [{\"t\": 0, \"s\": 0, \"e\": 1, \"i\": {\"x\":[]}}]}}}]}";
+
+ SkMemoryStream stream(json, strlen(json));
+
+ // Passes if parsing doesn't crash.
+ auto animation = Animation::Make(&stream);
+}
+
+DEF_TEST(Skottie_Annotations, reporter) {
+ static constexpr char json[] = R"({
+ "v": "5.2.1",
+ "w": 100,
+ "h": 100,
+ "fr": 10,
+ "ip": 0,
+ "op": 100,
+ "layers": [
+ {
+ "ty": 1,
+ "ind": 0,
+ "ip": 0,
+ "op": 1,
+ "ks": {
+ "o": { "a": 0, "k": 50 }
+ },
+ "sw": 100,
+ "sh": 100,
+ "sc": "#ffffff"
+ }
+ ],
+ "markers": [
+ {
+ "cm": "marker_1",
+ "dr": 25,
+ "tm": 25
+ },
+ {
+ "cm": "marker_2",
+ "dr": 0,
+ "tm": 75
+ }
+ ]
+ })";
+
+ class TestMarkerObserver final : public MarkerObserver {
+ public:
+ void onMarker(const char name[], float t0, float t1) override {
+ fMarkers.push_back(std::make_tuple(name, t0, t1));
+ }
+
+ std::vector<std::tuple<std::string, float, float>> fMarkers;
+ };
+
+ SkMemoryStream stream(json, strlen(json));
+ auto observer = sk_make_sp<TestMarkerObserver>();
+
+ auto animation = skottie::Animation::Builder()
+ .setMarkerObserver(observer)
+ .make(&stream);
+
+ REPORTER_ASSERT(reporter, animation);
+ REPORTER_ASSERT(reporter, animation->duration() == 10);
+ REPORTER_ASSERT(reporter, animation->inPoint() == 0.0);
+ REPORTER_ASSERT(reporter, animation->outPoint() == 100.0);
+
+ REPORTER_ASSERT(reporter, observer->fMarkers.size() == 2ul);
+ REPORTER_ASSERT(reporter, std::get<0>(observer->fMarkers[0]) == "marker_1");
+ REPORTER_ASSERT(reporter, std::get<1>(observer->fMarkers[0]) == 0.25f);
+ REPORTER_ASSERT(reporter, std::get<2>(observer->fMarkers[0]) == 0.50f);
+ REPORTER_ASSERT(reporter, std::get<0>(observer->fMarkers[1]) == "marker_2");
+ REPORTER_ASSERT(reporter, std::get<1>(observer->fMarkers[1]) == 0.75f);
+ REPORTER_ASSERT(reporter, std::get<2>(observer->fMarkers[1]) == 0.75f);
+}
+
+DEF_TEST(Skottie_Image_Loading, reporter) {
+ class TestResourceProvider final : public skresources::ResourceProvider {
+ public:
+ TestResourceProvider(sk_sp<skresources::ImageAsset> single_asset,
+ sk_sp<skresources::ImageAsset> multi_asset)
+ : fSingleFrameAsset(std::move(single_asset))
+ , fMultiFrameAsset (std::move( multi_asset)) {}
+
+ private:
+ sk_sp<ImageAsset> loadImageAsset(const char path[],
+ const char name[],
+ const char id[]) const override {
+ return strcmp(id, "single_frame")
+ ? fMultiFrameAsset
+ : fSingleFrameAsset;
+ }
+
+ const sk_sp<skresources::ImageAsset> fSingleFrameAsset,
+ fMultiFrameAsset;
+ };
+
+ auto make_animation = [&reporter] (sk_sp<skresources::ImageAsset> single_asset,
+ sk_sp<skresources::ImageAsset> multi_asset,
+ bool deferred_image_loading) {
+ static constexpr char json[] = R"({
+ "v": "5.2.1",
+ "w": 100,
+ "h": 100,
+ "fr": 10,
+ "ip": 0,
+ "op": 100,
+ "assets": [
+ {
+ "id": "single_frame",
+ "p" : "single_frame.png",
+ "u" : "images/",
+ "w" : 500,
+ "h" : 500
+ },
+ {
+ "id": "multi_frame",
+ "p" : "multi_frame.png",
+ "u" : "images/",
+ "w" : 500,
+ "h" : 500
+ }
+ ],
+ "layers": [
+ {
+ "ty": 2,
+ "refId": "single_frame",
+ "ind": 0,
+ "ip": 0,
+ "op": 100,
+ "ks": {}
+ },
+ {
+ "ty": 2,
+ "refId": "multi_frame",
+ "ind": 1,
+ "ip": 0,
+ "op": 100,
+ "ks": {}
+ }
+ ]
+ })";
+
+ SkMemoryStream stream(json, strlen(json));
+
+ const auto flags = deferred_image_loading
+ ? static_cast<uint32_t>(skottie::Animation::Builder::kDeferImageLoading)
+ : 0;
+ auto animation =
+ skottie::Animation::Builder(flags)
+ .setResourceProvider(sk_make_sp<TestResourceProvider>(std::move(single_asset),
+ std::move( multi_asset)))
+ .make(&stream);
+
+ REPORTER_ASSERT(reporter, animation);
+
+ return animation;
+ };
+
+ class TestAsset final : public skresources::ImageAsset {
+ public:
+ explicit TestAsset(bool multi_frame) : fMultiFrame(multi_frame) {}
+
+ const std::vector<float>& requestedFrames() const { return fRequestedFrames; }
+
+ private:
+ bool isMultiFrame() override { return fMultiFrame; }
+
+ sk_sp<SkImage> getFrame(float t) override {
+ fRequestedFrames.push_back(t);
+
+ return SkSurfaces::Raster(SkImageInfo::MakeN32Premul(10, 10))->makeImageSnapshot();
+ }
+
+ const bool fMultiFrame;
+
+ std::vector<float> fRequestedFrames;
+ };
+
+ {
+ auto single_asset = sk_make_sp<TestAsset>(false),
+ multi_asset = sk_make_sp<TestAsset>(true);
+
+ // Default image loading: single-frame images are loaded upfront, multi-frame images are
+ // loaded on-demand.
+ auto animation = make_animation(single_asset, multi_asset, false);
+
+ REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 1);
+ REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 0);
+ REPORTER_ASSERT(reporter, SkScalarNearlyZero(single_asset->requestedFrames()[0]));
+
+ animation->seekFrameTime(1);
+ REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 1);
+ REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 1);
+ REPORTER_ASSERT(reporter, SkScalarNearlyEqual(multi_asset->requestedFrames()[0], 1));
+
+ animation->seekFrameTime(2);
+ REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 1);
+ REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 2);
+ REPORTER_ASSERT(reporter, SkScalarNearlyEqual(multi_asset->requestedFrames()[1], 2));
+ }
+
+ {
+ auto single_asset = sk_make_sp<TestAsset>(false),
+ multi_asset = sk_make_sp<TestAsset>(true);
+
+ // Deferred image loading: both single-frame and multi-frame images are loaded on-demand.
+ auto animation = make_animation(single_asset, multi_asset, true);
+
+ REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 0);
+ REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 0);
+
+ animation->seekFrameTime(1);
+ REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 1);
+ REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 1);
+ REPORTER_ASSERT(reporter, SkScalarNearlyEqual(single_asset->requestedFrames()[0], 1));
+ REPORTER_ASSERT(reporter, SkScalarNearlyEqual (multi_asset->requestedFrames()[0], 1));
+
+ animation->seekFrameTime(2);
+ REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 1);
+ REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 2);
+ REPORTER_ASSERT(reporter, SkScalarNearlyEqual(multi_asset->requestedFrames()[1], 2));
+ }
+}
+
+DEF_TEST(Skottie_Layer_NoType, r) {
+ static constexpr char json[] =
+ R"({
+ "v": "5.2.1",
+ "w": 100,
+ "h": 100,
+ "fr": 10,
+ "ip": 0,
+ "op": 100,
+ "layers": [
+ {
+ "ind": 0,
+ "ip": 0,
+ "op": 100,
+ "ks": {}
+ }
+ ]
+ })";
+
+ SkMemoryStream stream(json, strlen(json));
+ auto anim = Animation::Make(&stream);
+
+ // passes if we don't crash
+ REPORTER_ASSERT(r, anim);
+}
+
+DEF_TEST(Skottie_Gradient_InvalidCount, r) {
+ static constexpr char json[] = R"({
+ "v": "5.12.0",
+ "fr": 60,
+ "ip": 0,
+ "op": 1,
+ "w": 64,
+ "h": 64,
+ "layers": [
+ {
+ "ind": 1,
+ "ty": 4,
+ "sr": 1,
+ "ks": {},
+ "shapes": [
+ {
+ "ty": "rc",
+ "d": 1,
+ "p": { "a": 0, "k": [0, 0] },
+ "s": { "a": 0, "k": [32, 32] },
+ "r": { "a": 0, "k": 0 }
+ },
+ {
... (truncated)
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page