tor  0.4.2.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-2019, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
5 
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 
28 #define MEMORY_LIMIT (16 * 1024 * 1024)
29 
32 
33 #ifdef HAVE_LZMA
34 
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 
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 
83 int
85 {
86 #ifdef HAVE_LZMA
87  return 1;
88 #else
89  return 0;
90 #endif
91 }
92 
95 const char *
97 {
98 #ifdef HAVE_LZMA
99  return lzma_version_string();
100 #else
101  return NULL;
102 #endif
103 }
104 
107 const char *
109 {
110 #ifdef HAVE_LZMA
111  return LZMA_VERSION_STRING;
112 #else
113  return NULL;
114 #endif
115 }
116 
120 #ifdef HAVE_LZMA
121  lzma_stream stream;
122 #endif
123 
124  int compress;
127  size_t input_so_far;
130 
132  size_t allocation;
133 };
134 
135 #ifdef HAVE_LZMA
136 
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 
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 
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 &&
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;
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 
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 
343 size_t
345 {
346  tor_assert(state != NULL);
347  return state->allocation;
348 }
349 
351 size_t
353 {
355 }
356 
358 void
360 {
362 }
size_t atomic_counter_get(atomic_counter_t *counter)
static int memory_level(compression_level_t level)
Definition: compress_zlib.c:56
void tor_lzma_init(void)
#define LD_GENERAL
Definition: log.h:59
Headers for compress.c.
void atomic_counter_init(atomic_counter_t *counter)
size_t tor_lzma_compress_state_size(const tor_lzma_compress_state_t *state)
static atomic_counter_t total_lzma_allocation
Definition: compress_lzma.c:31
#define tor_free(p)
Definition: malloc.h:52
Headers for util_malloc.c.
Header for compress_lzma.c.
#define MEMORY_LIMIT
Definition: compress_lzma.c:28
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)
Header for threads.c.
tor_assert(buffer)
void atomic_counter_sub(atomic_counter_t *counter, size_t sub)
void atomic_counter_add(atomic_counter_t *counter, size_t add)
void tor_lzma_compress_free_(tor_lzma_compress_state_t *state)
struct tor_lzma_compress_state_t tor_lzma_compress_state_t
Definition: compress_lzma.h:21
#define LD_DIR
Definition: log.h:85
tor_lzma_compress_state_t * tor_lzma_compress_new(int compress, compress_method_t method, compression_level_t level)
const char * tor_lzma_get_header_version_str(void)
size_t tor_lzma_get_total_allocation(void)
compression_level_t
Definition: compress.h:35
compress_method_t
Definition: compress.h:21
const char * tor_lzma_get_version_str(void)
Definition: compress_lzma.c:96
Headers for log.c.
int tor_lzma_method_supported(void)
Definition: compress_lzma.c:84
tor_compress_output_t
Definition: compress.h:68
Macros to manage assertions, fatal and non-fatal.