Coverage Report

Created: 2026-09-13 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/file_layout.cc
Line
Count
Source
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
4
 *
5
 * This file is part of libheif.
6
 *
7
 * libheif is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as
9
 * published by the Free Software Foundation, either version 3 of
10
 * the License, or (at your option) any later version.
11
 *
12
 * libheif is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include "file_layout.h"
22
#include "sequences/seq_boxes.h"
23
#include <limits>
24
25
26
FileLayout::FileLayout()
27
26.4k
{
28
26.4k
  auto ftyp = std::make_shared<Box_ftyp>();
29
26.4k
  ftyp->set_output_position(0);
30
26.4k
  m_boxes.push_back(ftyp);
31
32
  // TODO: these variables are not used yet
33
26.4k
  (void)m_writeMode;
34
26.4k
  (void)m_file_size;
35
26.4k
}
36
37
38
Error FileLayout::read(const std::shared_ptr<StreamReader>& stream, const heif_security_limits* limits)
39
13.2k
{
40
13.2k
  m_boxes.clear();
41
42
13.2k
  m_stream_reader = stream;
43
44
  // --- read initial range, large enough to cover 'ftyp' box
45
46
13.2k
  m_max_length = stream->request_range(0, INITIAL_FTYP_REQUEST);
47
48
13.2k
  if (m_max_length < MAXIMUM_BOX_HEADER_SIZE) {
49
9
    return {heif_error_Invalid_input,
50
9
            heif_suberror_Unspecified,
51
9
            "File size too small."};
52
9
  }
53
54
  // --- read 'ftyp' box header
55
56
13.2k
  BitstreamRange ftyp_hdr_range(m_stream_reader, m_max_length);
57
13.2k
  BoxHeader ftyp_header;
58
13.2k
  Error err;
59
13.2k
  err = ftyp_header.parse_header(ftyp_hdr_range);
60
13.2k
  if (err) {
61
11
    return err;
62
11
  }
63
64
  // --- check whether it is a valid 'ftyp' box header
65
66
13.2k
  if (ftyp_header.get_short_type() != fourcc("ftyp")) {
67
94
    return {heif_error_Invalid_input,
68
94
            heif_suberror_No_ftyp_box,
69
94
            "File does not start with 'ftyp' box."};
70
94
  }
71
72
13.1k
  uint64_t ftyp_size = ftyp_header.get_box_size();
73
74
13.1k
  if (ftyp_size == 0) {
75
1
    return {heif_error_Invalid_input,
76
1
            heif_suberror_No_ftyp_box,
77
1
            "ftyp box shall not be the only box in the file"};
78
1
  }
79
80
13.1k
  if (ftyp_size > m_max_length) {
81
128
    return {heif_error_Invalid_input,
82
128
            heif_suberror_No_ftyp_box,
83
128
            "ftyp box larger than initial read range"};
84
128
  }
85
86
  // --- read the 'ftyp' box
87
88
12.9k
  BitstreamRange ftyp_range(m_stream_reader, 0, ftyp_size);
89
12.9k
  std::shared_ptr<Box> ftyp_box;
90
12.9k
  err = Box::read(ftyp_range, &ftyp_box, limits);
91
12.9k
  if (err) {
92
28
    return err;
93
28
  }
94
95
12.9k
  m_boxes.push_back(ftyp_box);
96
12.9k
  m_ftyp_box = std::dynamic_pointer_cast<Box_ftyp>(ftyp_box);
97
98
99
  // --- skip through box headers until we find the 'meta' box
100
101
12.9k
  uint64_t next_box_start = ftyp_size;
102
103
12.9k
  bool meta_found = false;
104
12.9k
  bool mini_found = false;
105
12.9k
  bool moov_found = false;
106
107
39.0k
  for (;;) {
108
    // TODO: overflow
109
39.0k
    uint64_t next_box_header_end = next_box_start + MAXIMUM_BOX_HEADER_SIZE;
110
39.0k
    if (next_box_header_end > m_max_length) {
111
3.37k
      m_max_length = stream->request_range(next_box_start, next_box_header_end);
112
3.37k
    }
113
114
39.0k
    if (next_box_header_end > m_max_length) {
115
3.37k
      if (meta_found || mini_found || moov_found) {
116
3.16k
        return Error::Ok;
117
3.16k
      }
118
215
      else {
119
215
        return {heif_error_Invalid_input,
120
215
                heif_suberror_Unspecified,
121
215
                "Insufficient input data"};
122
215
      }
123
3.37k
    }
124
125
35.6k
    BitstreamRange box_range(m_stream_reader, next_box_start, m_max_length);
126
35.6k
    BoxHeader box_header;
127
35.6k
    err = box_header.parse_header(box_range);
128
35.6k
    if (err) {
129
1
      return err;
130
1
    }
131
132
35.6k
    if (box_header.get_short_type() == fourcc("meta")) {
133
2.32k
      const uint64_t meta_box_start = next_box_start;
134
2.32k
      uint64_t end_of_meta_box;
135
2.32k
      if (box_header.get_box_size() == BoxHeader::size_until_end_of_file) {
136
        // A box size of 0 means the box extends to the end of the file
137
        // (ISO/IEC 14496-12 clause 4.2), which is legal for the last box.
138
        // Resolve it to the file size.
139
339
        end_of_meta_box = m_stream_reader->request_range(meta_box_start,
140
339
                                                         std::numeric_limits<uint64_t>::max());
141
339
        m_max_length = end_of_meta_box;
142
339
        if (end_of_meta_box <= meta_box_start) {
143
0
          return {heif_error_Invalid_input,
144
0
                  heif_suberror_No_meta_box,
145
0
                  "Cannot read meta box with unspecified size"};
146
0
        }
147
339
      }
148
1.99k
      else {
149
1.99k
        end_of_meta_box = box_header.get_box_size();
150
1.99k
        if (end_of_meta_box > std::numeric_limits<uint64_t>::max() - meta_box_start) {
151
0
          return {heif_error_Invalid_input,
152
0
                  heif_suberror_No_meta_box,
153
0
                  "Cannot read meta box with invalid size"};
154
0
        }
155
1.99k
        end_of_meta_box += meta_box_start;
156
1.99k
      }
157
2.32k
      if (m_max_length < end_of_meta_box) {
158
122
        m_max_length = m_stream_reader->request_range(meta_box_start, end_of_meta_box);
159
122
      }
160
161
2.32k
      if (m_max_length < end_of_meta_box) {
162
122
        return {heif_error_Invalid_input,
163
122
                heif_suberror_No_meta_box,
164
122
                "Cannot read full meta box"};
165
122
      }
166
167
2.20k
      BitstreamRange meta_box_range(m_stream_reader, meta_box_start, end_of_meta_box);
168
2.20k
      std::shared_ptr<Box> meta_box;
169
2.20k
      err = Box::read(meta_box_range, &meta_box, limits);
170
2.20k
      if (err) {
171
300
        return err;
172
300
      }
173
174
1.90k
      m_boxes.push_back(meta_box);
175
1.90k
      m_meta_box = std::dynamic_pointer_cast<Box_meta>(meta_box);
176
1.90k
      meta_found = true;
177
1.90k
    }
178
179
    // TODO: this is basically the same as the meta box case above, with different error handling.
180
35.2k
    if (box_header.get_short_type() == fourcc("mini")) {
181
4.80k
      const uint64_t mini_box_start = next_box_start;
182
4.80k
      uint64_t end_of_mini_box;
183
4.80k
      if (box_header.get_box_size() == BoxHeader::size_until_end_of_file) {
184
        // A box size of 0 means the box extends to the end of the file
185
        // (ISO/IEC 14496-12 clause 4.2), which is legal for the last box.
186
        // Resolve it to the file size.
187
551
        end_of_mini_box = m_stream_reader->request_range(mini_box_start,
188
551
                                                         std::numeric_limits<uint64_t>::max());
189
551
        m_max_length = end_of_mini_box;
190
551
        if (end_of_mini_box <= mini_box_start) {
191
0
          return {heif_error_Invalid_input,
192
0
                  heif_suberror_Invalid_mini_box,
193
0
                  "Cannot read mini box with unspecified size"};
194
0
        }
195
551
      }
196
4.24k
      else {
197
4.24k
        end_of_mini_box = box_header.get_box_size();
198
4.24k
        if (end_of_mini_box > std::numeric_limits<uint64_t>::max() - mini_box_start) {
199
0
          return {heif_error_Invalid_input,
200
0
                  heif_suberror_Invalid_mini_box,
201
0
                  "Cannot read mini box with invalid size"};
202
0
        }
203
4.24k
        end_of_mini_box += mini_box_start;
204
4.24k
      }
205
4.80k
      if (m_max_length < end_of_mini_box) {
206
131
        m_max_length = m_stream_reader->request_range(mini_box_start, end_of_mini_box);
207
131
      }
208
209
4.80k
      if (m_max_length < end_of_mini_box) {
210
131
        return {heif_error_Invalid_input,
211
131
                heif_suberror_Invalid_mini_box,
212
131
                "Cannot read full mini box"};
213
131
      }
214
4.66k
      BitstreamRange mini_box_range(m_stream_reader, mini_box_start, end_of_mini_box);
215
4.66k
      std::shared_ptr<Box> mini_box;
216
4.66k
      err = Box::read(mini_box_range, &mini_box, heif_get_global_security_limits());
217
4.66k
      if (err) {
218
335
        return err;
219
335
      }
220
221
4.33k
      m_boxes.push_back(mini_box);
222
4.33k
      m_mini_box = std::dynamic_pointer_cast<Box_mini>(mini_box);
223
224
4.33k
      mini_found = true;
225
4.33k
    }
226
227
34.7k
    if (box_header.get_short_type() == fourcc("moov")) {
228
23.1k
      const uint64_t moov_box_start = next_box_start;
229
23.1k
      uint64_t end_of_moov_box;
230
23.1k
      if (box_header.get_box_size() == BoxHeader::size_until_end_of_file) {
231
        // A box size of 0 means the box extends to the end of the file
232
        // (ISO/IEC 14496-12 clause 4.2), which is legal for the last box.
233
        // Resolve it to the file size.
234
6.98k
        end_of_moov_box = m_stream_reader->request_range(moov_box_start,
235
6.98k
                                                         std::numeric_limits<uint64_t>::max());
236
6.98k
        m_max_length = end_of_moov_box;
237
6.98k
        if (end_of_moov_box <= moov_box_start) {
238
0
          return {heif_error_Invalid_input,
239
0
                  heif_suberror_No_moov_box,
240
0
                  "Cannot read moov box with unspecified size"};
241
0
        }
242
6.98k
      }
243
16.1k
      else {
244
16.1k
        end_of_moov_box = box_header.get_box_size();
245
16.1k
        if (end_of_moov_box > std::numeric_limits<uint64_t>::max() - moov_box_start) {
246
0
          return {heif_error_Invalid_input,
247
0
                  heif_suberror_No_moov_box,
248
0
                  "Cannot read moov box with invalid size"};
249
0
        }
250
16.1k
        end_of_moov_box += moov_box_start;
251
16.1k
      }
252
23.1k
      if (m_max_length < end_of_moov_box) {
253
134
        m_max_length = m_stream_reader->request_range(moov_box_start, end_of_moov_box);
254
134
      }
255
256
23.1k
      if (m_max_length < end_of_moov_box) {
257
134
        return {heif_error_Invalid_input,
258
134
                heif_suberror_No_moov_box,
259
134
                "Cannot read full moov box"};
260
134
      }
261
262
23.0k
      BitstreamRange moov_box_range(m_stream_reader, moov_box_start, end_of_moov_box);
263
23.0k
      std::shared_ptr<Box> moov_box;
264
23.0k
      err = Box::read(moov_box_range, &moov_box, limits);
265
23.0k
      if (err) {
266
6.25k
        return err;
267
6.25k
      }
268
269
16.7k
      m_boxes.push_back(moov_box);
270
16.7k
      m_moov_box = std::dynamic_pointer_cast<Box_moov>(moov_box);
271
272
16.7k
      moov_found = true;
273
16.7k
    }
274
275
28.3k
    uint64_t boxSize = box_header.get_box_size();
276
28.3k
    if (boxSize == Box::size_until_end_of_file) {
277
2.29k
      if (meta_found || mini_found || moov_found) {
278
2.28k
        return Error::Ok;
279
2.28k
      }
280
12
      else {
281
12
        return {heif_error_Invalid_input,
282
12
                heif_suberror_Unspecified,
283
12
                "No meta box found"};
284
12
      }
285
2.29k
    }
286
287
26.0k
    if (std::numeric_limits<uint64_t>::max() - boxSize < next_box_start) {
288
0
      return {heif_error_Invalid_input,
289
0
              heif_suberror_Unspecified,
290
0
              "Box size too large, integer overflow"};
291
0
    }
292
293
26.0k
    next_box_start = next_box_start + boxSize;
294
26.0k
  }
295
296
0
  return Error::Ok;
297
12.9k
}
298
299
300
void FileLayout::set_write_mode(WriteMode writeMode, const std::shared_ptr<StreamWriter>& writer)
301
0
{
302
303
0
}
304
305
306
Error FileLayout::write(std::shared_ptr<StreamWriter>& stream)
307
0
{
308
0
  return Error::Ok;
309
0
}