Subject: | [PATCH] Handle non-PVs better |
See the attached patch. I tested this with a debugging bleadperl,
which failed assertions whenever SvCUR was used on a typeglob.
I’m afraid I have not had time to check whether we need new #defines
for 5.6 and 5.8, and I do not foresee having an opportunity any
time soon.
Some of the changes to Zlib.xs are not tested in the new test script
in the patch, as I don’t know how to reach all the code paths.
I am currently working on a debug mode for perl
(PERL_DEBUG_READONLY_COW) that will turn all COW buffer violations
into crashes. That was what drew my attention to this. The
changes to scan() fix crashes that I am getting from some of the
IO-Compress tests.
Subject: | patch.txt |
diff -Nurp Compress-Raw-Zlib-2.063-_WLyA5-orig/MANIFEST Compress-Raw-Zlib-2.063-_WLyA5/MANIFEST
--- Compress-Raw-Zlib-2.063-_WLyA5-orig/MANIFEST 2013-11-02 08:24:44.000000000 -0700
+++ Compress-Raw-Zlib-2.063-_WLyA5/MANIFEST 2013-12-15 16:31:36.000000000 -0800
@@ -6,6 +6,7 @@ t/02zlib.t
t/07bufsize.t
t/09limitoutput.t
t/18lvalue.t
+t/19nonpv.t
t/99pod.t
t/Test/Builder.pm
t/Test/More.pm
diff -Nurp Compress-Raw-Zlib-2.063-_WLyA5-orig/Zlib.xs Compress-Raw-Zlib-2.063-_WLyA5/Zlib.xs
--- Compress-Raw-Zlib-2.063-_WLyA5-orig/Zlib.xs 2013-05-19 04:21:34.000000000 -0700
+++ Compress-Raw-Zlib-2.063-_WLyA5/Zlib.xs 2013-12-15 21:28:14.000000000 -0800
@@ -864,13 +864,14 @@ _inflateInit(flags, windowBits, bufsize,
Safefree(s) ;
s = NULL ;
}
- else if (SvCUR(dictionary)) {
+ else if (sv_len(dictionary)) {
#ifdef AT_LEAST_ZLIB_1_2_2_1
/* Zlib 1.2.2.1 or better allows a dictionary with raw inflate */
if (s->WindowBits < 0) {
+ STRLEN dlen;
+ const Bytef* b = (const Bytef*)SvPVbyte(dictionary, dlen);
err = inflateSetDictionary(&(s->stream),
- (const Bytef*)SvPVbyte_nolen(dictionary),
- SvCUR(dictionary));
+ b, dlen);
if (err != Z_OK) {
Safefree(s) ;
s = NULL ;
@@ -938,6 +939,7 @@ deflate (s, buf, output)
uInt prefix = NO_INIT
int RETVAL = 0;
uLong bufinc = NO_INIT
+ STRLEN origlen = NO_INIT
CODE:
bufinc = s->bufsize;
@@ -949,8 +951,8 @@ deflate (s, buf, output)
if (DO_UTF8(buf) && !sv_utf8_downgrade(buf, 1))
croak("Wide character in Compress::Raw::Zlib::Deflate::deflate input parameter");
#endif
- s->stream.next_in = (Bytef*)SvPV_nomg_nolen(buf) ;
- s->stream.avail_in = SvCUR(buf) ;
+ s->stream.next_in = (Bytef*)SvPV_nomg(buf, origlen) ;
+ s->stream.avail_in = origlen;
if (s->flags & FLAG_CRC32)
s->crc32 = crc32(s->crc32, s->stream.next_in, s->stream.avail_in) ;
@@ -1033,7 +1035,7 @@ deflate (s, buf, output)
}
s->compressedBytes += cur_length + increment - prefix - s->stream.avail_out ;
- s->uncompressedBytes += SvCUR(buf) - s->stream.avail_in ;
+ s->uncompressedBytes += origlen - s->stream.avail_in ;
s->last_error = RETVAL ;
if (RETVAL == Z_OK) {
@@ -1365,6 +1367,7 @@ inflate (s, buf, output, eof=FALSE)
#ifdef UTF8_AVAILABLE
bool out_utf8 = FALSE;
#endif
+ STRLEN origlen;
CODE:
bufinc = s->bufsize;
/* If the buffer is a reference, dereference it */
@@ -1381,8 +1384,8 @@ inflate (s, buf, output, eof=FALSE)
#endif
/* initialise the input buffer */
- s->stream.next_in = (Bytef*)SvPV_nomg_nolen(buf) ;
- s->stream.avail_in = SvCUR(buf) ;
+ s->stream.next_in = (Bytef*)SvPV_nomg(buf, origlen) ;
+ s->stream.avail_in = origlen ;
/* and retrieve the output buffer */
output = deRef_l(output, "inflate") ;
@@ -1445,10 +1448,11 @@ Perl_sv_dump(output); */
if (RETVAL == Z_NEED_DICT && s->dictionary) {
+ STRLEN dlen;
+ const Bytef* b = SvPV(s->dictionary, dlen) ;
s->dict_adler = s->stream.adler ;
RETVAL = inflateSetDictionary(&(s->stream),
- (const Bytef*)SvPVX(s->dictionary),
- SvCUR(s->dictionary));
+ b, dlen);
if (RETVAL == Z_OK)
continue;
}
@@ -1497,7 +1501,7 @@ Perl_sv_dump(output); */
s->bytesInflated = cur_length + increment - s->stream.avail_out - prefix_length;
s->uncompressedBytes += s->bytesInflated ;
- s->compressedBytes += SvCUR(buf) - s->stream.avail_in ;
+ s->compressedBytes += origlen - s->stream.avail_in ;
SvPOK_only(output);
SvCUR_set(output, prefix_length + s->bytesInflated) ;
@@ -1571,7 +1575,7 @@ inflateSync (s, buf)
#endif
/* initialise the input buffer */
- s->stream.next_in = (Bytef*)SvPV_nomg_nolen(buf) ;
+ s->stream.next_in = (Bytef*)SvPV_force_nomg_nolen(buf) ;
s->stream.avail_in = SvCUR(buf) ;
/* inflateSync doesn't create any output */
diff -Nurp Compress-Raw-Zlib-2.063-_WLyA5-orig/t/19nonpv.t Compress-Raw-Zlib-2.063-_WLyA5/t/19nonpv.t
--- Compress-Raw-Zlib-2.063-_WLyA5-orig/t/19nonpv.t 1969-12-31 16:00:00.000000000 -0800
+++ Compress-Raw-Zlib-2.063-_WLyA5/t/19nonpv.t 2013-12-21 05:54:51.000000000 -0800
@@ -0,0 +1,135 @@
+BEGIN {
+ if ($ENV{PERL_CORE}) {
+ chdir 't' if -d 't';
+ @INC = ("../lib", "lib/compress");
+ }
+}
+
+use lib qw(t t/compress);
+use strict;
+use warnings;
+
+use Test::More ;
+use CompTestUtils;
+
+BEGIN
+{
+ # use Test::NoWarnings, if available
+ my $extra = 0 ;
+ $extra = 1
+ if eval { require Test::NoWarnings ; import Test::NoWarnings; 1 };
+
+ plan tests => 38 + $extra ;
+
+ use_ok('Compress::Raw::Zlib', 2) ;
+}
+
+
+
+my $hello = <<EOM ;
+hello world
+this is a test
+EOM
+
+my $len = length $hello ;
+
+# Check zlib_version and ZLIB_VERSION are the same.
+SKIP: {
+ skip "TEST_SKIP_VERSION_CHECK is set", 1
+ if $ENV{TEST_SKIP_VERSION_CHECK};
+ is Compress::Raw::Zlib::zlib_version, ZLIB_VERSION,
+ "ZLIB_VERSION matches Compress::Raw::Zlib::zlib_version" ;
+}
+
+
+{
+ title 'non-PV dictionary';
+ # ==============================
+
+ my $dictionary = *hello ;
+
+ ok my $x = new Compress::Raw::Zlib::Deflate({-Level => Z_BEST_COMPRESSION,
+ -Dictionary => $dictionary}) ;
+
+ my $dictID = $x->dict_adler() ;
+
+ my ($X, $Y, $Z);
+ cmp_ok $x->deflate($hello, $X), '==', Z_OK;
+ cmp_ok $x->flush($Y), '==', Z_OK;
+ $X .= $Y ;
+
+ ok my $k = new Compress::Raw::Zlib::Inflate(-Dictionary => $dictionary) ;
+
+ cmp_ok $k->inflate($X, $Z), '==', Z_STREAM_END;
+ is $k->dict_adler(), $dictID;
+ is $hello, $Z ;
+
+}
+
+{
+
+ title "deflate/inflate - non-PV buffers";
+ # ==============================
+
+ my $hello = *hello ;
+ my @hello = split('', $hello) ;
+ my ($err, $x, $X, $status);
+
+ ok( ($x, $err) = new Compress::Raw::Zlib::Deflate, "Create deflate object" );
+ ok $x, "Compress::Raw::Zlib::Deflate ok" ;
+ cmp_ok $err, '==', Z_OK, "status is Z_OK" ;
+
+ ok ! defined $x->msg() ;
+ is $x->total_in(), 0, "total_in() == 0" ;
+ is $x->total_out(), 0, "total_out() == 0" ;
+
+ $X = "" ;
+ my $Answer = '';
+ $status = $x->deflate($hello, $X) ;
+ $Answer .= $X ;
+
+ cmp_ok $status, '==', Z_OK, "deflate returned Z_OK" ;
+
+ cmp_ok $x->flush($X), '==', Z_OK, "flush returned Z_OK" ;
+ $Answer .= $X ;
+
+ ok ! defined $x->msg() ;
+ is $x->total_in(), length $hello, "total_in ok" ;
+ is $x->total_out(), length $Answer, "total_out ok" ;
+
+ my $k;
+ ok(($k, $err) = new Compress::Raw::Zlib::Inflate);
+ ok $k, "Compress::Raw::Zlib::Inflate ok" ;
+ cmp_ok $err, '==', Z_OK, "status is Z_OK" ;
+
+ ok ! defined $k->msg(), "No error messages" ;
+ is $k->total_in(), 0, "total_in() == 0" ;
+ is $k->total_out(), 0, "total_out() == 0" ;
+ my $GOT = '';
+ my $Z;
+ $Z = 1 ;#x 2000 ;
+ my $Alen = length $Answer;
+ $status = $k->inflate($Answer, $Z) ;
+ $GOT .= $Z ;
+
+ cmp_ok $status, '==', Z_STREAM_END, "Got Z_STREAM_END" ;
+ is $GOT, $hello, "uncompressed data matches ok" ;
+ ok ! defined $k->msg(), "No error messages" ;
+ is $k->total_in(), $Alen, "total_in ok" ;
+ is $k->total_out(), length $hello , "total_out ok";
+
+
+ ok(($k, $err) = new Compress::Raw::Zlib::Inflate);
+ ok $k, "Compress::Raw::Zlib::Inflate ok" ;
+ cmp_ok $err, '==', Z_OK, "status is Z_OK" ;
+
+ $Z = "";
+ $status = $k->inflate($hello, $Z);
+ is $Z, "", 'inflating *hello does not crash';
+
+ $hello = *hello;
+ $status = $k->inflateSync($hello);
+ cmp_ok $status, "!=", Z_OK,
+ "inflateSync on *hello returns error (and does not crash)";
+}
+