[erlang-questions] zlib design flaw?
Park, Sungjin
jinni.park@REDACTED
Thu Sep 25 10:48:50 CEST 2014
Organized the patch files again that you can just patch -p1 < zlib-patch.txt
On Thu, Sep 25, 2014 at 4:45 PM, Park, Sungjin <jinni.park@REDACTED> wrote:
> Related with this problem, I tried attached simple patches to define
> zlib:inflate/3. The last parameter, MaxSize is handed over to the port
> driver. And changed the port driver to stop streaming at this point.
>
> Patches compile good but at runtime, I get undefined function exception
> even it's exported by zlib.erl
>
> 1> Data=<<"0123456789">>.
> 2> Compressed=zlib:compress(Data).
> 3> Z=zlib:open().
> 4> zlib:inflateInit(Z).
> 5> catch zlib:inflate(Z,Compressed,9).
> {'EXIT',{undef,[{zlib,inflate,
> [#Port<0.582>,
>
> <<120,156,51,48,52,50,54,49,53,51,183,0,0,8,241,1,213>>,
> 9],
> []},
> {erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,661}]},
> {erl_eval,expr,5,[{file,"erl_eval.erl"},{line,434}]},
> {shell,exprs,7,[{file,"shell.erl"},{line,676}]},
> {shell,eval_exprs,7,[{file,"shell.erl"},{line,631}]},
> {shell,eval_loop,3,[{file,"shell.erl"},{line,616}]}]}}
> ...
>
> And also I get einval error (maybe from the port driver) when I call
> zlib:inflate/2.
>
> 6> catch zlib:inflate(Z,Compressed).
> {'EXIT',{einval,[{zlib,call,3,[]},
> {zlib,inflate,2,[]},
> {erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,661}]},
> {erl_eval,expr,5,[{file,"erl_eval.erl"},{line,434}]},
> {shell,exprs,7,[{file,"shell.erl"},{line,676}]},
> {shell,eval_exprs,7,[{file,"shell.erl"},{line,631}]},
> {shell,eval_loop,3,[{file,"shell.erl"},{line,616}]}]}}
>
> I suspect that the port driver patch is applied but zlib.erl is not.
> Is there any other place that I have to define zlib:inflate/3? Or did I
> make other mistake?
>
>
> On Thu, Sep 25, 2014 at 1:11 PM, Richard A. O'Keefe <ok@REDACTED>
> wrote:
>
>>
>> On 25/09/2014, at 5:46 AM, Tony Rogvall wrote:
>> >
>> > Thanks. Cool stuff :-)
>> >
>> > The following is also a fun version ( I am not the only one to blame
>> for api design faults :-)
>>
>> [[billion laughs]]
>>
>> Wikimedia markup has the same problem.
>>
>> For that matter, templates make the C++ type language
>> a Turing-complete (but seriously ugly) functional programming
>> language, and the various features that have been added to
>> the Haskell type system since 2010 make that a Turing-complete
>> logic programming language, so you can write a fairly short
>> C++ or Haskell program that takes as long to type-check as
>> you please.
>>
>> The Erlang binary term representation is just on the safe side
>> of this kind of attack. The fact that backreferences can only
>> be used to refer to *atoms* keeps it safe.
>>
>> I'm a little bit sad that this seems like a good reason not
>> to make the binary representation "smarter".
>>
>> Oh heck. The binary format I use to provide persistence for
>> Smalltalk has precisely this kind of problem, but if I
>> *didn't* allow backreferences to arbitrary objects I would not
>> be able to handle cyclic object graphs.
>>
>> I console myself: UBF(A) is on the wrong side of the safety
>> line. The term you get by decoding n UBF(a) bytes will be
>> O(n) in size, but it may take O(2**n) time to traverse it.
>>
>>
>> _______________________________________________
>> erlang-questions mailing list
>> erlang-questions@REDACTED
>> http://erlang.org/mailman/listinfo/erlang-questions
>>
>
>
>
> --
> Park, Sungjin
>
> -------------------------------------------------------------------------------------------------------------------
> Peculiar travel suggestions are dancing lessons from god.
> -- The Books of Bokonon
>
> -------------------------------------------------------------------------------------------------------------------
>
--
Park, Sungjin
-------------------------------------------------------------------------------------------------------------------
Peculiar travel suggestions are dancing lessons from god.
-- The Books of Bokonon
-------------------------------------------------------------------------------------------------------------------
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20140925/e4712504/attachment.htm>
-------------- next part --------------
diff --git a/erts/emulator/drivers/common/zlib_drv.c b/erts/emulator/drivers/common/zlib_drv.c
index 3143e45..bf2f63c 100644
--- a/erts/emulator/drivers/common/zlib_drv.c
+++ b/erts/emulator/drivers/common/zlib_drv.c
@@ -64,6 +64,8 @@
#define DEFAULT_BUFSZ 4000
+#define ZLIB_PORT_ERROR 90000
+
static int zlib_init(void);
static ErlDrvData zlib_start(ErlDrvPort port, char* buf);
static void zlib_stop(ErlDrvData e);
@@ -113,10 +115,12 @@ typedef struct {
uLong crc;
int inflate_eos_seen;
int want_crc; /* 1 if crc is calculated on clear text */
+ int sentsz;
+ int sentsz_max;
ErlDrvPort port; /* the associcated port */
} ZLibData;
-static int zlib_inflate(ZLibData* d, int flush);
+static int zlib_inflate(ZLibData* d, int flush, int sentsz_max);
static int zlib_deflate(ZLibData* d, int flush);
#if defined(__WIN32__)
@@ -159,6 +163,9 @@ static char* zlib_reason(int code, int* err)
case Z_VERSION_ERROR:
*err = 1;
return "version_error";
+ case ZLIB_PORT_ERROR:
+ *err = 1;
+ return "port_error";
default:
*err = 1;
return "unknown_error";
@@ -226,19 +233,32 @@ static int zlib_output_init(ZLibData* d)
static int zlib_output(ZLibData* d)
{
if (d->bin != NULL) {
- int len = d->binsz - d->s.avail_out;
- if (len > 0) {
- if (driver_output_binary(d->port, NULL, 0, d->bin, 0, len) < 0)
- return -1;
- }
- driver_free_binary(d->bin);
- d->bin = NULL;
- d->binsz = 0;
+ int len = d->binsz - d->s.avail_out;
+ if (len > 0) {
+ int will_be_sentsz = d->sentsz+len;
+ if (d->sentsz_max > 0 && will_be_sentsz > d->sentsz_max) {
+ driver_output_binary(d->port, NULL, 0, NULL, 0, 0);
+ return -1;
+ } else {
+ if (driver_output_binary(d->port, NULL, 0, d->bin, 0, len) < 0)
+ return -1;
+ d->sentsz = will_be_sentsz;
+ }
+ }
+ driver_free_binary(d->bin);
+ d->bin = NULL;
+ d->binsz = 0;
}
return zlib_output_init(d);
}
-static int zlib_inflate(ZLibData* d, int flush)
+/**
+ * Start inflating data.
+ * sentsz_max : max number of bytes that can be sent to the emulator
+ * Emulator receives an empty binary on overflow.
+ * sentsz_max = 0 means infinity.
+ */
+static int zlib_inflate(ZLibData* d, int flush, int sentsz_max)
{
int res = Z_OK;
@@ -247,6 +267,9 @@ static int zlib_inflate(ZLibData* d, int flush)
return Z_ERRNO;
}
+ d->sentsz_max = sentsz_max;
+ d->sentsz = 0;
+
while ((driver_sizeq(d->port) > 0) && (res != Z_STREAM_END)) {
int vlen;
SysIOVec* iov = driver_peekq(d->port, &vlen);
@@ -255,7 +278,7 @@ static int zlib_inflate(ZLibData* d, int flush)
d->s.next_in = iov[0].iov_base;
d->s.avail_in = iov[0].iov_len;
- while((possibly_more_output || (d->s.avail_in > 0)) && (res != Z_STREAM_END)) {
+ while((possibly_more_output || (d->s.avail_in > 0)) && (res != Z_STREAM_END) && (res != ZLIB_PORT_ERROR)) {
res = inflate(&d->s, Z_NO_FLUSH);
if (res == Z_NEED_DICT) {
/* Essential to eat the header bytes that zlib has looked at */
@@ -276,7 +299,8 @@ static int zlib_inflate(ZLibData* d, int flush)
if (d->want_crc)
d->crc = crc32(d->crc, (unsigned char*)d->bin->orig_bytes,
d->binsz - d->s.avail_out);
- zlib_output(d);
+ if (zlib_output(d) < 0)
+ res = ZLIB_PORT_ERROR;
possibly_more_output = 1;
}
}
@@ -288,7 +312,8 @@ static int zlib_inflate(ZLibData* d, int flush)
d->crc = crc32(d->crc, (unsigned char*) d->bin->orig_bytes,
d->binsz - d->s.avail_out);
}
- zlib_output(d);
+ if (res != ZLIB_PORT_ERROR)
+ zlib_output(d);
if (res == Z_STREAM_END) {
d->inflate_eos_seen = 1;
}
@@ -304,6 +329,9 @@ static int zlib_deflate(ZLibData* d, int flush)
return Z_ERRNO;
}
+ d->sentsz_max = 0;
+ d->sentsz = 0;
+
while ((driver_sizeq(d->port) > 0) && (res != Z_STREAM_END)) {
int vlen;
SysIOVec* iov = driver_peekq(d->port, &vlen);
@@ -560,8 +588,8 @@ static ErlDrvSSizeT zlib_ctl(ErlDrvData drv_data, unsigned int command, char *bu
case INFLATE:
if (d->state != ST_INFLATE) goto badarg;
- if (len != 4) goto badarg;
- res = zlib_inflate(d, i32(buf));
+ if (len != 8) goto badarg;
+ res = zlib_inflate(d, i32(buf), i32(buf+4));
if (res == Z_NEED_DICT) {
return zlib_value2(3, d->s.adler, rbuf, rlen);
} else {
diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl
index df7b2e6..1a1ee0e 100644
--- a/erts/preloaded/src/zlib.erl
+++ b/erts/preloaded/src/zlib.erl
@@ -23,7 +23,7 @@
deflateSetDictionary/2,deflateReset/1,deflateParams/3,
deflate/2,deflate/3,deflateEnd/1,
inflateInit/1,inflateInit/2,inflateSetDictionary/2,
- inflateSync/1,inflateReset/1,inflate/2,inflateEnd/1,
+ inflateSync/1,inflateReset/1,inflate/2,inflate/3,inflateEnd/1,
setBufSize/2,getBufSize/1,
crc32/1,crc32/2,crc32/3,adler32/2,adler32/3,getQSize/1,
crc32_combine/4,adler32_combine/4,
@@ -212,6 +212,9 @@ deflate(Z, Data, Flush) ->
_ = call(Z, ?DEFLATE, <<(arg_flush(Flush)):32>>),
collect(Z)
catch
+ throw:Reason ->
+ flush(Z),
+ erlang:exit(Reason);
error:_Err ->
flush(Z),
erlang:error(badarg)
@@ -253,11 +256,19 @@ inflateReset(Z) ->
Data :: iodata(),
Decompressed :: iolist().
inflate(Z, Data) ->
+ inflate(Z, Data, infinity).
+
+inflate(Z, Data, infinity) ->
+ inflate(Z, Data, 0);
+inflate(Z, Data, MaxSize) when erlang:is_integer(MaxSize) ->
try port_command(Z, Data) of
true ->
- _ = call(Z, ?INFLATE, <<?Z_NO_FLUSH:32>>),
+ _ = call(Z, ?INFLATE, <<?Z_NO_FLUSH:32,MaxSize:32>>),
collect(Z)
catch
+ throw:Reason ->
+ flush(Z),
+ erlang:exit(Reason);
error:_Err ->
flush(Z),
erlang:error(badarg)
@@ -456,8 +467,10 @@ collect(Z) ->
-spec collect(zstream(), iolist()) -> iolist().
collect(Z, Acc) ->
receive
+ {Z, {data, <<>>}} ->
+ erlang:throw(overflow);
{Z, {data, Bin}} ->
- collect(Z, [Bin|Acc])
+ collect(Z, [Bin|Acc])
after 0 ->
reverse(Acc)
end.
diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl
index 3be6f39..92c6e5f 100644
--- a/lib/kernel/test/zlib_SUITE.erl
+++ b/lib/kernel/test/zlib_SUITE.erl
@@ -82,7 +82,7 @@ groups() ->
api_deflateSetDictionary, api_deflateReset,
api_deflateParams, api_deflate, api_deflateEnd,
api_inflateInit, api_inflateSetDictionary,
- api_inflateSync, api_inflateReset, api_inflate,
+ api_inflateSync, api_inflateReset, api_inflate, api_inflateUpto,
api_inflateEnd, api_setBufsz, api_getBufsz, api_crc32,
api_adler32, api_getQSize, api_un_compress, api_un_zip,
api_g_un_zip]},
@@ -357,6 +357,18 @@ api_inflate(Config) when is_list(Config) ->
?m({'EXIT',{data_error,_}}, zlib:inflate(Z1, <<2,1,2,1,2>>)),
?m(ok, zlib:close(Z1)).
+api_inflateUpto(doc) -> "Test inflate up to given max size limit";
+api_inflateUpto(suite) -> [];
+api_inflateUpto(Config) when is_list(Config) ->
+ Data = [<<1,2,2,3,3,3,4,4,4,4>>],
+ ?line Compressed = zlib:compress(Data),
+ ?line Z1 = zlib:open(),
+ ?m(ok, zlib:inflateInit(Z1)),
+ Options = [{max_size, erlang:iolist_size(Data)-1}],
+ ?m({'EXIT', overflow}, zlib:inflate(Z1, Compressed, Options)),
+ ?m(ok, zlib:inflateEnd(Z1)),
+ ?m(ok, zlib:close(Z1)).
+
api_inflateEnd(doc) -> "Test inflateEnd";
api_inflateEnd(suite) -> [];
api_inflateEnd(Config) when is_list(Config) ->
More information about the erlang-questions
mailing list