diff options
author | Сергей Прохоров <[email protected]> | 2015-01-19 05:06:53 +0300 |
---|---|---|
committer | Сергей Прохоров <[email protected]> | 2015-02-12 21:44:11 +0300 |
commit | b24651c3bb6fef59c0e92c24e69151d1a92c4b08 (patch) | |
tree | b69a6e2e6a5ffbf3c1d73d4d0170df40c1982273 /erts/preloaded/src | |
parent | 50a92094372b45c9864afe3418b79605da549122 (diff) | |
download | otp-b24651c3bb6fef59c0e92c24e69151d1a92c4b08.tar.gz otp-b24651c3bb6fef59c0e92c24e69151d1a92c4b08.tar.bz2 otp-b24651c3bb6fef59c0e92c24e69151d1a92c4b08.zip |
Add zlib limited output buffer size functionality
This functionality may be useful for compressed streams with high
compression ratio (in case of gzip it may be up to x1000), when
small amount of compressed data will produce large amount of
uncompressed output. This may lead to DoS attacks, because
server easily goes out of memory.
Example of such high compression ratio stream:
```
dd if=/dev/zero of=sparse.bin bs=1MB count=100 # 100mb of zeroes
gzip sparse.bin # 95kb sparse.bin.gz
$ erl
> {ok, Compressed} = file:read_file("sparse.bin.gz"),
> 97082 = size(Compressed),
> Uncompressed = zlib:gunzip(Compressed),
> 100000000 = iolist_size(Uncompressed).
```
Diffstat (limited to 'erts/preloaded/src')
-rw-r--r-- | erts/preloaded/src/zlib.erl | 39 |
1 files changed, 38 insertions, 1 deletions
diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl index df7b2e6198..5ebc67dcaa 100644 --- a/erts/preloaded/src/zlib.erl +++ b/erts/preloaded/src/zlib.erl @@ -24,6 +24,7 @@ deflate/2,deflate/3,deflateEnd/1, inflateInit/1,inflateInit/2,inflateSetDictionary/2, inflateSync/1,inflateReset/1,inflate/2,inflateEnd/1, + inflateChunk/1, inflateChunk/2, setBufSize/2,getBufSize/1, crc32/1,crc32/2,crc32/3,adler32/2,adler32/3,getQSize/1, crc32_combine/4,adler32_combine/4, @@ -100,6 +101,7 @@ -define(INFLATE_RESET, 12). -define(INFLATE_END, 13). -define(INFLATE, 14). +-define(INFLATE_CHUNK, 25). -define(CRC32_0, 15). -define(CRC32_1, 16). @@ -263,6 +265,39 @@ inflate(Z, Data) -> erlang:error(badarg) end. +-spec inflateChunk(Z, Data) -> Decompressed | {more, Decompressed} when + Z :: zstream(), + Data :: iodata(), + Decompressed :: iolist(). +inflateChunk(Z, Data) -> + try port_command(Z, Data) of + true -> + inflateChunk(Z) + catch + error:_Err -> + flush(Z), + erlang:error(badarg) + end. + +-spec inflateChunk(Z) -> Decompressed | {more, Decompressed} when + Z :: zstream(), + Decompressed :: iolist(). +inflateChunk(Z) -> + Status = call(Z, ?INFLATE_CHUNK, []), + Data = receive + {Z, {data, Bin}} -> + Bin + after 0 -> + [] + end, + + case Status of + Good when (Good == ok) orelse (Good == stream_end) -> + Data; + inflate_has_more -> + {more, Data} + end. + -spec inflateEnd(Z) -> 'ok' when Z :: zstream(). inflateEnd(Z) -> @@ -514,7 +549,9 @@ call(Z, Cmd, Arg) -> [2,A,B,C,D] -> (A bsl 24)+(B bsl 16)+(C bsl 8)+D; [3,A,B,C,D] -> - erlang:error({need_dictionary,(A bsl 24)+(B bsl 16)+(C bsl 8)+D}) + erlang:error({need_dictionary,(A bsl 24)+(B bsl 16)+(C bsl 8)+D}); + [4, _, _, _, _] -> + inflate_has_more catch error:badarg -> %% Rethrow loses port_control from stacktrace. erlang:error(badarg) |