Tor  0.4.7.0-alpha-dev
compress_lzma.c
Go to the documentation of this file.
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 */
5 
6 /**
7  * \file compress_lzma.c
8  * \brief Compression backend for LZMA.
9  *
10  * This module should never be invoked directly. Use the compress module
11  * instead.
12  **/
13 
14 #include "orconfig.h"
15 
16 #include "lib/compress/compress.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"
22 
23 #ifdef HAVE_LZMA
24 #include <lzma.h>
25 #endif
26 
27 /** The maximum amount of memory we allow the LZMA decoder to use, in bytes. */
28 #define MEMORY_LIMIT (16 * 1024 * 1024)
29 
30 /** Total number of bytes allocated for LZMA state. */
32 
33 #ifdef HAVE_LZMA
34 /** Given <b>level</b> return the memory level. */
35 static int
37 {
38  switch (level) {
39  default:
40  case BEST_COMPRESSION:
41  case HIGH_COMPRESSION: return 6;
42  case MEDIUM_COMPRESSION: return 4;
43  case LOW_COMPRESSION: return 2;
44  }
45 }
46 
47 /** Convert a given <b>error</b> to a human readable error string. */
48 static const char *
49 lzma_error_str(lzma_ret error)
50 {
51  switch (error) {
52  case LZMA_OK:
53  return "Operation completed successfully";
54  case LZMA_STREAM_END:
55  return "End of stream";
56  case LZMA_NO_CHECK:
57  return "Input stream lacks integrity check";
58  case LZMA_UNSUPPORTED_CHECK:
59  return "Unable to calculate integrity check";
60  case LZMA_GET_CHECK:
61  return "Integrity check available";
62  case LZMA_MEM_ERROR:
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";
70  case LZMA_DATA_ERROR:
71  return "Corrupt input data";
72  case LZMA_BUF_ERROR:
73  return "Unable to progress";
74  case LZMA_PROG_ERROR:
75  return "Programming error";
76  default:
77  return "Unknown LZMA error";
78  }
79 }
80 #endif /* defined(HAVE_LZMA) */
81 
82 /** Return 1 if LZMA compression is supported; otherwise 0. */
83 int
85 {
86 #ifdef HAVE_LZMA
87  return 1;
88 #else
89  return 0;
90 #endif
91 }
92 
93 /** Return a string representation of the version of the currently running
94  * version of liblzma. Returns NULL if LZMA is unsupported. */
95 const char *
97 {
98 #ifdef HAVE_LZMA
99  return lzma_version_string();
100 #else
101  return NULL;
102 #endif
103 }
104 
105 /** Return a string representation of the version of liblzma used at
106  * compilation time. Returns NULL if LZMA is unsupported. */
107 const char *
109 {
110 #ifdef HAVE_LZMA
111  return LZMA_VERSION_STRING;
112 #else
113  return NULL;
114 #endif
115 }
116 
117 /** Internal LZMA state for incremental compression/decompression.
118  * The body of this struct is not exposed. */
120 #ifdef HAVE_LZMA
121  lzma_stream stream; /**< The LZMA stream. */
122 #endif
123 
124  int compress; /**< True if we are compressing; false if we are inflating */
125 
126  /** Number of bytes read so far. Used to detect compression bombs. */
127  size_t input_so_far;
128  /** Number of bytes written so far. Used to detect compression bombs. */
130 
131  /** Approximate number of bytes allocated for this object. */
132  size_t allocation;
133 };
134 
135 #ifdef HAVE_LZMA
136 /** Return an approximate number of bytes stored in memory to hold the LZMA
137  * encoder/decoder state. */
138 static size_t
139 tor_lzma_state_size_precalc(int compress, compression_level_t level)
140 {
141  uint64_t memory_usage;
142 
143  if (compress)
144  memory_usage = lzma_easy_encoder_memusage(memory_level(level));
145  else
146  memory_usage = lzma_easy_decoder_memusage(memory_level(level));
147 
148  if (memory_usage == UINT64_MAX) {
149  // LCOV_EXCL_START
150  log_warn(LD_GENERAL, "Unsupported compression level passed to LZMA %s",
151  compress ? "encoder" : "decoder");
152  goto err;
153  // LCOV_EXCL_STOP
154  }
155 
156  if (memory_usage + sizeof(tor_lzma_compress_state_t) > SIZE_MAX)
157  memory_usage = SIZE_MAX;
158  else
159  memory_usage += sizeof(tor_lzma_compress_state_t);
160 
161  return (size_t)memory_usage;
162 
163  // LCOV_EXCL_START
164  err:
165  return 0;
166  // LCOV_EXCL_STOP
167 }
168 #endif /* defined(HAVE_LZMA) */
169 
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
172  * decompression. */
175  compress_method_t method,
176  compression_level_t level)
177 {
178  tor_assert(method == LZMA_METHOD);
179 
180 #ifdef HAVE_LZMA
182  lzma_ret retval;
183  lzma_options_lzma stream_options;
184 
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);
191 
192  if (compress) {
193  lzma_lzma_preset(&stream_options, memory_level(level));
194 
195  retval = lzma_alone_encoder(&result->stream, &stream_options);
196 
197  if (retval != LZMA_OK) {
198  // LCOV_EXCL_START
199  log_warn(LD_GENERAL, "Error from LZMA encoder: %s (%u).",
200  lzma_error_str(retval), retval);
201  goto err;
202  // LCOV_EXCL_STOP
203  }
204  } else {
205  retval = lzma_alone_decoder(&result->stream, MEMORY_LIMIT);
206 
207  if (retval != LZMA_OK) {
208  // LCOV_EXCL_START
209  log_warn(LD_GENERAL, "Error from LZMA decoder: %s (%u).",
210  lzma_error_str(retval), retval);
211  goto err;
212  // LCOV_EXCL_STOP
213  }
214  }
215 
217  return result;
218 
219  /* LCOV_EXCL_START */
220  err:
221  tor_free(result);
222  return NULL;
223  /* LCOV_EXCL_STOP */
224 #else /* !defined(HAVE_LZMA) */
225  (void)compress;
226  (void)method;
227  (void)level;
228 
229  return NULL;
230 #endif /* defined(HAVE_LZMA) */
231 }
232 
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.
237  *
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.
243  */
246  char **out, size_t *out_len,
247  const char **in, size_t *in_len,
248  int finish)
249 {
250 #ifdef HAVE_LZMA
251  lzma_ret retval;
252  lzma_action action;
253 
254  tor_assert(state != NULL);
255  tor_assert(*in_len <= UINT_MAX);
256  tor_assert(*out_len <= UINT_MAX);
257 
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;
262 
263  action = finish ? LZMA_FINISH : LZMA_RUN;
264 
265  retval = lzma_code(&state->stream, action);
266 
267  state->input_so_far += state->stream.next_in - ((unsigned char *)*in);
268  state->output_so_far += state->stream.next_out - ((unsigned char *)*out);
269 
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;
274 
275  if (! state->compress &&
277  state->output_so_far)) {
278  log_warn(LD_DIR, "Possible compression bomb; abandoning stream.");
279  return TOR_COMPRESS_ERROR;
280  }
281 
282  switch (retval) {
283  case LZMA_OK:
284  if (state->stream.avail_out == 0 || finish)
285  return TOR_COMPRESS_BUFFER_FULL;
286 
287  return TOR_COMPRESS_OK;
288 
289  case LZMA_BUF_ERROR:
290  if (state->stream.avail_in == 0 && !finish)
291  return TOR_COMPRESS_OK;
292 
293  return TOR_COMPRESS_BUFFER_FULL;
294 
295  case LZMA_STREAM_END:
296  return TOR_COMPRESS_DONE;
297 
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.
300  case LZMA_NO_CHECK:
301  case LZMA_UNSUPPORTED_CHECK:
302  case LZMA_GET_CHECK:
303  case LZMA_MEM_ERROR:
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:
309  default:
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;
314  }
315 #else /* !defined(HAVE_LZMA) */
316  (void)state;
317  (void)out;
318  (void)out_len;
319  (void)in;
320  (void)in_len;
321  (void)finish;
322  return TOR_COMPRESS_ERROR;
323 #endif /* defined(HAVE_LZMA) */
324 }
325 
326 /** Deallocate <b>state</b>. */
327 void
329 {
330  if (state == NULL)
331  return;
332 
334 
335 #ifdef HAVE_LZMA
336  lzma_end(&state->stream);
337 #endif
338 
339  tor_free(state);
340 }
341 
342 /** Return the approximate number of bytes allocated for <b>state</b>. */
343 size_t
345 {
346  tor_assert(state != NULL);
347  return state->allocation;
348 }
349 
350 /** Return the approximate number of bytes allocated for all LZMA states. */
351 size_t
353 {
355 }
356 
357 /** Initialize the lzma module */
358 void
360 {
362 }
void atomic_counter_init(atomic_counter_t *counter)
void atomic_counter_sub(atomic_counter_t *counter, size_t sub)
void atomic_counter_add(atomic_counter_t *counter, size_t add)
size_t atomic_counter_get(atomic_counter_t *counter)
int tor_compress_is_compression_bomb(size_t size_in, size_t size_out)
Definition: compress.c:64
Headers for compress.c.
tor_compress_output_t
Definition: compress.h:68
compress_method_t
Definition: compress.h:21
compression_level_t
Definition: compress.h:35
int tor_lzma_method_supported(void)
Definition: compress_lzma.c:84
const char * tor_lzma_get_version_str(void)
Definition: compress_lzma.c:96
static atomic_counter_t total_lzma_allocation
Definition: compress_lzma.c:31
#define MEMORY_LIMIT
Definition: compress_lzma.c:28
const char * tor_lzma_get_header_version_str(void)
tor_compress_output_t tor_lzma_compress_process(tor_lzma_compress_state_t *state, char **out, size_t *out_len, const char **in, size_t *in_len, int finish)
tor_lzma_compress_state_t * tor_lzma_compress_new(int compress, compress_method_t method, compression_level_t level)
void tor_lzma_init(void)
size_t tor_lzma_compress_state_size(const tor_lzma_compress_state_t *state)
size_t tor_lzma_get_total_allocation(void)
void tor_lzma_compress_free_(tor_lzma_compress_state_t *state)
Header for compress_lzma.c.
static int memory_level(compression_level_t level)
Definition: compress_zlib.c:56
Headers for log.c.
#define LD_GENERAL
Definition: log.h:62
#define LD_DIR
Definition: log.h:88
Headers for util_malloc.c.
#define tor_free(p)
Definition: malloc.h:52
Header for threads.c.
Macros to manage assertions, fatal and non-fatal.
#define tor_assert(expr)
Definition: util_bug.h:102