libjxl/libjxl-CVE-2024-11498.patch

104 lines
4.0 KiB
Diff

From 3e2d0f3d0615bf67eb2fe213c9daeb929e3bd2fa Mon Sep 17 00:00:00 2001
From: Luca Versari <veluca@google.com>
Date: Thu, 21 Nov 2024 16:33:08 +0100
Subject: [PATCH] Check height limit in modular trees. (#3943)
Also rewrite the implementation to use iterative checking instead of
recursive checking of tree property values, to ensure stack usage is
low.
Before, it was possible for appropriately-crafted files to use a
significant amount of stack (in the order of hundreds of MB).
(cherry picked from commit bf4781a2eed2eef664790170977d1d3d8347efb9)
---
lib/jxl/modular/encoding/dec_ma.cc | 65 ++++++++++++++++++++----------
1 file changed, 44 insertions(+), 21 deletions(-)
diff --git a/lib/jxl/modular/encoding/dec_ma.cc b/lib/jxl/modular/encoding/dec_ma.cc
index 66562f7d..2d88e9b1 100644
--- a/lib/jxl/modular/encoding/dec_ma.cc
+++ b/lib/jxl/modular/encoding/dec_ma.cc
@@ -14,23 +14,49 @@ namespace jxl {
namespace {
-Status ValidateTree(
- const Tree &tree,
- const std::vector<std::pair<pixel_type, pixel_type>> &prop_bounds,
- size_t root) {
- if (tree[root].property == -1) return true;
- size_t p = tree[root].property;
- int val = tree[root].splitval;
- if (prop_bounds[p].first > val) return JXL_FAILURE("Invalid tree");
- // Splitting at max value makes no sense: left range will be exactly same
- // as parent, right range will be invalid (min > max).
- if (prop_bounds[p].second <= val) return JXL_FAILURE("Invalid tree");
- auto new_bounds = prop_bounds;
- new_bounds[p].first = val + 1;
- JXL_RETURN_IF_ERROR(ValidateTree(tree, new_bounds, tree[root].lchild));
- new_bounds[p] = prop_bounds[p];
- new_bounds[p].second = val;
- return ValidateTree(tree, new_bounds, tree[root].rchild);
+Status ValidateTree(const Tree &tree) {
+ int num_properties = 0;
+ for (auto node : tree) {
+ if (node.property >= num_properties) {
+ num_properties = node.property + 1;
+ }
+ }
+ std::vector<int> height(tree.size());
+ std::vector<std::pair<pixel_type, pixel_type>> property_ranges(
+ num_properties * tree.size());
+ for (int i = 0; i < num_properties; i++) {
+ property_ranges[i].first = std::numeric_limits<pixel_type>::min();
+ property_ranges[i].second = std::numeric_limits<pixel_type>::max();
+ }
+ const int kHeightLimit = 2048;
+ for (size_t i = 0; i < tree.size(); i++) {
+ if (height[i] > kHeightLimit) {
+ return JXL_FAILURE("Tree too tall: %d", height[i]);
+ }
+ if (tree[i].property == -1) continue;
+ height[tree[i].lchild] = height[i] + 1;
+ height[tree[i].rchild] = height[i] + 1;
+ for (size_t p = 0; p < static_cast<size_t>(num_properties); p++) {
+ if (p == static_cast<size_t>(tree[i].property)) {
+ pixel_type l = property_ranges[i * num_properties + p].first;
+ pixel_type u = property_ranges[i * num_properties + p].second;
+ pixel_type val = tree[i].splitval;
+ if (l > val || u <= val) {
+ return JXL_FAILURE("Invalid tree");
+ }
+ property_ranges[tree[i].lchild * num_properties + p] =
+ std::make_pair(val + 1, u);
+ property_ranges[tree[i].rchild * num_properties + p] =
+ std::make_pair(l, val);
+ } else {
+ property_ranges[tree[i].lchild * num_properties + p] =
+ property_ranges[i * num_properties + p];
+ property_ranges[tree[i].rchild * num_properties + p] =
+ property_ranges[i * num_properties + p];
+ }
+ }
+ }
+ return true;
}
Status DecodeTree(BitReader *br, ANSSymbolReader *reader,
@@ -79,10 +105,7 @@ Status DecodeTree(BitReader *br, ANSSymbolReader *reader,
tree->size() + to_decode + 2, Predictor::Zero, 0, 1);
to_decode += 2;
}
- std::vector<std::pair<pixel_type, pixel_type>> prop_bounds;
- prop_bounds.resize(256, {std::numeric_limits<pixel_type>::min(),
- std::numeric_limits<pixel_type>::max()});
- return ValidateTree(*tree, prop_bounds, 0);
+ return ValidateTree(*tree);
}
} // namespace
--
2.47.1