Coverage Report

Created: 2026-06-14 06:57

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
4.89k
JxlBoxContentDecoder::JxlBoxContentDecoder() = default;
21
22
4.89k
JxlBoxContentDecoder::~JxlBoxContentDecoder() {
23
4.89k
  if (brotli_dec) {
24
543
    BrotliDecoderDestroyInstance(brotli_dec);
25
543
  }
26
4.89k
}
27
28
void JxlBoxContentDecoder::StartBox(bool brob_decode, bool box_until_eof,
29
9.31k
                                    size_t contents_size) {
30
9.31k
  if (brotli_dec) {
31
17
    BrotliDecoderDestroyInstance(brotli_dec);
32
17
    brotli_dec = nullptr;
33
17
  }
34
9.31k
  header_done_ = false;
35
9.31k
  brob_decode_ = brob_decode;
36
9.31k
  box_until_eof_ = box_until_eof;
37
9.31k
  remaining_ = box_until_eof ? 0 : contents_size;
38
9.31k
  pos_ = 0;
39
9.31k
}
40
41
JxlDecoderStatus JxlBoxContentDecoder::Process(const uint8_t* next_in,
42
                                               size_t avail_in, size_t box_pos,
43
                                               uint8_t** next_out,
44
30.0k
                                               size_t* avail_out) {
45
  // The caller provides `box_pos` as the current position (in bytes) within
46
  // the box contents corresponding to `next_in`. Our internal `pos_` tracks
47
  // how many bytes of box contents have been consumed/processed so far.
48
  // If `box_pos` ever exceeds `pos_`, adjusting by `pos_ - box_pos` would
49
  // underflow and cause out-of-bounds reads.
50
30.0k
  if (box_pos > pos_) return JXL_DEC_ERROR;
51
30.0k
  const size_t offset = pos_ - box_pos;
52
30.0k
  if (offset > avail_in) return JXL_DEC_ERROR;
53
30.0k
  next_in += offset;
54
30.0k
  avail_in -= offset;
55
56
30.0k
  if (brob_decode_) {
57
26.0k
    if (!header_done_) {
58
560
      if (avail_in < 4) return JXL_DEC_NEED_MORE_INPUT;
59
560
      if (!box_until_eof_) {
60
140
        if (remaining_ < 4) return JXL_DEC_ERROR;
61
140
        remaining_ -= 4;
62
140
      }
63
560
      next_in += 4;
64
560
      avail_in -= 4;
65
560
      pos_ += 4;
66
560
      header_done_ = true;
67
560
    }
68
69
26.0k
    if (!brotli_dec) {
70
560
      brotli_dec = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
71
560
    }
72
73
26.0k
    size_t avail_in_brotli = avail_in;
74
26.0k
    if (!box_until_eof_) avail_in_brotli = std::min<size_t>(avail_in_brotli, remaining_);
75
26.0k
    const uint8_t* next_in_before = next_in;
76
26.0k
    uint8_t* next_out_before = *next_out;
77
26.0k
    msan::MemoryIsInitialized(next_in, avail_in_brotli);
78
26.0k
    BrotliDecoderResult res = BrotliDecoderDecompressStream(
79
26.0k
        brotli_dec, &avail_in_brotli, &next_in, avail_out, next_out, nullptr);
80
26.0k
    size_t consumed = next_in - next_in_before;
81
26.0k
    size_t produced = *next_out - next_out_before;
82
26.0k
    if (res == BROTLI_DECODER_RESULT_ERROR) {
83
155
      return JXL_DEC_ERROR;
84
155
    }
85
25.8k
    msan::UnpoisonMemory(next_out_before, produced);
86
25.8k
    pos_ += consumed;
87
25.8k
    if (!box_until_eof_) {
88
9.85k
      remaining_ -= consumed;
89
9.85k
      if (res == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT && remaining_ == 0) {
90
46
        return JXL_DEC_ERROR;
91
46
      }
92
9.85k
    }
93
25.8k
    if (res == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {
94
327
      return JXL_DEC_NEED_MORE_INPUT;
95
327
    }
96
25.5k
    if (res == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
97
25.4k
      return JXL_DEC_BOX_NEED_MORE_OUTPUT;
98
25.4k
    }
99
32
    if (res == BROTLI_DECODER_RESULT_SUCCESS) {
100
32
      if (!box_until_eof_ && remaining_ != 0) return JXL_DEC_ERROR;
101
30
      return JXL_DEC_BOX_COMPLETE;
102
32
    }
103
    // unknown Brotli result
104
0
    return JXL_DEC_ERROR;
105
4.01k
  } else {
106
    // remaining box bytes as seen from dec->file_pos
107
4.01k
    size_t can_read = avail_in;
108
4.01k
    if (!box_until_eof_) can_read = std::min<size_t>(can_read, remaining_);
109
4.01k
    size_t to_write = std::min<size_t>(can_read, *avail_out);
110
4.01k
    memcpy(*next_out, next_in, to_write);
111
112
4.01k
    *next_out += to_write;
113
4.01k
    *avail_out -= to_write;
114
4.01k
    if (!box_until_eof_) remaining_ -= to_write;
115
4.01k
    pos_ += to_write;
116
117
4.01k
    if (to_write < can_read) return JXL_DEC_BOX_NEED_MORE_OUTPUT;
118
119
1.09k
    if (!box_until_eof_ && remaining_ > 0) return JXL_DEC_NEED_MORE_INPUT;
120
121
1.09k
    return JXL_DEC_BOX_COMPLETE;
122
1.09k
  }
123
30.0k
}
124
}  // namespace jxl