1 /* Copyright (c) 2004, Roger Dingledine.
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2021, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
7 * \file compress_lzma.c
8 * \brief Compression backend for LZMA.
10 * This module should never be invoked directly. Use the compress module
16 #include "lib/compress/compress.h"
17 #include "lib/compress/compress_lzma.h"
18 #include "lib/log/log.h"
19 #include "lib/log/util_bug.h"
20 #include "lib/malloc/malloc.h"
21 #include "lib/thread/threads.h"
27 /** The maximum amount of memory we allow the LZMA decoder to use, in bytes. */
28 #define MEMORY_LIMIT (16 * 1024 * 1024)
30 /** Total number of bytes allocated for LZMA state. */
31 static atomic_counter_t total_lzma_allocation
;
34 /** Given <b>level</b> return the memory level. */
36 memory_level(compression_level_t level
)
40 case BEST_COMPRESSION
:
41 case HIGH_COMPRESSION
: return 6;
42 case MEDIUM_COMPRESSION
: return 4;
43 case LOW_COMPRESSION
: return 2;
47 /** Convert a given <b>error</b> to a human readable error string. */
49 lzma_error_str(lzma_ret error
)
53 return "Operation completed successfully";
55 return "End of stream";
57 return "Input stream lacks integrity check";
58 case LZMA_UNSUPPORTED_CHECK
:
59 return "Unable to calculate integrity check";
61 return "Integrity check available";
63 return "Unable to allocate memory";
64 case LZMA_MEMLIMIT_ERROR
:
65 return "Memory limit reached";
66 case LZMA_FORMAT_ERROR
:
67 return "Unknown file format";
68 case LZMA_OPTIONS_ERROR
:
69 return "Unsupported options";
71 return "Corrupt input data";
73 return "Unable to progress";
75 return "Programming error";
77 return "Unknown LZMA error";
80 #endif /* defined(HAVE_LZMA) */
82 /** Return 1 if LZMA compression is supported; otherwise 0. */
84 tor_lzma_method_supported(void)
93 /** Return a string representation of the version of the currently running
94 * version of liblzma. Returns NULL if LZMA is unsupported. */
96 tor_lzma_get_version_str(void)
99 return lzma_version_string();
105 /** Return a string representation of the version of liblzma used at
106 * compilation time. Returns NULL if LZMA is unsupported. */
108 tor_lzma_get_header_version_str(void)
111 return LZMA_VERSION_STRING
;
117 /** Internal LZMA state for incremental compression/decompression.
118 * The body of this struct is not exposed. */
119 struct tor_lzma_compress_state_t
{
121 lzma_stream stream
; /**< The LZMA stream. */
124 int compress
; /**< True if we are compressing; false if we are inflating */
126 /** Number of bytes read so far. Used to detect compression bombs. */
128 /** Number of bytes written so far. Used to detect compression bombs. */
129 size_t output_so_far
;
131 /** Approximate number of bytes allocated for this object. */
136 /** Return an approximate number of bytes stored in memory to hold the LZMA
137 * encoder/decoder state. */
139 tor_lzma_state_size_precalc(int compress
, compression_level_t level
)
141 uint64_t memory_usage
;
144 memory_usage
= lzma_easy_encoder_memusage(memory_level(level
));
146 memory_usage
= lzma_easy_decoder_memusage(memory_level(level
));
148 if (memory_usage
== UINT64_MAX
) {
150 log_warn(LD_GENERAL
, "Unsupported compression level passed to LZMA %s",
151 compress
? "encoder" : "decoder");
156 if (memory_usage
+ sizeof(tor_lzma_compress_state_t
) > SIZE_MAX
)
157 memory_usage
= SIZE_MAX
;
159 memory_usage
+= sizeof(tor_lzma_compress_state_t
);
161 return (size_t)memory_usage
;
168 #endif /* defined(HAVE_LZMA) */
170 /** Construct and return a tor_lzma_compress_state_t object using
171 * <b>method</b>. If <b>compress</b>, it's for compression; otherwise it's for
173 tor_lzma_compress_state_t
*
174 tor_lzma_compress_new(int compress
,
175 compress_method_t method
,
176 compression_level_t level
)
178 tor_assert(method
== LZMA_METHOD
);
181 tor_lzma_compress_state_t
*result
;
183 lzma_options_lzma stream_options
;
185 // Note that we do not explicitly initialize the lzma_stream object here,
186 // since the LZMA_STREAM_INIT "just" initializes all members to 0, which is
187 // also what `tor_malloc_zero()` does.
188 result
= tor_malloc_zero(sizeof(tor_lzma_compress_state_t
));
189 result
->compress
= compress
;
190 result
->allocation
= tor_lzma_state_size_precalc(compress
, level
);
193 lzma_lzma_preset(&stream_options
, memory_level(level
));
195 retval
= lzma_alone_encoder(&result
->stream
, &stream_options
);
197 if (retval
!= LZMA_OK
) {
199 log_warn(LD_GENERAL
, "Error from LZMA encoder: %s (%u).",
200 lzma_error_str(retval
), retval
);
205 retval
= lzma_alone_decoder(&result
->stream
, MEMORY_LIMIT
);
207 if (retval
!= LZMA_OK
) {
209 log_warn(LD_GENERAL
, "Error from LZMA decoder: %s (%u).",
210 lzma_error_str(retval
), retval
);
216 atomic_counter_add(&total_lzma_allocation
, result
->allocation
);
219 /* LCOV_EXCL_START */
224 #else /* !defined(HAVE_LZMA) */
230 #endif /* defined(HAVE_LZMA) */
233 /** Compress/decompress some bytes using <b>state</b>. Read up to
234 * *<b>in_len</b> bytes from *<b>in</b>, and write up to *<b>out_len</b> bytes
235 * to *<b>out</b>, adjusting the values as we go. If <b>finish</b> is true,
236 * we've reached the end of the input.
238 * Return TOR_COMPRESS_DONE if we've finished the entire
239 * compression/decompression.
240 * Return TOR_COMPRESS_OK if we're processed everything from the input.
241 * Return TOR_COMPRESS_BUFFER_FULL if we're out of space on <b>out</b>.
242 * Return TOR_COMPRESS_ERROR if the stream is corrupt.
244 tor_compress_output_t
245 tor_lzma_compress_process(tor_lzma_compress_state_t
*state
,
246 char **out
, size_t *out_len
,
247 const char **in
, size_t *in_len
,
254 tor_assert(state
!= NULL
);
255 tor_assert(*in_len
<= UINT_MAX
);
256 tor_assert(*out_len
<= UINT_MAX
);
258 state
->stream
.next_in
= (unsigned char *)*in
;
259 state
->stream
.avail_in
= *in_len
;
260 state
->stream
.next_out
= (unsigned char *)*out
;
261 state
->stream
.avail_out
= *out_len
;
263 action
= finish
? LZMA_FINISH
: LZMA_RUN
;
265 retval
= lzma_code(&state
->stream
, action
);
267 state
->input_so_far
+= state
->stream
.next_in
- ((unsigned char *)*in
);
268 state
->output_so_far
+= state
->stream
.next_out
- ((unsigned char *)*out
);
270 *out
= (char *)state
->stream
.next_out
;
271 *out_len
= state
->stream
.avail_out
;
272 *in
= (const char *)state
->stream
.next_in
;
273 *in_len
= state
->stream
.avail_in
;
275 if (! state
->compress
&&
276 tor_compress_is_compression_bomb(state
->input_so_far
,
277 state
->output_so_far
)) {
278 log_warn(LD_DIR
, "Possible compression bomb; abandoning stream.");
279 return TOR_COMPRESS_ERROR
;
284 if (state
->stream
.avail_out
== 0 || finish
)
285 return TOR_COMPRESS_BUFFER_FULL
;
287 return TOR_COMPRESS_OK
;
290 if (state
->stream
.avail_in
== 0 && !finish
)
291 return TOR_COMPRESS_OK
;
293 return TOR_COMPRESS_BUFFER_FULL
;
295 case LZMA_STREAM_END
:
296 return TOR_COMPRESS_DONE
;
298 // We list all the possible values of `lzma_ret` here to silence the
299 // `switch-enum` warning and to detect if a new member was added.
301 case LZMA_UNSUPPORTED_CHECK
:
304 case LZMA_MEMLIMIT_ERROR
:
305 case LZMA_FORMAT_ERROR
:
306 case LZMA_OPTIONS_ERROR
:
307 case LZMA_DATA_ERROR
:
308 case LZMA_PROG_ERROR
:
310 log_warn(LD_GENERAL
, "LZMA %s didn't finish: %s.",
311 state
->compress
? "compression" : "decompression",
312 lzma_error_str(retval
));
313 return TOR_COMPRESS_ERROR
;
315 #else /* !defined(HAVE_LZMA) */
322 return TOR_COMPRESS_ERROR
;
323 #endif /* defined(HAVE_LZMA) */
326 /** Deallocate <b>state</b>. */
328 tor_lzma_compress_free_(tor_lzma_compress_state_t
*state
)
333 atomic_counter_sub(&total_lzma_allocation
, state
->allocation
);
336 lzma_end(&state
->stream
);
342 /** Return the approximate number of bytes allocated for <b>state</b>. */
344 tor_lzma_compress_state_size(const tor_lzma_compress_state_t
*state
)
346 tor_assert(state
!= NULL
);
347 return state
->allocation
;
350 /** Return the approximate number of bytes allocated for all LZMA states. */
352 tor_lzma_get_total_allocation(void)
354 return atomic_counter_get(&total_lzma_allocation
);
357 /** Initialize the lzma module */
361 atomic_counter_init(&total_lzma_allocation
);