Coverage Report

Created: 2026-01-20 07:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjxl/lib/jxl/dec_context_map.cc
Line
Count
Source
1
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
2
//
3
// Use of this source code is governed by a BSD-style
4
// license that can be found in the LICENSE file.
5
6
#include "lib/jxl/dec_context_map.h"
7
8
#include <jxl/memory_manager.h>
9
10
#include <algorithm>
11
#include <cstddef>
12
#include <cstdint>
13
#include <vector>
14
15
#include "lib/jxl/base/status.h"
16
#include "lib/jxl/dec_ans.h"
17
#include "lib/jxl/dec_bit_reader.h"
18
#include "lib/jxl/inverse_mtf-inl.h"
19
20
namespace jxl {
21
22
namespace {
23
24
// Context map uses uint8_t.
25
constexpr size_t kMaxClusters = 256;
26
27
Status VerifyContextMap(const std::vector<uint8_t>& context_map,
28
254k
                        const size_t num_htrees) {
29
254k
  std::vector<bool> have_htree(num_htrees);
30
254k
  size_t num_found = 0;
31
48.4M
  for (const uint8_t htree : context_map) {
32
48.4M
    if (htree >= num_htrees) {
33
0
      return JXL_FAILURE("Invalid histogram index in context map.");
34
0
    }
35
48.4M
    if (!have_htree[htree]) {
36
560k
      have_htree[htree] = true;
37
560k
      ++num_found;
38
560k
    }
39
48.4M
  }
40
254k
  if (num_found != num_htrees) {
41
10.6k
    return JXL_FAILURE("Incomplete context map.");
42
10.6k
  }
43
244k
  return true;
44
254k
}
45
46
}  // namespace
47
48
Status DecodeContextMap(JxlMemoryManager* memory_manager,
49
                        std::vector<uint8_t>* context_map, size_t* num_htrees,
50
272k
                        BitReader* input) {
51
272k
  bool is_simple = static_cast<bool>(input->ReadFixedBits<1>());
52
272k
  if (is_simple) {
53
104k
    int bits_per_entry = input->ReadFixedBits<2>();
54
104k
    if (bits_per_entry != 0) {
55
8.45M
      for (uint8_t& entry : *context_map) {
56
8.45M
        entry = input->ReadBits(bits_per_entry);
57
8.45M
      }
58
54.3k
    } else {
59
49.9k
      std::fill(context_map->begin(), context_map->end(), 0);
60
49.9k
    }
61
168k
  } else {
62
168k
    bool use_mtf = static_cast<bool>(input->ReadFixedBits<1>());
63
168k
    ANSCode code;
64
168k
    std::vector<uint8_t> sink_ctx_map;
65
    // Usage of LZ77 is disallowed if decoding only two symbols. This doesn't
66
    // make sense in non-malicious bitstreams, and could cause a stack overflow
67
    // in malicious bitstreams by making every context map require its own
68
    // context map.
69
168k
    JXL_RETURN_IF_ERROR(
70
168k
        DecodeHistograms(memory_manager, input, 1, &code, &sink_ctx_map,
71
168k
                         /*disallow_lz77=*/context_map->size() <= 2));
72
306k
    JXL_ASSIGN_OR_RETURN(ANSSymbolReader reader,
73
306k
                         ANSSymbolReader::Create(&code, input));
74
306k
    size_t i = 0;
75
306k
    uint32_t maxsym = 0;
76
28.7M
    while (i < context_map->size()) {
77
28.5M
      uint32_t sym = reader.ReadHybridUintInlined</*uses_lz77=*/true>(
78
28.5M
          0, input, sink_ctx_map);
79
28.5M
      maxsym = sym > maxsym ? sym : maxsym;
80
28.5M
      (*context_map)[i] = sym;
81
28.5M
      i++;
82
28.5M
    }
83
306k
    if (maxsym >= kMaxClusters) {
84
2.84k
      return JXL_FAILURE("Invalid cluster ID");
85
2.84k
    }
86
150k
    if (!reader.CheckANSFinalState()) {
87
0
      return JXL_FAILURE("Invalid context map");
88
0
    }
89
150k
    if (use_mtf) {
90
21.6k
      InverseMoveToFrontTransform(context_map->data(), context_map->size());
91
21.6k
    }
92
150k
  }
93
254k
  *num_htrees = *std::max_element(context_map->begin(), context_map->end()) + 1;
94
254k
  return VerifyContextMap(*context_map, *num_htrees);
95
272k
}
96
97
}  // namespace jxl