[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