compress_zlib.c (9004B)
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_zlib.c 8 * \brief Compression backend for gzip and zlib. 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/log/log.h" 17 #include "lib/log/util_bug.h" 18 #include "lib/compress/compress.h" 19 #include "lib/compress/compress_zlib.h" 20 #include "lib/thread/threads.h" 21 22 /* zlib 1.2.4 and 1.2.5 do some "clever" things with macros. Instead of 23 saying "(defined(FOO) ? FOO : 0)" they like to say "FOO-0", on the theory 24 that nobody will care if the compile outputs a no-such-identifier warning. 25 26 Sorry, but we like -Werror over here, so I guess we need to define these. 27 I hope that zlib 1.2.6 doesn't break these too. 28 */ 29 #ifndef _LARGEFILE64_SOURCE 30 #define _LARGEFILE64_SOURCE 0 31 #endif 32 #ifndef _LFS64_LARGEFILE 33 #define _LFS64_LARGEFILE 0 34 #endif 35 #ifndef _FILE_OFFSET_BITS 36 #define _FILE_OFFSET_BITS 0 37 #endif 38 #ifndef off64_t 39 #define off64_t int64_t 40 #endif 41 42 #include <zlib.h> 43 44 #if defined ZLIB_VERNUM && ZLIB_VERNUM < 0x1200 45 #error "We require zlib version 1.2 or later." 46 #endif 47 48 static size_t tor_zlib_state_size_precalc(int inflate, 49 int windowbits, int memlevel); 50 51 /** Total number of bytes allocated for zlib state */ 52 static atomic_counter_t total_zlib_allocation; 53 54 /** Given <b>level</b> return the memory level. */ 55 static int 56 memory_level(compression_level_t level) 57 { 58 switch (level) { 59 default: 60 case BEST_COMPRESSION: return 9; 61 case HIGH_COMPRESSION: return 8; 62 case MEDIUM_COMPRESSION: return 7; 63 case LOW_COMPRESSION: return 6; 64 } 65 } 66 67 /** Return the 'bits' value to tell zlib to use <b>method</b>.*/ 68 static inline int 69 method_bits(compress_method_t method, compression_level_t level) 70 { 71 /* Bits+16 means "use gzip" in zlib >= 1.2 */ 72 const int flag = method == GZIP_METHOD ? 16 : 0; 73 switch (level) { 74 default: 75 case BEST_COMPRESSION: 76 case HIGH_COMPRESSION: return flag + 15; 77 case MEDIUM_COMPRESSION: return flag + 13; 78 case LOW_COMPRESSION: return flag + 11; 79 } 80 } 81 82 /** Return 1 if zlib/gzip compression is supported; otherwise 0. */ 83 int 84 tor_zlib_method_supported(void) 85 { 86 /* We currently always support zlib/gzip, but we keep this function around in 87 * case we some day decide to deprecate zlib/gzip support. 88 */ 89 return 1; 90 } 91 92 /** Return a string representation of the version of the currently running 93 * version of zlib. */ 94 const char * 95 tor_zlib_get_version_str(void) 96 { 97 return zlibVersion(); 98 } 99 100 /** Return a string representation of the version of the version of zlib 101 * used at compilation. */ 102 const char * 103 tor_zlib_get_header_version_str(void) 104 { 105 return ZLIB_VERSION; 106 } 107 108 /** Internal zlib state for an incremental compression/decompression. 109 * The body of this struct is not exposed. */ 110 struct tor_zlib_compress_state_t { 111 struct z_stream_s stream; /**< The zlib stream */ 112 int compress; /**< True if we are compressing; false if we are inflating */ 113 114 /** Number of bytes read so far. Used to detect zlib bombs. */ 115 size_t input_so_far; 116 /** Number of bytes written so far. Used to detect zlib bombs. */ 117 size_t output_so_far; 118 119 /** Approximate number of bytes allocated for this object. */ 120 size_t allocation; 121 }; 122 123 /** Return an approximate number of bytes used in RAM to hold a state with 124 * window bits <b>windowBits</b> and compression level 'memlevel' */ 125 static size_t 126 tor_zlib_state_size_precalc(int inflate_, int windowbits, int memlevel) 127 { 128 windowbits &= 15; 129 130 #define A_FEW_KILOBYTES 2048 131 132 if (inflate_) { 133 /* From zconf.h: 134 135 "The memory requirements for inflate are (in bytes) 1 << windowBits 136 that is, 32K for windowBits=15 (default value) plus a few kilobytes 137 for small objects." 138 */ 139 return sizeof(tor_zlib_compress_state_t) + sizeof(struct z_stream_s) + 140 (1 << 15) + A_FEW_KILOBYTES; 141 } else { 142 /* Also from zconf.h: 143 144 "The memory requirements for deflate are (in bytes): 145 (1 << (windowBits+2)) + (1 << (memLevel+9)) 146 ... plus a few kilobytes for small objects." 147 */ 148 return sizeof(tor_zlib_compress_state_t) + sizeof(struct z_stream_s) + 149 (1 << (windowbits + 2)) + (1 << (memlevel + 9)) + A_FEW_KILOBYTES; 150 } 151 #undef A_FEW_KILOBYTES 152 } 153 154 /** Construct and return a tor_zlib_compress_state_t object using 155 * <b>method</b>. If <b>compress</b>, it's for compression; otherwise it's for 156 * decompression. */ 157 tor_zlib_compress_state_t * 158 tor_zlib_compress_new(int compress_, 159 compress_method_t method, 160 compression_level_t compression_level) 161 { 162 tor_zlib_compress_state_t *out; 163 int bits, memlevel; 164 165 if (! compress_) { 166 /* use this setting for decompression, since we might have the 167 * max number of window bits */ 168 compression_level = BEST_COMPRESSION; 169 } 170 171 out = tor_malloc_zero(sizeof(tor_zlib_compress_state_t)); 172 out->stream.zalloc = Z_NULL; 173 out->stream.zfree = Z_NULL; 174 out->stream.opaque = NULL; 175 out->compress = compress_; 176 bits = method_bits(method, compression_level); 177 memlevel = memory_level(compression_level); 178 if (compress_) { 179 if (deflateInit2(&out->stream, Z_BEST_COMPRESSION, Z_DEFLATED, 180 bits, memlevel, 181 Z_DEFAULT_STRATEGY) != Z_OK) 182 goto err; // LCOV_EXCL_LINE 183 } else { 184 if (inflateInit2(&out->stream, bits) != Z_OK) 185 goto err; // LCOV_EXCL_LINE 186 } 187 out->allocation = tor_zlib_state_size_precalc(!compress_, bits, memlevel); 188 189 atomic_counter_add(&total_zlib_allocation, out->allocation); 190 191 return out; 192 193 err: 194 tor_free(out); 195 return NULL; 196 } 197 198 /** Compress/decompress some bytes using <b>state</b>. Read up to 199 * *<b>in_len</b> bytes from *<b>in</b>, and write up to *<b>out_len</b> bytes 200 * to *<b>out</b>, adjusting the values as we go. If <b>finish</b> is true, 201 * we've reached the end of the input. 202 * 203 * Return TOR_COMPRESS_DONE if we've finished the entire 204 * compression/decompression. 205 * Return TOR_COMPRESS_OK if we're processed everything from the input. 206 * Return TOR_COMPRESS_BUFFER_FULL if we're out of space on <b>out</b>. 207 * Return TOR_COMPRESS_ERROR if the stream is corrupt. 208 */ 209 tor_compress_output_t 210 tor_zlib_compress_process(tor_zlib_compress_state_t *state, 211 char **out, size_t *out_len, 212 const char **in, size_t *in_len, 213 int finish) 214 { 215 int err; 216 tor_assert(state != NULL); 217 if (*in_len > UINT_MAX || 218 *out_len > UINT_MAX) { 219 return TOR_COMPRESS_ERROR; 220 } 221 222 state->stream.next_in = (unsigned char*) *in; 223 state->stream.avail_in = (unsigned int)*in_len; 224 state->stream.next_out = (unsigned char*) *out; 225 state->stream.avail_out = (unsigned int)*out_len; 226 227 if (state->compress) { 228 err = deflate(&state->stream, finish ? Z_FINISH : Z_NO_FLUSH); 229 } else { 230 err = inflate(&state->stream, finish ? Z_FINISH : Z_SYNC_FLUSH); 231 } 232 233 state->input_so_far += state->stream.next_in - ((unsigned char*)*in); 234 state->output_so_far += state->stream.next_out - ((unsigned char*)*out); 235 236 *out = (char*) state->stream.next_out; 237 *out_len = state->stream.avail_out; 238 *in = (const char *) state->stream.next_in; 239 *in_len = state->stream.avail_in; 240 241 if (! state->compress && 242 tor_compress_is_compression_bomb(state->input_so_far, 243 state->output_so_far)) { 244 log_warn(LD_DIR, "Possible zlib bomb; abandoning stream."); 245 return TOR_COMPRESS_ERROR; 246 } 247 248 switch (err) 249 { 250 case Z_STREAM_END: 251 return TOR_COMPRESS_DONE; 252 case Z_BUF_ERROR: 253 if (state->stream.avail_in == 0 && !finish) 254 return TOR_COMPRESS_OK; 255 return TOR_COMPRESS_BUFFER_FULL; 256 case Z_OK: 257 if (state->stream.avail_out == 0 || finish) 258 return TOR_COMPRESS_BUFFER_FULL; 259 return TOR_COMPRESS_OK; 260 default: 261 log_warn(LD_GENERAL, "Gzip returned an error: %s", 262 state->stream.msg ? state->stream.msg : "<no message>"); 263 return TOR_COMPRESS_ERROR; 264 } 265 } 266 267 /** Deallocate <b>state</b>. */ 268 void 269 tor_zlib_compress_free_(tor_zlib_compress_state_t *state) 270 { 271 if (state == NULL) 272 return; 273 274 atomic_counter_sub(&total_zlib_allocation, state->allocation); 275 276 if (state->compress) 277 deflateEnd(&state->stream); 278 else 279 inflateEnd(&state->stream); 280 281 tor_free(state); 282 } 283 284 /** Return the approximate number of bytes allocated for <b>state</b>. */ 285 size_t 286 tor_zlib_compress_state_size(const tor_zlib_compress_state_t *state) 287 { 288 tor_assert(state != NULL); 289 return state->allocation; 290 } 291 292 /** Return the approximate number of bytes allocated for all zlib states. */ 293 size_t 294 tor_zlib_get_total_allocation(void) 295 { 296 return atomic_counter_get(&total_zlib_allocation); 297 } 298 299 /** Set up global state for the zlib module */ 300 void 301 tor_zlib_init(void) 302 { 303 atomic_counter_init(&total_zlib_allocation); 304 }