Coverage Report

Created: 2025-07-16 07:53

/src/libjxl/lib/jxl/dec_context_map.cc
Line
Count
Source (jump to first uncovered line)
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 <cstdint>
12
#include <vector>
13
14
#include "lib/jxl/base/status.h"
15
#include "lib/jxl/dec_ans.h"
16
#include "lib/jxl/inverse_mtf-inl.h"
17
18
namespace jxl {
19
20
namespace {
21
22
// Context map uses uint8_t.
23
constexpr size_t kMaxClusters = 256;
24
25
Status VerifyContextMap(const std::vector<uint8_t>& context_map,
26
24.1k
                        const size_t num_htrees) {
27
24.1k
  std::vector<bool> have_htree(num_htrees);
28
24.1k
  size_t num_found = 0;
29
45.1M
  for (const uint8_t htree : context_map) {
30
45.1M
    if (htree >= num_htrees) {
31
0
      return JXL_FAILURE("Invalid histogram index in context map.");
32
0
    }
33
45.1M
    if (!have_htree[htree]) {
34
33.2k
      have_htree[htree] = true;
35
33.2k
      ++num_found;
36
33.2k
    }
37
45.1M
  }
38
24.1k
  if (num_found != num_htrees) {
39
62
    return JXL_FAILURE("Incomplete context map.");
40
62
  }
41
24.0k
  return true;
42
24.1k
}
43
44
}  // namespace
45
46
Status DecodeContextMap(JxlMemoryManager* memory_manager,
47
                        std::vector<uint8_t>* context_map, size_t* num_htrees,
48
24.7k
                        BitReader* input) {
49
24.7k
  bool is_simple = static_cast<bool>(input->ReadFixedBits<1>());
50
24.7k
  if (is_simple) {
51
22.4k
    int bits_per_entry = input->ReadFixedBits<2>();
52
22.4k
    if (bits_per_entry != 0) {
53
94.3k
      for (uint8_t& entry : *context_map) {
54
94.3k
        entry = input->ReadBits(bits_per_entry);
55
94.3k
      }
56
18.1k
    } else {
57
18.1k
      std::fill(context_map->begin(), context_map->end(), 0);
58
18.1k
    }
59
22.4k
  } else {
60
2.28k
    bool use_mtf = static_cast<bool>(input->ReadFixedBits<1>());
61
2.28k
    ANSCode code;
62
2.28k
    std::vector<uint8_t> sink_ctx_map;
63
    // Usage of LZ77 is disallowed if decoding only two symbols. This doesn't
64
    // make sense in non-malicious bitstreams, and could cause a stack overflow
65
    // in malicious bitstreams by making every context map require its own
66
    // context map.
67
2.28k
    JXL_RETURN_IF_ERROR(
68
2.28k
        DecodeHistograms(memory_manager, input, 1, &code, &sink_ctx_map,
69
2.28k
                         /*disallow_lz77=*/context_map->size() <= 2));
70
3.69k
    JXL_ASSIGN_OR_RETURN(ANSSymbolReader reader,
71
3.69k
                         ANSSymbolReader::Create(&code, input));
72
3.69k
    size_t i = 0;
73
3.69k
    uint32_t maxsym = 0;
74
63.2k
    while (i < context_map->size()) {
75
61.3k
      uint32_t sym = reader.ReadHybridUintInlined</*uses_lz77=*/true>(
76
61.3k
          0, input, sink_ctx_map);
77
61.3k
      maxsym = sym > maxsym ? sym : maxsym;
78
61.3k
      (*context_map)[i] = sym;
79
61.3k
      i++;
80
61.3k
    }
81
3.69k
    if (maxsym >= kMaxClusters) {
82
130
      return JXL_FAILURE("Invalid cluster ID");
83
130
    }
84
1.71k
    if (!reader.CheckANSFinalState()) {
85
0
      return JXL_FAILURE("Invalid context map");
86
0
    }
87
1.71k
    if (use_mtf) {
88
845
      InverseMoveToFrontTransform(context_map->data(), context_map->size());
89
845
    }
90
1.71k
  }
91
24.1k
  *num_htrees = *std::max_element(context_map->begin(), context_map->end()) + 1;
92
24.1k
  return VerifyContextMap(*context_map, *num_htrees);
93
24.7k
}
94
95
}  // namespace jxl