LCOV - code coverage report
Current view: top level - lib/compress - compress.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 218 245 89.0 %
Date: 2021-11-24 03:28:48 Functions: 20 21 95.2 %

          Line data    Source code
       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.c
       8             :  * \brief Common compression API implementation.
       9             :  *
      10             :  * This file provides a unified interface to all the compression libraries Tor
      11             :  * knows how to use.
      12             :  **/
      13             : 
      14             : #include "orconfig.h"
      15             : 
      16             : #include <stdlib.h>
      17             : #include <stdio.h>
      18             : #include <string.h>
      19             : #include "lib/cc/torint.h"
      20             : 
      21             : #ifdef HAVE_NETINET_IN_H
      22             : #include <netinet/in.h>
      23             : #endif
      24             : 
      25             : #include "lib/log/log.h"
      26             : #include "lib/log/util_bug.h"
      27             : #include "lib/arch/bytes.h"
      28             : #include "lib/ctime/di_ops.h"
      29             : #include "lib/compress/compress.h"
      30             : #include "lib/compress/compress_lzma.h"
      31             : #include "lib/compress/compress_none.h"
      32             : #include "lib/compress/compress_sys.h"
      33             : #include "lib/compress/compress_zlib.h"
      34             : #include "lib/compress/compress_zstd.h"
      35             : #include "lib/intmath/cmp.h"
      36             : #include "lib/malloc/malloc.h"
      37             : #include "lib/subsys/subsys.h"
      38             : #include "lib/thread/threads.h"
      39             : 
      40             : /** Total number of bytes allocated for compression state overhead. */
      41             : static atomic_counter_t total_compress_allocation;
      42             : 
      43             : /** @{ */
      44             : /* These macros define the maximum allowable compression factor.  Anything of
      45             :  * size greater than CHECK_FOR_COMPRESSION_BOMB_AFTER is not allowed to
      46             :  * have an uncompression factor (uncompressed size:compressed size ratio) of
      47             :  * any greater than MAX_UNCOMPRESSION_FACTOR.
      48             :  *
      49             :  * Picking a value for MAX_UNCOMPRESSION_FACTOR is a trade-off: we want it to
      50             :  * be small to limit the attack multiplier, but we also want it to be large
      51             :  * enough so that no legitimate document --even ones we might invent in the
      52             :  * future -- ever compresses by a factor of greater than
      53             :  * MAX_UNCOMPRESSION_FACTOR. Within those parameters, there's a reasonably
      54             :  * large range of possible values. IMO, anything over 8 is probably safe; IMO
      55             :  * anything under 50 is probably sufficient.
      56             :  */
      57             : #define MAX_UNCOMPRESSION_FACTOR 25
      58             : #define CHECK_FOR_COMPRESSION_BOMB_AFTER (1024*64)
      59             : /** @} */
      60             : 
      61             : /** Return true if uncompressing an input of size <b>in_size</b> to an input of
      62             :  * size at least <b>size_out</b> looks like a compression bomb. */
      63         392 : MOCK_IMPL(int,
      64             : tor_compress_is_compression_bomb,(size_t size_in, size_t size_out))
      65             : {
      66         392 :   if (size_in == 0 || size_out < CHECK_FOR_COMPRESSION_BOMB_AFTER)
      67             :     return 0;
      68             : 
      69          19 :   return (size_out / size_in > MAX_UNCOMPRESSION_FACTOR);
      70             : }
      71             : 
      72             : /** Guess the size that <b>in_len</b> will be after compression or
      73             :  * decompression. */
      74             : static size_t
      75         325 : guess_compress_size(int compress, compress_method_t method,
      76             :                     compression_level_t compression_level,
      77             :                     size_t in_len)
      78             : {
      79             :   // ignore these for now.
      80         325 :   (void)compression_level;
      81         325 :   if (method == NO_METHOD) {
      82             :     /* Guess that we'll need an extra byte, to avoid a needless realloc
      83             :      * for nul-termination */
      84          24 :     return (in_len < SIZE_MAX) ? in_len + 1 : in_len;
      85             :   }
      86             : 
      87             :   /* Always guess a factor of 2. */
      88         301 :   if (compress) {
      89         170 :     in_len /= 2;
      90             :   } else {
      91         131 :     if (in_len < SIZE_T_CEILING/2)
      92         131 :       in_len *= 2;
      93             :   }
      94         301 :   return MAX(in_len, 1024);
      95             : }
      96             : 
      97             : /** Internal function to implement tor_compress/tor_uncompress, depending on
      98             :  * whether <b>compress</b> is set.  All arguments are as for tor_compress or
      99             :  * tor_uncompress. */
     100             : static int
     101         325 : tor_compress_impl(int compress,
     102             :                   char **out, size_t *out_len,
     103             :                   const char *in, size_t in_len,
     104             :                   compress_method_t method,
     105             :                   compression_level_t compression_level,
     106             :                   int complete_only,
     107             :                   int protocol_warn_level)
     108             : {
     109         325 :   tor_compress_state_t *stream;
     110         325 :   int rv;
     111             : 
     112         325 :   stream = tor_compress_new(compress, method, compression_level);
     113             : 
     114         325 :   if (stream == NULL) {
     115           0 :     log_warn(LD_GENERAL, "NULL stream while %scompressing",
     116             :              compress?"":"de");
     117           0 :     log_debug(LD_GENERAL, "method: %d level: %d at len: %lu",
     118             :               method, compression_level, (unsigned long)in_len);
     119           0 :     return -1;
     120             :   }
     121             : 
     122         325 :   size_t in_len_orig = in_len;
     123         325 :   size_t out_remaining, out_alloc;
     124         325 :   char *outptr;
     125             : 
     126         325 :   out_remaining = out_alloc =
     127         325 :     guess_compress_size(compress, method, compression_level, in_len);
     128         325 :   *out = outptr = tor_malloc(out_remaining);
     129             : 
     130         325 :   const int finish = complete_only || compress;
     131             : 
     132         432 :   while (1) {
     133         432 :     switch (tor_compress_process(stream,
     134             :                                  &outptr, &out_remaining,
     135             :                                  &in, &in_len, finish)) {
     136         307 :       case TOR_COMPRESS_DONE:
     137         307 :         if (in_len == 0 || compress) {
     138         299 :           goto done;
     139             :         } else {
     140             :           // More data is present, and we're decompressing.  So we may need to
     141             :           // reinitialize the stream if we are handling multiple concatenated
     142             :           // inputs.
     143           8 :           tor_compress_free(stream);
     144           8 :           stream = tor_compress_new(compress, method, compression_level);
     145           8 :           if (stream == NULL) {
     146           0 :             log_warn(LD_GENERAL, "NULL stream while %scompressing",
     147             :                      compress?"":"de");
     148           0 :             goto err;
     149             :           }
     150             :         }
     151             :         break;
     152          25 :       case TOR_COMPRESS_OK:
     153          25 :         if (compress || complete_only) {
     154           4 :           log_fn(protocol_warn_level, LD_PROTOCOL,
     155             :                  "Unexpected %s while %scompressing",
     156             :                  complete_only?"end of input":"result",
     157             :                  compress?"":"de");
     158           2 :           log_debug(LD_GENERAL, "method: %d level: %d at len: %lu",
     159             :                     method, compression_level, (unsigned long)in_len);
     160           2 :           goto err;
     161             :         } else {
     162          23 :           if (in_len == 0) {
     163           9 :             goto done;
     164             :           }
     165             :         }
     166             :         break;
     167          88 :       case TOR_COMPRESS_BUFFER_FULL: {
     168          88 :         if (!compress && outptr < *out+out_alloc) {
     169             :           // A buffer error in this case means that we have a problem
     170             :           // with our input.
     171           3 :           log_fn(protocol_warn_level, LD_PROTOCOL,
     172             :                  "Possible truncated or corrupt compressed data");
     173           3 :           goto err;
     174             :         }
     175          85 :         if (out_alloc >= SIZE_T_CEILING / 2) {
     176           0 :           log_warn(LD_GENERAL, "While %scompressing data: ran out of space.",
     177             :                    compress?"":"un");
     178           0 :           goto err;
     179             :         }
     180         118 :         if (!compress &&
     181          33 :             tor_compress_is_compression_bomb(in_len_orig, out_alloc)) {
     182             :           // This should already have been caught down in the backend logic.
     183             :           // LCOV_EXCL_START
     184             :           tor_assert_nonfatal_unreached();
     185             :           goto err;
     186             :           // LCOV_EXCL_STOP
     187             :         }
     188          85 :         const size_t offset = outptr - *out;
     189          85 :         out_alloc *= 2;
     190          85 :         *out = tor_realloc(*out, out_alloc);
     191          85 :         outptr = *out + offset;
     192          85 :         out_remaining = out_alloc - offset;
     193          85 :         break;
     194             :       }
     195          12 :       case TOR_COMPRESS_ERROR:
     196          24 :         log_fn(protocol_warn_level, LD_GENERAL,
     197             :                "Error while %scompressing data: bad input?",
     198             :                compress?"":"un");
     199          12 :         goto err; // bad data.
     200             : 
     201             :         // LCOV_EXCL_START
     202             :       default:
     203             :         tor_assert_nonfatal_unreached();
     204             :         goto err;
     205             :         // LCOV_EXCL_STOP
     206             :     }
     207             :   }
     208         308 :  done:
     209         308 :   *out_len = outptr - *out;
     210         308 :   if (compress && tor_compress_is_compression_bomb(*out_len, in_len_orig)) {
     211           6 :     log_warn(LD_BUG, "We compressed something and got an insanely high "
     212             :              "compression factor; other Tors would think this was a "
     213             :              "compression bomb.");
     214           6 :     goto err;
     215             :   }
     216         302 :   if (!compress) {
     217             :     // NUL-terminate our output.
     218         134 :     if (out_alloc == *out_len)
     219           0 :       *out = tor_realloc(*out, out_alloc + 1);
     220         134 :     (*out)[*out_len] = '\0';
     221             :   }
     222         302 :   rv = 0;
     223         302 :   goto out;
     224             : 
     225          23 :  err:
     226          23 :   tor_free(*out);
     227          23 :   *out_len = 0;
     228          23 :   rv = -1;
     229          23 :   goto out;
     230             : 
     231         325 :  out:
     232         325 :   tor_compress_free(stream);
     233         325 :   return rv;
     234             : }
     235             : 
     236             : /** Given <b>in_len</b> bytes at <b>in</b>, compress them into a newly
     237             :  * allocated buffer, using the method described in <b>method</b>.  Store the
     238             :  * compressed string in *<b>out</b>, and its length in *<b>out_len</b>.
     239             :  * Return 0 on success, -1 on failure.
     240             :  */
     241             : int
     242         174 : tor_compress(char **out, size_t *out_len,
     243             :              const char *in, size_t in_len,
     244             :              compress_method_t method)
     245             : {
     246         174 :   return tor_compress_impl(1, out, out_len, in, in_len, method,
     247             :                            BEST_COMPRESSION,
     248             :                            1, LOG_WARN);
     249             : }
     250             : 
     251             : /** Given zero or more compressed strings of total length <b>in_len</b> bytes
     252             :  * at <b>in</b>, uncompress them into a newly allocated buffer, using the
     253             :  * method described in <b>method</b>.  Store the uncompressed string in
     254             :  * *<b>out</b>, and its length in *<b>out_len</b>.  Return 0 on success, -1 on
     255             :  * failure.
     256             :  *
     257             :  * If any bytes are written to <b>out</b>, an extra byte NUL is always
     258             :  * written at the end, but not counted in <b>out_len</b>.  This is a
     259             :  * safety feature to ensure that the output can be treated as a
     260             :  * NUL-terminated string -- though of course, callers should check
     261             :  * out_len anyway.
     262             :  *
     263             :  * If <b>complete_only</b> is true, we consider a truncated input as a
     264             :  * failure; otherwise we decompress as much as we can.  Warn about truncated
     265             :  * or corrupt inputs at <b>protocol_warn_level</b>.
     266             :  */
     267             : int
     268         151 : tor_uncompress(char **out, size_t *out_len,
     269             :                const char *in, size_t in_len,
     270             :                compress_method_t method,
     271             :                int complete_only,
     272             :                int protocol_warn_level)
     273             : {
     274         151 :   return tor_compress_impl(0, out, out_len, in, in_len, method,
     275             :                            BEST_COMPRESSION,
     276             :                            complete_only, protocol_warn_level);
     277             : }
     278             : 
     279             : /** Try to tell whether the <b>in_len</b>-byte string in <b>in</b> is likely
     280             :  * to be compressed or not.  If it is, return the likeliest compression method.
     281             :  * Otherwise, return UNKNOWN_METHOD.
     282             :  */
     283             : compress_method_t
     284          13 : detect_compression_method(const char *in, size_t in_len)
     285             : {
     286          13 :   if (in_len > 2 && fast_memeq(in, "\x1f\x8b", 2)) {
     287             :     return GZIP_METHOD;
     288          12 :   } else if (in_len > 2 && (in[0] & 0x0f) == 8 &&
     289           4 :              (tor_ntohs(get_uint16(in)) % 31) == 0) {
     290             :     return ZLIB_METHOD;
     291          10 :   } else if (in_len > 2 &&
     292          10 :              fast_memeq(in, "\x5d\x00\x00", 3)) {
     293             :     return LZMA_METHOD;
     294           9 :   } else if (in_len > 3 &&
     295           9 :              fast_memeq(in, "\x28\xb5\x2f\xfd", 4)) {
     296             :     return ZSTD_METHOD;
     297             :   } else {
     298           7 :     return UNKNOWN_METHOD;
     299             :   }
     300             : }
     301             : 
     302             : /** Return 1 if a given <b>method</b> is supported; otherwise 0. */
     303             : int
     304         760 : tor_compress_supports_method(compress_method_t method)
     305             : {
     306         760 :   switch (method) {
     307         255 :     case GZIP_METHOD:
     308             :     case ZLIB_METHOD:
     309         255 :       return tor_zlib_method_supported();
     310         245 :     case LZMA_METHOD:
     311         245 :       return tor_lzma_method_supported();
     312         248 :     case ZSTD_METHOD:
     313         248 :       return tor_zstd_method_supported();
     314             :     case NO_METHOD:
     315             :       return 1;
     316           4 :     case UNKNOWN_METHOD:
     317             :     default:
     318           4 :       return 0;
     319             :   }
     320             : }
     321             : 
     322             : /**
     323             :  * Return a bitmask of the supported compression types, where 1&lt;&lt;m is
     324             :  * set in the bitmask if and only if compression with method <b>m</b> is
     325             :  * supported.
     326             :  */
     327             : unsigned
     328          62 : tor_compress_get_supported_method_bitmask(void)
     329             : {
     330          62 :   static unsigned supported = 0;
     331          62 :   if (supported == 0) {
     332             :     compress_method_t m;
     333          28 :     for (m = NO_METHOD; m <= UNKNOWN_METHOD; ++m) {
     334          24 :       if (tor_compress_supports_method(m)) {
     335          20 :         supported |= (1u << m);
     336             :       }
     337             :     }
     338             :   }
     339          62 :   return supported;
     340             : }
     341             : 
     342             : /** Table of compression method names.  These should have an "x-" prefix,
     343             :  * if they are not listed in the IANA content coding registry. */
     344             : static const struct {
     345             :   const char *name;
     346             :   compress_method_t method;
     347             : } compression_method_names[] = {
     348             :   { "gzip", GZIP_METHOD },
     349             :   { "deflate", ZLIB_METHOD },
     350             :   // We call this "x-tor-lzma" rather than "x-lzma", because we impose a
     351             :   // lower maximum memory usage on the decoding side.
     352             :   { "x-tor-lzma", LZMA_METHOD },
     353             :   { "x-zstd" , ZSTD_METHOD },
     354             :   { "identity", NO_METHOD },
     355             : 
     356             :   /* Later entries in this table are not canonical; these are recognized but
     357             :    * not emitted. */
     358             :   { "x-gzip", GZIP_METHOD },
     359             : };
     360             : 
     361             : /** Return the canonical string representation of the compression method
     362             :  * <b>method</b>, or NULL if the method isn't recognized. */
     363             : const char *
     364         321 : compression_method_get_name(compress_method_t method)
     365             : {
     366         321 :   unsigned i;
     367         973 :   for (i = 0; i < ARRAY_LENGTH(compression_method_names); ++i) {
     368         973 :     if (method == compression_method_names[i].method)
     369         321 :       return compression_method_names[i].name;
     370             :   }
     371             :   return NULL;
     372             : }
     373             : 
     374             : /** Table of compression human readable method names. */
     375             : static const struct {
     376             :   compress_method_t method;
     377             :   const char *name;
     378             : } compression_method_human_names[] = {
     379             :   { NO_METHOD, "uncompressed" },
     380             :   { GZIP_METHOD, "gzipped" },
     381             :   { ZLIB_METHOD, "deflated" },
     382             :   { LZMA_METHOD, "LZMA compressed" },
     383             :   { ZSTD_METHOD, "Zstandard compressed" },
     384             :   { UNKNOWN_METHOD, "unknown encoding" },
     385             : };
     386             : 
     387             : /** Return a human readable string representation of the compression method
     388             :  * <b>method</b>, or NULL if the method isn't recognized. */
     389             : const char *
     390           0 : compression_method_get_human_name(compress_method_t method)
     391             : {
     392           0 :   unsigned i;
     393           0 :   for (i = 0; i < ARRAY_LENGTH(compression_method_human_names); ++i) {
     394           0 :     if (method == compression_method_human_names[i].method)
     395           0 :       return compression_method_human_names[i].name;
     396             :   }
     397             :   return NULL;
     398             : }
     399             : 
     400             : /** Return the compression method represented by the string <b>name</b>, or
     401             :  * UNKNOWN_METHOD if the string isn't recognized. */
     402             : compress_method_t
     403         109 : compression_method_get_by_name(const char *name)
     404             : {
     405         109 :   unsigned i;
     406         320 :   for (i = 0; i < ARRAY_LENGTH(compression_method_names); ++i) {
     407         311 :     if (!strcmp(compression_method_names[i].name, name))
     408         100 :       return compression_method_names[i].method;
     409             :   }
     410             :   return UNKNOWN_METHOD;
     411             : }
     412             : 
     413             : /** Return a string representation of the version of the library providing the
     414             :  * compression method given in <b>method</b>. Returns NULL if <b>method</b> is
     415             :  * unknown or unsupported. */
     416             : const char *
     417         710 : tor_compress_version_str(compress_method_t method)
     418             : {
     419         710 :   switch (method) {
     420         237 :     case GZIP_METHOD:
     421             :     case ZLIB_METHOD:
     422         237 :       return tor_zlib_get_version_str();
     423         236 :     case LZMA_METHOD:
     424         236 :       return tor_lzma_get_version_str();
     425         237 :     case ZSTD_METHOD:
     426         237 :       return tor_zstd_get_version_str();
     427             :     case NO_METHOD:
     428             :     case UNKNOWN_METHOD:
     429             :     default:
     430             :       return NULL;
     431             :   }
     432             : }
     433             : 
     434             : /** Return a string representation of the version of the library, found at
     435             :  * compile time, providing the compression method given in <b>method</b>.
     436             :  * Returns NULL if <b>method</b> is unknown or unsupported. */
     437             : const char *
     438           5 : tor_compress_header_version_str(compress_method_t method)
     439             : {
     440           5 :   switch (method) {
     441           2 :     case GZIP_METHOD:
     442             :     case ZLIB_METHOD:
     443           2 :       return tor_zlib_get_header_version_str();
     444           1 :     case LZMA_METHOD:
     445           1 :       return tor_lzma_get_header_version_str();
     446           2 :     case ZSTD_METHOD:
     447           2 :       return tor_zstd_get_header_version_str();
     448             :     case NO_METHOD:
     449             :     case UNKNOWN_METHOD:
     450             :     default:
     451             :       return NULL;
     452             :   }
     453             : }
     454             : 
     455             : /** Return the approximate number of bytes allocated for all
     456             :  * supported compression schemas. */
     457             : size_t
     458          19 : tor_compress_get_total_allocation(void)
     459             : {
     460          19 :   return atomic_counter_get(&total_compress_allocation) +
     461          19 :          tor_zlib_get_total_allocation() +
     462          19 :          tor_lzma_get_total_allocation() +
     463          19 :          tor_zstd_get_total_allocation();
     464             : }
     465             : 
     466             : /** Internal state for an incremental compression/decompression.  The body of
     467             :  * this struct is not exposed. */
     468             : struct tor_compress_state_t {
     469             :   compress_method_t method; /**< The compression method. */
     470             : 
     471             :   union {
     472             :     tor_zlib_compress_state_t *zlib_state;
     473             :     tor_lzma_compress_state_t *lzma_state;
     474             :     tor_zstd_compress_state_t *zstd_state;
     475             :   } u; /**< Compression backend state. */
     476             : };
     477             : 
     478             : /** Construct and return a tor_compress_state_t object using <b>method</b>.  If
     479             :  * <b>compress</b>, it's for compression; otherwise it's for decompression. */
     480             : tor_compress_state_t *
     481         420 : tor_compress_new(int compress, compress_method_t method,
     482             :                  compression_level_t compression_level)
     483             : {
     484         420 :   tor_compress_state_t *state;
     485             : 
     486         420 :   state = tor_malloc_zero(sizeof(tor_compress_state_t));
     487         420 :   state->method = method;
     488             : 
     489         420 :   switch (method) {
     490         187 :     case GZIP_METHOD:
     491             :     case ZLIB_METHOD: {
     492         187 :       tor_zlib_compress_state_t *zlib_state =
     493         187 :         tor_zlib_compress_new(compress, method, compression_level);
     494             : 
     495         187 :       if (zlib_state == NULL)
     496           0 :         goto err;
     497             : 
     498         187 :       state->u.zlib_state = zlib_state;
     499         187 :       break;
     500             :     }
     501          88 :     case LZMA_METHOD: {
     502          88 :       tor_lzma_compress_state_t *lzma_state =
     503          88 :         tor_lzma_compress_new(compress, method, compression_level);
     504             : 
     505          88 :       if (lzma_state == NULL)
     506           0 :         goto err;
     507             : 
     508          88 :       state->u.lzma_state = lzma_state;
     509          88 :       break;
     510             :     }
     511         105 :     case ZSTD_METHOD: {
     512         105 :       tor_zstd_compress_state_t *zstd_state =
     513         105 :         tor_zstd_compress_new(compress, method, compression_level);
     514             : 
     515         105 :       if (zstd_state == NULL)
     516           0 :         goto err;
     517             : 
     518         105 :       state->u.zstd_state = zstd_state;
     519         105 :       break;
     520             :     }
     521             :     case NO_METHOD: {
     522             :       break;
     523             :     }
     524           0 :     case UNKNOWN_METHOD:
     525           0 :       goto err;
     526             :   }
     527             : 
     528         420 :   atomic_counter_add(&total_compress_allocation,
     529             :                      sizeof(tor_compress_state_t));
     530         420 :   return state;
     531             : 
     532           0 :  err:
     533           0 :   tor_free(state);
     534           0 :   return NULL;
     535             : }
     536             : 
     537             : /** Compress/decompress some bytes using <b>state</b>.  Read up to
     538             :  * *<b>in_len</b> bytes from *<b>in</b>, and write up to *<b>out_len</b> bytes
     539             :  * to *<b>out</b>, adjusting the values as we go.  If <b>finish</b> is true,
     540             :  * we've reached the end of the input.
     541             :  *
     542             :  * Return TOR_COMPRESS_DONE if we've finished the entire
     543             :  * compression/decompression.
     544             :  * Return TOR_COMPRESS_OK if we're processed everything from the input.
     545             :  * Return TOR_COMPRESS_BUFFER_FULL if we're out of space on <b>out</b>.
     546             :  * Return TOR_COMPRESS_ERROR if the stream is corrupt.
     547             :  */
     548             : tor_compress_output_t
     549         776 : tor_compress_process(tor_compress_state_t *state,
     550             :                      char **out, size_t *out_len,
     551             :                      const char **in, size_t *in_len,
     552             :                      int finish)
     553             : {
     554         776 :   tor_assert(state != NULL);
     555         776 :   const size_t in_len_orig = *in_len;
     556         776 :   const size_t out_len_orig = *out_len;
     557         776 :   tor_compress_output_t rv;
     558             : 
     559         776 :   if (*out_len == 0 && (*in_len > 0 || finish)) {
     560             :     // If we still have input data, but no space for output data, we might as
     561             :     // well return early and let the caller do the reallocation of the out
     562             :     // variable.
     563             :     return TOR_COMPRESS_BUFFER_FULL;
     564             :   }
     565             : 
     566         764 :   switch (state->method) {
     567         352 :     case GZIP_METHOD:
     568             :     case ZLIB_METHOD:
     569         352 :       rv = tor_zlib_compress_process(state->u.zlib_state,
     570             :                                      out, out_len, in, in_len,
     571             :                                      finish);
     572         352 :       break;
     573         153 :     case LZMA_METHOD:
     574         153 :       rv = tor_lzma_compress_process(state->u.lzma_state,
     575             :                                      out, out_len, in, in_len,
     576             :                                      finish);
     577         153 :       break;
     578         171 :     case ZSTD_METHOD:
     579         171 :       rv = tor_zstd_compress_process(state->u.zstd_state,
     580             :                                      out, out_len, in, in_len,
     581             :                                      finish);
     582         171 :       break;
     583          88 :     case NO_METHOD:
     584          88 :       rv = tor_cnone_compress_process(out, out_len, in, in_len,
     585             :                                       finish);
     586          88 :       break;
     587           0 :     default:
     588             :     case UNKNOWN_METHOD:
     589           0 :       goto err;
     590             :   }
     591         764 :   if (BUG((rv == TOR_COMPRESS_OK) &&
     592             :           *in_len == in_len_orig &&
     593             :           *out_len == out_len_orig)) {
     594           0 :     log_warn(LD_GENERAL,
     595             :              "More info on the bug: method == %s, finish == %d, "
     596             :              " *in_len == in_len_orig == %lu, "
     597             :              "*out_len == out_len_orig == %lu",
     598             :              compression_method_get_human_name(state->method), finish,
     599             :              (unsigned long)in_len_orig, (unsigned long)out_len_orig);
     600           0 :     return TOR_COMPRESS_ERROR;
     601             :   }
     602             : 
     603             :   return rv;
     604           0 :  err:
     605           0 :   return TOR_COMPRESS_ERROR;
     606             : }
     607             : 
     608             : /** Deallocate <b>state</b>. */
     609             : void
     610         493 : tor_compress_free_(tor_compress_state_t *state)
     611             : {
     612         493 :   if (state == NULL)
     613             :     return;
     614             : 
     615         420 :   switch (state->method) {
     616         187 :     case GZIP_METHOD:
     617             :     case ZLIB_METHOD:
     618         187 :       tor_zlib_compress_free(state->u.zlib_state);
     619         187 :       break;
     620          88 :     case LZMA_METHOD:
     621          88 :       tor_lzma_compress_free(state->u.lzma_state);
     622          88 :       break;
     623         105 :     case ZSTD_METHOD:
     624         105 :       tor_zstd_compress_free(state->u.zstd_state);
     625         105 :       break;
     626             :     case NO_METHOD:
     627             :       break;
     628             :     case UNKNOWN_METHOD:
     629             :       break;
     630             :   }
     631             : 
     632         420 :   atomic_counter_sub(&total_compress_allocation,
     633             :                      sizeof(tor_compress_state_t));
     634         420 :   tor_free(state);
     635             : }
     636             : 
     637             : /** Return the approximate number of bytes allocated for <b>state</b>. */
     638             : size_t
     639          24 : tor_compress_state_size(const tor_compress_state_t *state)
     640             : {
     641          24 :   tor_assert(state != NULL);
     642             : 
     643          24 :   size_t size = sizeof(tor_compress_state_t);
     644             : 
     645          24 :   switch (state->method) {
     646           8 :     case GZIP_METHOD:
     647             :     case ZLIB_METHOD:
     648           8 :       size += tor_zlib_compress_state_size(state->u.zlib_state);
     649           8 :       break;
     650           4 :     case LZMA_METHOD:
     651           4 :       size += tor_lzma_compress_state_size(state->u.lzma_state);
     652           4 :       break;
     653           8 :     case ZSTD_METHOD:
     654           8 :       size += tor_zstd_compress_state_size(state->u.zstd_state);
     655           8 :       break;
     656             :     case NO_METHOD:
     657             :     case UNKNOWN_METHOD:
     658             :       break;
     659             :   }
     660             : 
     661          24 :   return size;
     662             : }
     663             : 
     664             : /** Initialize all compression modules. */
     665             : int
     666       10862 : tor_compress_init(void)
     667             : {
     668       10862 :   atomic_counter_init(&total_compress_allocation);
     669             : 
     670       10862 :   tor_zlib_init();
     671       10862 :   tor_lzma_init();
     672       10862 :   tor_zstd_init();
     673             : 
     674       10862 :   return 0;
     675             : }
     676             : 
     677             : /** Warn if we had any problems while setting up our compression libraries.
     678             :  *
     679             :  * (This isn't part of tor_compress_init, since the logs aren't set up yet.)
     680             :  */
     681             : void
     682         235 : tor_compress_log_init_warnings(void)
     683             : {
     684             :   // XXXX can we move this into tor_compress_init() after all?  log.c queues
     685             :   // XXXX log messages at startup.
     686         235 :   tor_zstd_warn_if_version_mismatched();
     687         235 : }
     688             : 
     689             : static int
     690        5553 : subsys_compress_initialize(void)
     691             : {
     692        5553 :   return tor_compress_init();
     693             : }
     694             : 
     695             : const subsys_fns_t sys_compress = {
     696             :   .name = "compress",
     697             :   SUBSYS_DECLARE_LOCATION(),
     698             :   .supported = true,
     699             :   .level = -55,
     700             :   .initialize = subsys_compress_initialize,
     701             : };

Generated by: LCOV version 1.14