Coverage Report

Created: 2026-11-12 07:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjxl/lib/jxl/box_content_decoder.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/box_content_decoder.h"
7
8
#include <brotli/decode.h>
9
#include <jxl/decode.h>
10
11
#include <algorithm>
12
#include <cstddef>
13
#include <cstdint>
14
#include <cstring>
15
16
#include "lib/jxl/base/sanitizers.h"
17
18
namespace jxl {
19
20
52.6k
JxlBoxContentDecoder::JxlBoxContentDecoder() = default;
21
22
52.6k
JxlBoxContentDecoder::~JxlBoxContentDecoder() { Reset(); }
23
24
109k
void JxlBoxContentDecoder::Reset() {
25
109k
  if (brotli_dec) {
26
506
    BrotliDecoderDestroyInstance(brotli_dec);
27
506
    brotli_dec = nullptr;
28
506
  }
29
109k
  header_done_ = false;
30
109k
  brob_decode_ = false;
31
109k
  box_until_eof_ = false;
32
109k
  remaining_ = 0;
33
109k
  pos_ = 0;
34
109k
}
35
36
void JxlBoxContentDecoder::StartBox(bool brob_decode, bool box_until_eof,
37
3.71k
                                    size_t contents_size) {
38
3.71k
  Reset();
39
3.71k
  brob_decode_ = brob_decode;
40
3.71k
  box_until_eof_ = box_until_eof;
41
3.71k
  remaining_ = box_until_eof ? 0 : contents_size;
42
3.71k
}
43
44
JxlDecoderStatus JxlBoxContentDecoder::Process(const uint8_t* next_in,
45
                                               size_t avail_in, size_t box_pos,
46
                                               uint8_t** next_out,
47
923
                                               size_t* avail_out) {
48
  // The caller provides `box_pos` as the current position (in bytes) within
49
  // the box contents corresponding to `next_in`. Our internal `pos_` tracks
50
  // how many bytes of box contents have been consumed/processed so far.
51
  // If `box_pos` ever exceeds `pos_`, adjusting by `pos_ - box_pos` would
52
  // underflow and cause out-of-bounds reads.
53
923
  if (box_pos > pos_) return JXL_DEC_ERROR;
54
923
  const size_t offset = pos_ - box_pos;
55
923
  if (offset > avail_in) return JXL_DEC_ERROR;
56
923
  next_in += offset;
57
923
  avail_in -= offset;
58
59
923
  if (brob_decode_) {
60
842
    if (!header_done_) {
61
507
      if (avail_in < 4) return JXL_DEC_NEED_MORE_INPUT;
62
507
      if (!box_until_eof_) {
63
507
        if (remaining_ < 4) return JXL_DEC_ERROR;
64
506
        remaining_ -= 4;
65
506
      }
66
506
      next_in += 4;
67
506
      avail_in -= 4;
68
506
      pos_ += 4;
69
506
      header_done_ = true;
70
506
    }
71
72
841
    if (!brotli_dec) {
73
506
      brotli_dec = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
74
506
    }
75
76
841
    size_t avail_in_brotli = avail_in;
77
841
    if (!box_until_eof_) avail_in_brotli = std::min<size_t>(avail_in_brotli, remaining_);
78
841
    const uint8_t* next_in_before = next_in;
79
841
    uint8_t* next_out_before = *next_out;
80
841
    msan::MemoryIsInitialized(next_in, avail_in_brotli);
81
841
    BrotliDecoderResult res = BrotliDecoderDecompressStream(
82
841
        brotli_dec, &avail_in_brotli, &next_in, avail_out, next_out, nullptr);
83
841
    size_t consumed = next_in - next_in_before;
84
841
    size_t produced = *next_out - next_out_before;
85
841
    if (res == BROTLI_DECODER_RESULT_ERROR) {
86
165
      return JXL_DEC_ERROR;
87
165
    }
88
676
    msan::UnpoisonMemory(next_out_before, produced);
89
676
    pos_ += consumed;
90
676
    if (!box_until_eof_) {
91
676
      remaining_ -= consumed;
92
676
      if (res == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT && remaining_ == 0) {
93
17
        return JXL_DEC_ERROR;
94
17
      }
95
676
    }
96
659
    if (res == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {
97
634
      return JXL_DEC_NEED_MORE_INPUT;
98
634
    }
99
25
    if (res == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
100
18
      return JXL_DEC_BOX_NEED_MORE_OUTPUT;
101
18
    }
102
7
    if (res == BROTLI_DECODER_RESULT_SUCCESS) {
103
7
      if (!box_until_eof_ && remaining_ != 0) return JXL_DEC_ERROR;
104
0
      return JXL_DEC_BOX_COMPLETE;
105
7
    }
106
    // unknown Brotli result
107
0
    return JXL_DEC_ERROR;
108
81
  } else {
109
    // remaining box bytes as seen from dec->file_pos
110
81
    size_t can_read = avail_in;
111
81
    if (!box_until_eof_) can_read = std::min<size_t>(can_read, remaining_);
112
81
    size_t to_write = std::min<size_t>(can_read, *avail_out);
113
81
    memcpy(*next_out, next_in, to_write);
114
115
81
    *next_out += to_write;
116
81
    *avail_out -= to_write;
117
81
    if (!box_until_eof_) remaining_ -= to_write;
118
81
    pos_ += to_write;
119
120
81
    if (to_write < can_read) return JXL_DEC_BOX_NEED_MORE_OUTPUT;
121
122
81
    if (!box_until_eof_ && remaining_ > 0) return JXL_DEC_NEED_MORE_INPUT;
123
124
51
    return JXL_DEC_BOX_COMPLETE;
125
81
  }
126
923
}
127
}  // namespace jxl