[skottie] Avoid UB while parsing 1-based indices
Current impl can underflow int due to unchecked arithmetic.
Add a Parse<size_t> specialization and convert call sites which use
inline arithmetic. Underflowing unsigned types should be well defined
and caught in later tests.
Bug: oss-fuzz:9798
Change-Id: Iaebe8aad4009e2511fe1d8733d336f5f119bb384
Reviewed-on: https://skia-review.googlesource.com/146648
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/modules/skottie/src/Skottie.cpp b/modules/skottie/src/Skottie.cpp
index 0331cbb..b1bd2c9 100644
--- a/modules/skottie/src/Skottie.cpp
+++ b/modules/skottie/src/Skottie.cpp
@@ -218,8 +218,8 @@
PolyStarAdapter::Type::kPoly, // "sy": 2
};
- const auto type = ParseDefault<int>(jstar["sy"], 0) - 1;
- if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gTypes))) {
+ const auto type = ParseDefault<size_t>(jstar["sy"], 0) - 1;
+ if (type >= SK_ARRAY_COUNT(gTypes)) {
LogFail(jstar, "Unknown polystar type");
return nullptr;
}
@@ -344,16 +344,16 @@
SkPaint::kRound_Join,
SkPaint::kBevel_Join,
};
- stroke_node->setStrokeJoin(gJoins[SkTPin<int>(ParseDefault<int>(jstroke["lj"], 1) - 1,
- 0, SK_ARRAY_COUNT(gJoins) - 1)]);
+ stroke_node->setStrokeJoin(gJoins[SkTMin<size_t>(ParseDefault<size_t>(jstroke["lj"], 1) - 1,
+ SK_ARRAY_COUNT(gJoins) - 1)]);
static constexpr SkPaint::Cap gCaps[] = {
SkPaint::kButt_Cap,
SkPaint::kRound_Cap,
SkPaint::kSquare_Cap,
};
- stroke_node->setStrokeCap(gCaps[SkTPin<int>(ParseDefault(jstroke["lc"], 1) - 1,
- 0, SK_ARRAY_COUNT(gCaps) - 1)]);
+ stroke_node->setStrokeCap(gCaps[SkTMin<size_t>(ParseDefault<size_t>(jstroke["lc"], 1) - 1,
+ SK_ARRAY_COUNT(gCaps) - 1)]);
return stroke_node;
}
@@ -398,8 +398,8 @@
sksg::Merge::Mode::kXOR , // "mm": 5
};
- const auto mode = gModes[SkTPin<int>(ParseDefault<int>(jmerge["mm"], 1) - 1,
- 0, SK_ARRAY_COUNT(gModes) - 1)];
+ const auto mode = gModes[SkTMin<size_t>(ParseDefault<size_t>(jmerge["mm"], 1) - 1,
+ SK_ARRAY_COUNT(gModes) - 1)];
std::vector<sk_sp<sksg::GeometryNode>> merged;
merged.push_back(Merge(std::move(geos), mode));
@@ -416,8 +416,8 @@
kSeparate, // "m": 2
} gModes[] = { Mode::kMerged, Mode::kSeparate };
- const auto mode = gModes[SkTPin<int>(ParseDefault<int>(jtrim["m"], 1) - 1,
- 0, SK_ARRAY_COUNT(gModes) - 1)];
+ const auto mode = gModes[SkTMin<size_t>(ParseDefault<size_t>(jtrim["m"], 1) - 1,
+ SK_ARRAY_COUNT(gModes) - 1)];
std::vector<sk_sp<sksg::GeometryNode>> inputs;
if (mode == Mode::kMerged) {
@@ -1258,9 +1258,9 @@
sksg::MaskEffect::Mode::kNormal, // tt: 1
sksg::MaskEffect::Mode::kInvert, // tt: 2
};
- const auto matteType = ParseDefault<int>((*jlayer)["tt"], 1) - 1;
+ const auto matteType = ParseDefault<size_t>((*jlayer)["tt"], 1) - 1;
- if (matteType >= 0 && matteType < SkTo<int>(SK_ARRAY_COUNT(gMaskModes))) {
+ if (matteType < SK_ARRAY_COUNT(gMaskModes)) {
return sksg::MaskEffect::Make(std::move(controller_node),
std::move(layerCtx->fCurrentMatte),
gMaskModes[matteType]);
diff --git a/modules/skottie/src/SkottieJson.cpp b/modules/skottie/src/SkottieJson.cpp
index 23221af..568f4df 100644
--- a/modules/skottie/src/SkottieJson.cpp
+++ b/modules/skottie/src/SkottieJson.cpp
@@ -53,18 +53,28 @@
return false;
}
-template <>
-bool Parse<int>(const Value& v, int* i) {
+template <typename T>
+bool ParseIntegral(const Value& v, T* result) {
if (const skjson::NumberValue* num = v) {
const auto dbl = **num;
- *i = dbl;
- return *i == dbl;
+ *result = static_cast<T>(dbl);
+ return static_cast<double>(*result) == dbl;
}
return false;
}
template <>
+bool Parse<int>(const Value& v, int* i) {
+ return ParseIntegral(v, i);
+}
+
+template <>
+bool Parse<size_t>(const Value& v, size_t* sz) {
+ return ParseIntegral(v, sz);
+}
+
+template <>
bool Parse<SkString>(const Value& v, SkString* s) {
if (const skjson::StringValue* sv = v) {
s->set(sv->begin(), sv->size());