1 // SPDX-License-Identifier: 0BSD
3 ///////////////////////////////////////////////////////////////////////////////
6 /// \brief Validates Index by using a hash function
8 // Author: Lasse Collin
10 ///////////////////////////////////////////////////////////////////////////////
18 /// Sum of the Block sizes (including Block Padding)
21 /// Sum of the Uncompressed Size fields
22 lzma_vli uncompressed_size
;
27 /// Size of the List of Index Records as bytes
28 lzma_vli index_list_size
;
30 /// Check calculated from Unpadded Sizes and Uncompressed Sizes.
31 lzma_check_state check
;
33 } lzma_index_hash_info
;
36 struct lzma_index_hash_s
{
47 /// Information collected while decoding the actual Blocks.
48 lzma_index_hash_info blocks
;
50 /// Information collected from the Index field.
51 lzma_index_hash_info records
;
53 /// Number of Records not fully decoded
56 /// Unpadded Size currently being read from an Index Record.
57 lzma_vli unpadded_size
;
59 /// Uncompressed Size currently being read from an Index Record.
60 lzma_vli uncompressed_size
;
62 /// Position in variable-length integers when decoding them from
63 /// the List of Records.
66 /// CRC32 of the Index
71 extern LZMA_API(lzma_index_hash
*)
72 lzma_index_hash_init(lzma_index_hash
*index_hash
,
73 const lzma_allocator
*allocator
)
75 if (index_hash
== NULL
) {
76 index_hash
= lzma_alloc(sizeof(lzma_index_hash
), allocator
);
77 if (index_hash
== NULL
)
81 index_hash
->sequence
= SEQ_BLOCK
;
82 index_hash
->blocks
.blocks_size
= 0;
83 index_hash
->blocks
.uncompressed_size
= 0;
84 index_hash
->blocks
.count
= 0;
85 index_hash
->blocks
.index_list_size
= 0;
86 index_hash
->records
.blocks_size
= 0;
87 index_hash
->records
.uncompressed_size
= 0;
88 index_hash
->records
.count
= 0;
89 index_hash
->records
.index_list_size
= 0;
90 index_hash
->unpadded_size
= 0;
91 index_hash
->uncompressed_size
= 0;
93 index_hash
->crc32
= 0;
95 // These cannot fail because LZMA_CHECK_BEST is known to be supported.
96 (void)lzma_check_init(&index_hash
->blocks
.check
, LZMA_CHECK_BEST
);
97 (void)lzma_check_init(&index_hash
->records
.check
, LZMA_CHECK_BEST
);
103 extern LZMA_API(void)
104 lzma_index_hash_end(lzma_index_hash
*index_hash
,
105 const lzma_allocator
*allocator
)
107 lzma_free(index_hash
, allocator
);
112 extern LZMA_API(lzma_vli
)
113 lzma_index_hash_size(const lzma_index_hash
*index_hash
)
115 // Get the size of the Index from ->blocks instead of ->records for
116 // cases where application wants to know the Index Size before
117 // decoding the Index.
118 return index_size(index_hash
->blocks
.count
,
119 index_hash
->blocks
.index_list_size
);
123 /// Updates the sizes and the hash without any validation.
125 hash_append(lzma_index_hash_info
*info
, lzma_vli unpadded_size
,
126 lzma_vli uncompressed_size
)
128 info
->blocks_size
+= vli_ceil4(unpadded_size
);
129 info
->uncompressed_size
+= uncompressed_size
;
130 info
->index_list_size
+= lzma_vli_size(unpadded_size
)
131 + lzma_vli_size(uncompressed_size
);
134 const lzma_vli sizes
[2] = { unpadded_size
, uncompressed_size
};
135 lzma_check_update(&info
->check
, LZMA_CHECK_BEST
,
136 (const uint8_t *)(sizes
), sizeof(sizes
));
142 extern LZMA_API(lzma_ret
)
143 lzma_index_hash_append(lzma_index_hash
*index_hash
, lzma_vli unpadded_size
,
144 lzma_vli uncompressed_size
)
146 // Validate the arguments.
147 if (index_hash
== NULL
|| index_hash
->sequence
!= SEQ_BLOCK
148 || unpadded_size
< UNPADDED_SIZE_MIN
149 || unpadded_size
> UNPADDED_SIZE_MAX
150 || uncompressed_size
> LZMA_VLI_MAX
)
151 return LZMA_PROG_ERROR
;
154 hash_append(&index_hash
->blocks
, unpadded_size
, uncompressed_size
);
156 // Validate the properties of *info are still in allowed limits.
157 if (index_hash
->blocks
.blocks_size
> LZMA_VLI_MAX
158 || index_hash
->blocks
.uncompressed_size
> LZMA_VLI_MAX
159 || index_size(index_hash
->blocks
.count
,
160 index_hash
->blocks
.index_list_size
)
161 > LZMA_BACKWARD_SIZE_MAX
162 || index_stream_size(index_hash
->blocks
.blocks_size
,
163 index_hash
->blocks
.count
,
164 index_hash
->blocks
.index_list_size
)
166 return LZMA_DATA_ERROR
;
172 extern LZMA_API(lzma_ret
)
173 lzma_index_hash_decode(lzma_index_hash
*index_hash
, const uint8_t *in
,
174 size_t *in_pos
, size_t in_size
)
176 // Catch zero input buffer here, because in contrast to Index encoder
177 // and decoder functions, applications call this function directly
178 // instead of via lzma_code(), which does the buffer checking.
179 if (*in_pos
>= in_size
)
180 return LZMA_BUF_ERROR
;
182 // NOTE: This function has many similarities to index_encode() and
183 // index_decode() functions found from index_encoder.c and
184 // index_decoder.c. See the comments especially in index_encoder.c.
185 const size_t in_start
= *in_pos
;
186 lzma_ret ret
= LZMA_OK
;
188 while (*in_pos
< in_size
)
189 switch (index_hash
->sequence
) {
191 // Check the Index Indicator is present.
192 if (in
[(*in_pos
)++] != INDEX_INDICATOR
)
193 return LZMA_DATA_ERROR
;
195 index_hash
->sequence
= SEQ_COUNT
;
199 ret
= lzma_vli_decode(&index_hash
->remaining
,
200 &index_hash
->pos
, in
, in_pos
, in_size
);
201 if (ret
!= LZMA_STREAM_END
)
204 // The count must match the count of the Blocks decoded.
205 if (index_hash
->remaining
!= index_hash
->blocks
.count
)
206 return LZMA_DATA_ERROR
;
211 // Handle the special case when there are no Blocks.
212 index_hash
->sequence
= index_hash
->remaining
== 0
213 ? SEQ_PADDING_INIT
: SEQ_UNPADDED
;
218 case SEQ_UNCOMPRESSED
: {
219 lzma_vli
*size
= index_hash
->sequence
== SEQ_UNPADDED
220 ? &index_hash
->unpadded_size
221 : &index_hash
->uncompressed_size
;
223 ret
= lzma_vli_decode(size
, &index_hash
->pos
,
224 in
, in_pos
, in_size
);
225 if (ret
!= LZMA_STREAM_END
)
231 if (index_hash
->sequence
== SEQ_UNPADDED
) {
232 if (index_hash
->unpadded_size
< UNPADDED_SIZE_MIN
233 || index_hash
->unpadded_size
235 return LZMA_DATA_ERROR
;
237 index_hash
->sequence
= SEQ_UNCOMPRESSED
;
240 hash_append(&index_hash
->records
,
241 index_hash
->unpadded_size
,
242 index_hash
->uncompressed_size
);
244 // Verify that we don't go over the known sizes. Note
245 // that this validation is simpler than the one used
246 // in lzma_index_hash_append(), because here we know
247 // that values in index_hash->blocks are already
248 // validated and we are fine as long as we don't
249 // exceed them in index_hash->records.
250 if (index_hash
->blocks
.blocks_size
251 < index_hash
->records
.blocks_size
252 || index_hash
->blocks
.uncompressed_size
253 < index_hash
->records
.uncompressed_size
254 || index_hash
->blocks
.index_list_size
255 < index_hash
->records
.index_list_size
)
256 return LZMA_DATA_ERROR
;
258 // Check if this was the last Record.
259 index_hash
->sequence
= --index_hash
->remaining
== 0
260 ? SEQ_PADDING_INIT
: SEQ_UNPADDED
;
266 case SEQ_PADDING_INIT
:
267 index_hash
->pos
= (LZMA_VLI_C(4) - index_size_unpadded(
268 index_hash
->records
.count
,
269 index_hash
->records
.index_list_size
)) & 3;
270 index_hash
->sequence
= SEQ_PADDING
;
275 if (index_hash
->pos
> 0) {
277 if (in
[(*in_pos
)++] != 0x00)
278 return LZMA_DATA_ERROR
;
283 // Compare the sizes.
284 if (index_hash
->blocks
.blocks_size
285 != index_hash
->records
.blocks_size
286 || index_hash
->blocks
.uncompressed_size
287 != index_hash
->records
.uncompressed_size
288 || index_hash
->blocks
.index_list_size
289 != index_hash
->records
.index_list_size
)
290 return LZMA_DATA_ERROR
;
292 // Finish the hashes and compare them.
293 lzma_check_finish(&index_hash
->blocks
.check
, LZMA_CHECK_BEST
);
294 lzma_check_finish(&index_hash
->records
.check
, LZMA_CHECK_BEST
);
295 if (memcmp(index_hash
->blocks
.check
.buffer
.u8
,
296 index_hash
->records
.check
.buffer
.u8
,
297 lzma_check_size(LZMA_CHECK_BEST
)) != 0)
298 return LZMA_DATA_ERROR
;
300 // Finish the CRC32 calculation.
301 index_hash
->crc32
= lzma_crc32(in
+ in_start
,
302 *in_pos
- in_start
, index_hash
->crc32
);
304 index_hash
->sequence
= SEQ_CRC32
;
310 if (*in_pos
== in_size
)
313 if (((index_hash
->crc32
>> (index_hash
->pos
* 8))
314 & 0xFF) != in
[(*in_pos
)++]) {
315 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
316 return LZMA_DATA_ERROR
;
320 } while (++index_hash
->pos
< 4);
322 return LZMA_STREAM_END
;
326 return LZMA_PROG_ERROR
;
332 // Avoid null pointer + 0 (undefined behavior) in "in + in_start".
333 // In such a case we had no input and thus in_used == 0.
335 const size_t in_used
= *in_pos
- in_start
;
337 index_hash
->crc32
= lzma_crc32(in
+ in_start
,
338 in_used
, index_hash
->crc32
);