Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PATCH] Stack overflow in Storable retrieve_hook #15831

Closed
p5pRT opened this issue Jan 24, 2017 · 26 comments
Closed

[PATCH] Stack overflow in Storable retrieve_hook #15831

p5pRT opened this issue Jan 24, 2017 · 26 comments

Comments

@p5pRT
Copy link

p5pRT commented Jan 24, 2017

Migrated from rt.perl.org#130635 (status was 'resolved')

Searchable as RT130635$

@p5pRT
Copy link
Author

p5pRT commented Jan 24, 2017

From @lightsey

Created by @lightsey

This is a bug report for perl from john@​nixnuts.net,
generated with the help of perlbug 1.40 running under perl 5.25.9.

-----------------------------------------------------------------
AFL detected a stack overflow in Storable's retrieve_hook() function.

The problem essentially is that a hook's classname length is read into
a signed integer, compared to the size of a stack buffer, then used to
read the classname. The size comparison treats the length as signed,
while the read treats the length as unsigned.

Perl Info

Flags:
    category=library
    severity=low
    Type=Patch
    PatchStatus=HasPatch
    module=Storable

Site configuration information for perl 5.25.9:

Configured by jd at Wed Jan  4 22:24:39 CST 2017.

Summary of my perl5 (revision 5 version 25 subversion 9) configuration:
  Snapshot of: 1e67156061b1f7aa186cda226e8470dfa1c5a681
  Platform:
    osname=linux
    osvers=4.8.0-2-amd64
    archname=x86_64-linux
    uname='linux slug 4.8.0-2-amd64 #1 smp debian 4.8.11-1 (2016-12-02) x86_64 gnulinux '
    config_args='-de -Dprefix=/home/jd/perl5/perlbrew/perls/perl-blead-new -Dcc=/usr/bin/afl-gcc -DDEBUGGING -Dusedevel -Aeval:scriptdir=/home/jd/perl5/perlbrew/perls/perl-blead-new/bin'
    hint=recommended
    useposix=true
    d_sigaction=define
    useithreads=undef
    usemultiplicity=undef
    use64bitint=define
    use64bitall=define
    uselongdouble=undef
    usemymalloc=n
    bincompat5005=undef
  Compiler:
    cc='/usr/bin/afl-gcc'
    ccflags ='-fwrapv -DDEBUGGING -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'
    optimize='-O2 -g'
    cppflags='-fwrapv -DDEBUGGING -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include'
    ccversion=''
    gccversion='6.3.0 20161229'
    gccosandvers=''
    intsize=4
    longsize=8
    ptrsize=8
    doublesize=8
    byteorder=12345678
    doublekind=3
    d_longlong=define
    longlongsize=8
    d_longdbl=define
    longdblsize=16
    longdblkind=3
    ivtype='long'
    ivsize=8
    nvtype='double'
    nvsize=8
    Off_t='off_t'
    lseeksize=8
    alignbytes=8
    prototype=define
  Linker and Libraries:
    ld='/usr/bin/afl-gcc'
    ldflags =' -fstack-protector-strong -L/usr/local/lib'
    libpth=/usr/local/lib /usr/lib/gcc/x86_64-linux-gnu/6/include-fixed /usr/include/x86_64-linux-gnu /usr/lib /lib/x86_64-linux-gnu /lib/../lib /usr/lib/x86_64-linux-gnu /usr/lib/../lib /lib
    libs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
    perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
    libc=libc-2.24.so
    so=so
    useshrplib=false
    libperl=libperl.a
    gnulibc_version='2.24'
  Dynamic Linking:
    dlsrc=dl_dlopen.xs
    dlext=so
    d_dlsymun=undef
    ccdlflags='-Wl,-E'
    cccdlflags='-fPIC'
    lddlflags='-shared -O2 -g -L/usr/local/lib -fstack-protector-strong'

Locally applied patches:
    Devel::PatchPerl 1.46


@INC for perl 5.25.9:
    /home/jd/perl5/perlbrew/perls/perl-blead-new/lib/site_perl/5.25.9/x86_64-linux
    /home/jd/perl5/perlbrew/perls/perl-blead-new/lib/site_perl/5.25.9
    /home/jd/perl5/perlbrew/perls/perl-blead-new/lib/5.25.9/x86_64-linux
    /home/jd/perl5/perlbrew/perls/perl-blead-new/lib/5.25.9


Environment for perl 5.25.9:
    HOME=/home/jd
    LANG=en_US.UTF-8
    LANGUAGE (unset)
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=/home/jd/perl5/perlbrew/bin:/home/jd/perl5/perlbrew/perls/perl-blead-new/bin:/home/jd/.gems/bin:/home/jd/bin:/home/jd/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
    PERLBREW_BASHRC_VERSION=0.76
    PERLBREW_HOME=/home/jd/.perlbrew
    PERLBREW_MANPATH=/home/jd/perl5/perlbrew/perls/perl-blead-new/man
    PERLBREW_PATH=/home/jd/perl5/perlbrew/bin:/home/jd/perl5/perlbrew/perls/perl-blead-new/bin
    PERLBREW_PERL=perl-blead-new
    PERLBREW_ROOT=/home/jd/perl5/perlbrew
    PERLBREW_VERSION=0.78
    PERL_BADLANG (unset)
    SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented Jan 24, 2017

From @lightsey

0001-Fix-stack-buffer-overflow-in-deserialization-of-hook.patch
From 819dc7bc9ce600ef0bb505392ed949a9b6b9bc91 Mon Sep 17 00:00:00 2001
From: John Lightsey <jd@cpanel.net>
Date: Tue, 24 Jan 2017 10:30:18 -0600
Subject: [PATCH] Fix stack buffer overflow in deserialization of hooks.

The use of signed lengths resulted in a stack overflow in retrieve_hook()
when a negative length was provided in the storable data.

The retrieve_blessed() codepath had a similar problem with the placement
of the trailing null byte when negative lengths were provided.
---
 dist/Storable/Storable.xs |  4 ++--
 dist/Storable/t/store.t   | 24 +++++++++++++++++++++++-
 2 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/dist/Storable/Storable.xs b/dist/Storable/Storable.xs
index a72d84cbf5..ba5f3503da 100644
--- a/dist/Storable/Storable.xs
+++ b/dist/Storable/Storable.xs
@@ -4048,7 +4048,7 @@ static SV *retrieve_idx_blessed(pTHX_ stcxt_t *cxt, const char *cname)
  */
 static SV *retrieve_blessed(pTHX_ stcxt_t *cxt, const char *cname)
 {
-	I32 len;
+	U32 len;
 	SV *sv;
 	char buf[LG_BLESS + 1];		/* Avoid malloc() if possible */
 	char *classname = buf;
@@ -4119,7 +4119,7 @@ static SV *retrieve_blessed(pTHX_ stcxt_t *cxt, const char *cname)
  */
 static SV *retrieve_hook(pTHX_ stcxt_t *cxt, const char *cname)
 {
-	I32 len;
+	U32 len;
 	char buf[LG_BLESS + 1];		/* Avoid malloc() if possible */
 	char *classname = buf;
 	unsigned int flags;
diff --git a/dist/Storable/t/store.t b/dist/Storable/t/store.t
index 3a4b9dca8a..8c07828ed5 100644
--- a/dist/Storable/t/store.t
+++ b/dist/Storable/t/store.t
@@ -19,7 +19,7 @@ sub BEGIN {
 
 use Storable qw(store retrieve store_fd nstore_fd fd_retrieve);
 
-use Test::More tests => 24;
+use Test::More tests => 25;
 
 $a = 'toto';
 $b = \$a;
@@ -101,5 +101,27 @@ isnt($@, '');
     }
 }
 
+{
+
+    my $pid = fork();
+    if ($pid) {
+        waitpid( $pid, 0 );
+        ok( $? == 0 || $? == 256, 'No stack smashing error when retieving hook' );
+    }
+    elsif ( defined $pid ) {
+        close STDOUT;
+        close STDERR;
+        my $frozen =
+          "\x70\x73\x74\x30\x04\x0a\x08\x31\x32\x33\x34\x35\x36\x37\x38\x04\x08\x08\x08\x03\x39\xff\xfa\x09\x19\x08\x17\x68\x08\x08\x08\x08\xf9\x16\x16\x13\x16\x10\x10\x10\xff\x15\x16\x16\x16\x1e\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x13\xf0\x16\x16\x16\xfe\x16\x41\x41\x41\x41\xe8\x03\x41\x41\x41\x41\x41\x41\x41\x41\x51\x41\xa9\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xb8\xac\xac\xac\xac\xac\xac\xac\xac\x9a\xac\xac\xac\xac\xac\xac\xac\xac\xac\x93\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\x00\x64\xac\xa8\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\x2c\xac\x41\x41\x41\x41\x41\x41\x41\x41\x41\x00\x80\x41\x80\x41\x41\x41\x41\x41\x41\x51\x41\xac\xac\xac";
+        open my $fh, '<', \$frozen;
+        eval { Storable::fd_retrieve($fh); };
+        exit(0);
+    }
+    else {
+        die "Failed to fork: $!";
+    }
+
+}
+
 close OUT or die "Could not close: $!";
 END { 1 while unlink 'store' }
-- 
2.11.0

@p5pRT
Copy link
Author

p5pRT commented Jan 25, 2017

From @jkeenan

On Tue, 24 Jan 2017 19​:22​:28 GMT, john@​nixnuts.net wrote​:

This is a bug report for perl from john@​nixnuts.net,
generated with the help of perlbug 1.40 running under perl 5.25.9.

-----------------------------------------------------------------
AFL detected a stack overflow in Storable's retrieve_hook() function.

The problem essentially is that a hook's classname length is read into
a signed integer, compared to the size of a stack buffer, then used to
read the classname. The size comparison treats the length as signed,
while the read treats the length as unsigned.

Available for smoke-testing in this branch​:

smoke-me/jkeenan/130635-storable

I corrected one spelling error in a test description and incremented the VERSION number.

--
James E Keenan (jkeenan@​cpan.org)

@p5pRT
Copy link
Author

p5pRT commented Jan 25, 2017

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented Jan 25, 2017

From @jkeenan

On Wed, 25 Jan 2017 02​:05​:45 GMT, jkeenan wrote​:

On Tue, 24 Jan 2017 19​:22​:28 GMT, john@​nixnuts.net wrote​:

This is a bug report for perl from john@​nixnuts.net,
generated with the help of perlbug 1.40 running under perl 5.25.9.

-----------------------------------------------------------------
AFL detected a stack overflow in Storable's retrieve_hook() function.

The problem essentially is that a hook's classname length is read
into
a signed integer, compared to the size of a stack buffer, then used
to
read the classname. The size comparison treats the length as signed,
while the read treats the length as unsigned.

Available for smoke-testing in this branch​:

smoke-me/jkeenan/130635-storable

I corrected one spelling error in a test description and incremented
the VERSION number.

This revision failed on FreeBSD-11. See​: http​://perl5.test-smoke.org/report/53470

When I ran the test file individually, it hung at 'ok 24', then printed these error messages​:

#####
swap_pager​: out of swap space
swap_pager_getswapspace(16)​: failed
swap_pager_getswapspace(16)​: failed
swap_pager_getswapspace(16)​: failed
#####

kernel​: pid 18627 (perl), uid 1001, was killed​: out of swap space

So something is clearly amiss with this patch.

Thank you very much.

--
James E Keenan (jkeenan@​cpan.org)

@p5pRT
Copy link
Author

p5pRT commented Jan 25, 2017

From @jkeenan

On Wed, 25 Jan 2017 04​:04​:15 GMT, jkeenan wrote​:

On Wed, 25 Jan 2017 02​:05​:45 GMT, jkeenan wrote​:

On Tue, 24 Jan 2017 19​:22​:28 GMT, john@​nixnuts.net wrote​:

This is a bug report for perl from john@​nixnuts.net,
generated with the help of perlbug 1.40 running under perl 5.25.9.

-----------------------------------------------------------------
AFL detected a stack overflow in Storable's retrieve_hook()
function.

The problem essentially is that a hook's classname length is read
into
a signed integer, compared to the size of a stack buffer, then used
to
read the classname. The size comparison treats the length as
signed,
while the read treats the length as unsigned.

Available for smoke-testing in this branch​:

smoke-me/jkeenan/130635-storable

I corrected one spelling error in a test description and incremented
the VERSION number.

This revision failed on FreeBSD-11. See​: http​://perl5.test-
smoke.org/report/53470

When I ran the test file individually, it hung at 'ok 24', then
printed these error messages​:

#####
swap_pager​: out of swap space
swap_pager_getswapspace(16)​: failed
swap_pager_getswapspace(16)​: failed
swap_pager_getswapspace(16)​: failed
#####

kernel​: pid 18627 (perl), uid 1001, was killed​: out of swap space

So something is clearly amiss with this patch.

Thank you very much.

Similar results on FreeBSD-10.3​:
http​://perl5.test-smoke.org/report/53481
--
James E Keenan (jkeenan@​cpan.org)

@p5pRT
Copy link
Author

p5pRT commented Jan 25, 2017

From @jkeenan

On Wed, 25 Jan 2017 15​:21​:36 GMT, jkeenan wrote​:

On Wed, 25 Jan 2017 04​:04​:15 GMT, jkeenan wrote​:

On Wed, 25 Jan 2017 02​:05​:45 GMT, jkeenan wrote​:

On Tue, 24 Jan 2017 19​:22​:28 GMT, john@​nixnuts.net wrote​:

This is a bug report for perl from john@​nixnuts.net,
generated with the help of perlbug 1.40 running under perl 5.25.9.

-----------------------------------------------------------------
AFL detected a stack overflow in Storable's retrieve_hook()
function.

The problem essentially is that a hook's classname length is read
into
a signed integer, compared to the size of a stack buffer, then used
to
read the classname. The size comparison treats the length as
signed,
while the read treats the length as unsigned.

Available for smoke-testing in this branch​:

smoke-me/jkeenan/130635-storable

I corrected one spelling error in a test description and incremented
the VERSION number.

This revision failed on FreeBSD-11. See​: http​://perl5.test-
smoke.org/report/53470

When I ran the test file individually, it hung at 'ok 24', then
printed these error messages​:

#####
swap_pager​: out of swap space
swap_pager_getswapspace(16)​: failed
swap_pager_getswapspace(16)​: failed
swap_pager_getswapspace(16)​: failed
#####

kernel​: pid 18627 (perl), uid 1001, was killed​: out of swap space

So something is clearly amiss with this patch.

Thank you very much.

Similar results on FreeBSD-10.3​:
http​://perl5.test-smoke.org/report/53481

On (at least) FreeBSD-10.3, it appears that the test failure occurs when I configure with -DDEBUGGING​:

#####
[perl] $ gitcurr
130635-storable
[perl] $ git show | head -1
commit 706cfb1
[perl] $ ./perl -Ilib -V | grep config_args
  config_args='-des -Dusedevel -Duseithreads -Doptimize=-O2 -pipe -fstack-protector -fno-strict-aliasing -DDEBUGGING'
#####
cd t;./perl harness -v ../dist/Storable/t/store.t; cd -
...
ok 24 - RT 130098​: no segfault in Storable​::fd_retrieve()

# Failed test 'No stack smashing error when retrieving hook'
# at t/store.t line 109.
# Looks like you failed 1 test of 25.
not ok 25 - No stack smashing error when retrieving hook
Dubious, test returned 1 (wstat 256, 0x100)
Failed 1/25 subtests
...
#####

In the same branch, at the same commit, configuring exactly the same except without -DDEBUGGING, the test file completes successfully.

--
James E Keenan (jkeenan@​cpan.org)

@p5pRT
Copy link
Author

p5pRT commented Jan 25, 2017

From @lightsey

On Wed, 2017-01-25 at 07​:21 -0800, James E Keenan via RT wrote​:

On Wed, 25 Jan 2017 04​:04​:15 GMT, jkeenan wrote​:

This revision failed on FreeBSD-11.  See​:  http​://perl5.test-
smoke.org/report/53470
 
When I ran the test file individually, it hung at 'ok 24', then
printed these error messages​:
 
#####
swap_pager​: out of swap space
swap_pager_getswapspace(16)​: failed
swap_pager_getswapspace(16)​: failed
swap_pager_getswapspace(16)​: failed
#####
 
kernel​: pid 18627 (perl), uid 1001, was killed​: out of swap space
 
So something is clearly amiss with this patch.
 
Thank you very much.

Similar results on FreeBSD-10.3​:
http​://perl5.test-smoke.org/report/53481

Makes sense.. When the bug is fixed, it ends up allocating an insanely large
amount of memory. On Linux, malloc doesn't even try to allocate the memory. It
sounds like BSD does try to allocate it.

Maybe it's possible to limit the maximum allowed classname length to what Perl
actually supports. I'll try and adjust the patch to do this.

@p5pRT
Copy link
Author

p5pRT commented Jan 25, 2017

From @jkeenan

On Wed, 25 Jan 2017 15​:42​:21 GMT, jkeenan wrote​:

On Wed, 25 Jan 2017 15​:21​:36 GMT, jkeenan wrote​:

On Wed, 25 Jan 2017 04​:04​:15 GMT, jkeenan wrote​:

On Wed, 25 Jan 2017 02​:05​:45 GMT, jkeenan wrote​:

On Tue, 24 Jan 2017 19​:22​:28 GMT, john@​nixnuts.net wrote​:

This is a bug report for perl from john@​nixnuts.net,
generated with the help of perlbug 1.40 running under perl
5.25.9.

-----------------------------------------------------------------
AFL detected a stack overflow in Storable's retrieve_hook()
function.

The problem essentially is that a hook's classname length is
read
into
a signed integer, compared to the size of a stack buffer, then
used
to
read the classname. The size comparison treats the length as
signed,
while the read treats the length as unsigned.

Available for smoke-testing in this branch​:

smoke-me/jkeenan/130635-storable

I corrected one spelling error in a test description and
incremented
the VERSION number.

This revision failed on FreeBSD-11. See​: http​://perl5.test-
smoke.org/report/53470

When I ran the test file individually, it hung at 'ok 24', then
printed these error messages​:

#####
swap_pager​: out of swap space
swap_pager_getswapspace(16)​: failed
swap_pager_getswapspace(16)​: failed
swap_pager_getswapspace(16)​: failed
#####

kernel​: pid 18627 (perl), uid 1001, was killed​: out of swap space

So something is clearly amiss with this patch.

Thank you very much.

Similar results on FreeBSD-10.3​:
http​://perl5.test-smoke.org/report/53481

On (at least) FreeBSD-10.3, it appears that the test failure occurs
when I configure with -DDEBUGGING​:

#####
[perl] $ gitcurr
130635-storable
[perl] $ git show | head -1
commit 706cfb1
[perl] $ ./perl -Ilib -V | grep config_args
config_args='-des -Dusedevel -Duseithreads -Doptimize=-O2 -pipe
-fstack-protector -fno-strict-aliasing -DDEBUGGING'
#####
cd t;./perl harness -v ../dist/Storable/t/store.t; cd -
...
ok 24 - RT 130098​: no segfault in Storable​::fd_retrieve()

# Failed test 'No stack smashing error when retrieving hook'
# at t/store.t line 109.
# Looks like you failed 1 test of 25.
not ok 25 - No stack smashing error when retrieving hook
Dubious, test returned 1 (wstat 256, 0x100)
Failed 1/25 subtests
...
#####

In the same branch, at the same commit, configuring exactly the same
except without -DDEBUGGING, the test file completes successfully.

On FreeBSD-11, I get the same difference in results between with/without -DDEBUGGING.

On Linux, with -DDEBUGGING, the test file eventually completes ... but clearly hangs for some time after 'ok 21' and takes 30s to run.

So the patch does not play well with -DDEBUGGING regardless of OS.

Thank you very much.
--
James E Keenan (jkeenan@​cpan.org)

@p5pRT
Copy link
Author

p5pRT commented Jan 25, 2017

From @lightsey

On Wed, 2017-01-25 at 08​:12 -0800, James E Keenan via RT wrote​:

On FreeBSD-11, I get the same difference in results between with/without
-DDEBUGGING.

On Linux, with -DDEBUGGING, the test file eventually completes ... but clearly
hangs for some time after 'ok 21' and takes 30s to run.

So the patch does not play well with -DDEBUGGING regardless of OS.

Thank you very much.

I'm attaching an updated patch that croaks before these oversized New() calls
rather than trying to handle the failures they generate.

@p5pRT
Copy link
Author

p5pRT commented Jan 25, 2017

From @lightsey

0001-Fix-stack-buffer-overflow-in-deserialization-of-hook.patch
From 8ce5d6358ae296600b6ee2950e17f8fbbdfda586 Mon Sep 17 00:00:00 2001
From: John Lightsey <jd@cpanel.net>
Date: Tue, 24 Jan 2017 10:30:18 -0600
Subject: [PATCH] Fix stack buffer overflow in deserialization of hooks.

The use of signed lengths resulted in a stack overflow in retrieve_hook()
when a negative length was provided in the storable data.

The retrieve_blessed() codepath had a similar problem with the placement
of the trailing null byte when negative lengths were provided.
---
 dist/Storable/Storable.pm |  2 +-
 dist/Storable/Storable.xs | 11 +++++++++--
 dist/Storable/t/store.t   | 12 +++++++++++-
 3 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/dist/Storable/Storable.pm b/dist/Storable/Storable.pm
index 397d584763..d8fd740eee 100644
--- a/dist/Storable/Storable.pm
+++ b/dist/Storable/Storable.pm
@@ -22,7 +22,7 @@ package Storable; @ISA = qw(Exporter);
 
 use vars qw($canonical $forgive_me $VERSION);
 
-$VERSION = '2.61';
+$VERSION = '2.62';
 
 BEGIN {
     if (eval {
diff --git a/dist/Storable/Storable.xs b/dist/Storable/Storable.xs
index a72d84cbf5..829c5d73ac 100644
--- a/dist/Storable/Storable.xs
+++ b/dist/Storable/Storable.xs
@@ -4048,7 +4048,7 @@ static SV *retrieve_idx_blessed(pTHX_ stcxt_t *cxt, const char *cname)
  */
 static SV *retrieve_blessed(pTHX_ stcxt_t *cxt, const char *cname)
 {
-	I32 len;
+	U32 len;
 	SV *sv;
 	char buf[LG_BLESS + 1];		/* Avoid malloc() if possible */
 	char *classname = buf;
@@ -4069,6 +4069,9 @@ static SV *retrieve_blessed(pTHX_ stcxt_t *cxt, const char *cname)
 	if (len & 0x80) {
 		RLEN(len);
 		TRACEME(("** allocating %d bytes for class name", len+1));
+		if (len > I32_MAX) {
+			CROAK(("Corrupted classname length"));
+		}
 		New(10003, classname, len+1, char);
 		malloced_classname = classname;
 	}
@@ -4119,7 +4122,7 @@ static SV *retrieve_blessed(pTHX_ stcxt_t *cxt, const char *cname)
  */
 static SV *retrieve_hook(pTHX_ stcxt_t *cxt, const char *cname)
 {
-	I32 len;
+	U32 len;
 	char buf[LG_BLESS + 1];		/* Avoid malloc() if possible */
 	char *classname = buf;
 	unsigned int flags;
@@ -4253,6 +4256,10 @@ static SV *retrieve_hook(pTHX_ stcxt_t *cxt, const char *cname)
 		else
 			GETMARK(len);
 
+		if (len > I32_MAX) {
+			CROAK(("Corrupted classname length"));
+		}
+
 		if (len > LG_BLESS) {
 			TRACEME(("** allocating %d bytes for class name", len+1));
 			New(10003, classname, len+1, char);
diff --git a/dist/Storable/t/store.t b/dist/Storable/t/store.t
index 3a4b9dca8a..d1ec55d60e 100644
--- a/dist/Storable/t/store.t
+++ b/dist/Storable/t/store.t
@@ -19,7 +19,7 @@ sub BEGIN {
 
 use Storable qw(store retrieve store_fd nstore_fd fd_retrieve);
 
-use Test::More tests => 24;
+use Test::More tests => 25;
 
 $a = 'toto';
 $b = \$a;
@@ -101,5 +101,15 @@ isnt($@, '');
     }
 }
 
+{
+
+    my $frozen =
+      "\x70\x73\x74\x30\x04\x0a\x08\x31\x32\x33\x34\x35\x36\x37\x38\x04\x08\x08\x08\x03\x39\xff\xfa\x09\x19\x08\x17\x68\x08\x08\x08\x08\xf9\x16\x16\x13\x16\x10\x10\x10\xff\x15\x16\x16\x16\x1e\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x13\xf0\x16\x16\x16\xfe\x16\x41\x41\x41\x41\xe8\x03\x41\x41\x41\x41\x41\x41\x41\x41\x51\x41\xa9\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xb8\xac\xac\xac\xac\xac\xac\xac\xac\x9a\xac\xac\xac\xac\xac\xac\xac\xac\xac\x93\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\x00\x64\xac\xa8\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\x2c\xac\x41\x41\x41\x41\x41\x41\x41\x41\x41\x00\x80\x41\x80\x41\x41\x41\x41\x41\x41\x51\x41\xac\xac\xac";
+    open my $fh, '<', \$frozen;
+    eval { Storable::fd_retrieve($fh); };
+    ok('RT 130635: No stack smashing error when retrieving hook');
+
+}
+
 close OUT or die "Could not close: $!";
 END { 1 while unlink 'store' }
-- 
2.11.0

@p5pRT
Copy link
Author

p5pRT commented Jan 25, 2017

From @jkeenan

On Wed, 25 Jan 2017 19​:44​:02 GMT, john@​nixnuts.net wrote​:

On Wed, 2017-01-25 at 08​:12 -0800, James E Keenan via RT wrote​:

On FreeBSD-11, I get the same difference in results between
with/without
-DDEBUGGING.

On Linux, with -DDEBUGGING, the test file eventually completes ...
but clearly
hangs for some time after 'ok 21' and takes 30s to run.

So the patch does not play well with -DDEBUGGING regardless of OS.

Thank you very much.

I'm attaching an updated patch that croaks before these oversized
New() calls
rather than trying to handle the failures they generate.

On Linux, with a -DDEBUGGING build, the runtime of the test came down to a reasonable length.

However on both FreeBSD 10 and 11, the results under -DDEBUGGING were still unsatisfactory. Running the test manually on 10.3, I had to Ctrl-C the test after 2 minutes. Running smoke tests on 11, it appears that the test completed successfully once but then got the same swap errors previously reported, leading to the curious result of "PASS-so-far"​:
http​://perl5.test-smoke.org/report/53487
--
James E Keenan (jkeenan@​cpan.org)

@p5pRT
Copy link
Author

p5pRT commented Jan 26, 2017

From @lightsey

On Wed, 2017-01-25 at 14​:31 -0800, James E Keenan via RT wrote​:

On Wed, 25 Jan 2017 19​:44​:02 GMT, john@​nixnuts.net wrote​:

On Wed, 2017-01-25 at 08​:12 -0800, James E Keenan via RT wrote​:

On FreeBSD-11, I get the same difference in results between
with/without
-DDEBUGGING.

On Linux, with -DDEBUGGING, the test file eventually completes ...
but clearly
hangs for some time after 'ok 21' and takes 30s to run.

So the patch does not play well with -DDEBUGGING regardless of OS.

Thank you very much.
 
I'm attaching an updated patch that croaks before these oversized
New() calls
rather than trying to handle the failures they generate.

On Linux, with a -DDEBUGGING build, the runtime of the test came down to a
reasonable length.

However on both FreeBSD 10 and 11, the results under -DDEBUGGING were still
unsatisfactory.  Running the test manually on 10.3, I had to Ctrl-C the test
after 2 minutes.  Running smoke tests on 11, it appears that the test
completed successfully once but then got the same swap errors previously
reported, leading to the curious result of "PASS-so-far"​:
http​://perl5.test-smoke.org/report/53487

I brought up a FreeBSD 10.3 AMD64 VM to test and saw a similar issue when I ran
the new unit test with the older version of Storable. The long pause on BSD
seemed to be caused by coredumps being enabled (default on FreeBSD, not default
on Linux.)

I didn't see any crashes on the FreeBSD VM when testing blead with the new
version of the patch. The configure settings I used were​:

./Configure -des -DDEBUGGING -Dusedevel

Is it possible that some of the failures you're seeing were caused by using the
wrong version of Storable?

@p5pRT
Copy link
Author

p5pRT commented Jan 26, 2017

From @jkeenan

On Thu, 26 Jan 2017 21​:02​:12 GMT, john@​nixnuts.net wrote​:

On Wed, 2017-01-25 at 14​:31 -0800, James E Keenan via RT wrote​:

On Wed, 25 Jan 2017 19​:44​:02 GMT, john@​nixnuts.net wrote​:

On Wed, 2017-01-25 at 08​:12 -0800, James E Keenan via RT wrote​:

On FreeBSD-11, I get the same difference in results between
with/without
-DDEBUGGING.

On Linux, with -DDEBUGGING, the test file eventually completes
...
but clearly
hangs for some time after 'ok 21' and takes 30s to run.

So the patch does not play well with -DDEBUGGING regardless of
OS.

Thank you very much.

I'm attaching an updated patch that croaks before these oversized
New() calls
rather than trying to handle the failures they generate.

On Linux, with a -DDEBUGGING build, the runtime of the test came down
to a
reasonable length.

However on both FreeBSD 10 and 11, the results under -DDEBUGGING were
still
unsatisfactory.  Running the test manually on 10.3, I had to Ctrl-C
the test
after 2 minutes.  Running smoke tests on 11, it appears that the test
completed successfully once but then got the same swap errors
previously
reported, leading to the curious result of "PASS-so-far"​:
http​://perl5.test-smoke.org/report/53487

I brought up a FreeBSD 10.3 AMD64 VM to test and saw a similar issue
when I ran
the new unit test with the older version of Storable. The long pause
on BSD
seemed to be caused by coredumps being enabled (default on FreeBSD,
not default
on Linux.)

I didn't see any crashes on the FreeBSD VM when testing blead with the
new
version of the patch. The configure settings I used were​:

./Configure -des -DDEBUGGING -Dusedevel

Is it possible that some of the failures you're seeing were caused by
using the
wrong version of Storable?

I used the most recent version of this branch in the perl 5 repository​:

smoke-me/jkeenan/130635-storable

... which I believe incorporates your revised patch.

As previously reported, I configure with​:

"-des -Dusedevel -Duseithreads -Doptimize='-O2 -pipe -fstack-protector -fno-strict-aliasing' -DDEBUGGING"

... because that gets us very close to the way that the FreeBSD port of perl is configured.

Thank you very much.

--
James E Keenan (jkeenan@​cpan.org)

@p5pRT
Copy link
Author

p5pRT commented Jan 29, 2017

From @lightsey

On Thu, 2017-01-26 at 13​:48 -0800, James E Keenan via RT wrote​:

As previously reported, I configure with​:

"-des -Dusedevel -Duseithreads -Doptimize='-O2 -pipe -fstack-protector -fno-
strict-aliasing' -DDEBUGGING"

... because that gets us very close to the way that the FreeBSD port of perl
is configured.

Excellent, thanks.

The problem turned out to be that the AFL generated payload was hitting two
other memory allocation errors before it even entered retrieve_hook(). That
combination of flags on FreeBSD seems to crash whenever Storable tries to
allocate too much memory.

I adjusted the test data to use more realistic sizes when it enters
retrieve_hash() and retrieve_flag_hash() so that it's only focusing on the stack
overflow in retrieve_hook().

I also cleaned up the test output formatting a bit.

An updated patch is attached.

@p5pRT
Copy link
Author

p5pRT commented Jan 29, 2017

From @lightsey

0001-Fix-stack-buffer-overflow-in-deserialization-of-hook.patch
From f42cd5a57f1e28632f7761e29c84cb0c72acd299 Mon Sep 17 00:00:00 2001
From: John Lightsey <jd@cpanel.net>
Date: Tue, 24 Jan 2017 10:30:18 -0600
Subject: [PATCH] Fix stack buffer overflow in deserialization of hooks.

The use of signed lengths resulted in a stack overflow in retrieve_hook()
when a negative length was provided in the storable data.

The retrieve_blessed() codepath had a similar problem with the placement
of the trailing null byte when negative lengths were provided.
---
 dist/Storable/Storable.pm |  2 +-
 dist/Storable/Storable.xs | 11 +++++++++--
 dist/Storable/t/store.t   | 12 +++++++++++-
 3 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/dist/Storable/Storable.pm b/dist/Storable/Storable.pm
index 397d584763..d8fd740eee 100644
--- a/dist/Storable/Storable.pm
+++ b/dist/Storable/Storable.pm
@@ -22,7 +22,7 @@ package Storable; @ISA = qw(Exporter);
 
 use vars qw($canonical $forgive_me $VERSION);
 
-$VERSION = '2.61';
+$VERSION = '2.62';
 
 BEGIN {
     if (eval {
diff --git a/dist/Storable/Storable.xs b/dist/Storable/Storable.xs
index a72d84cbf5..829c5d73ac 100644
--- a/dist/Storable/Storable.xs
+++ b/dist/Storable/Storable.xs
@@ -4048,7 +4048,7 @@ static SV *retrieve_idx_blessed(pTHX_ stcxt_t *cxt, const char *cname)
  */
 static SV *retrieve_blessed(pTHX_ stcxt_t *cxt, const char *cname)
 {
-	I32 len;
+	U32 len;
 	SV *sv;
 	char buf[LG_BLESS + 1];		/* Avoid malloc() if possible */
 	char *classname = buf;
@@ -4069,6 +4069,9 @@ static SV *retrieve_blessed(pTHX_ stcxt_t *cxt, const char *cname)
 	if (len & 0x80) {
 		RLEN(len);
 		TRACEME(("** allocating %d bytes for class name", len+1));
+		if (len > I32_MAX) {
+			CROAK(("Corrupted classname length"));
+		}
 		New(10003, classname, len+1, char);
 		malloced_classname = classname;
 	}
@@ -4119,7 +4122,7 @@ static SV *retrieve_blessed(pTHX_ stcxt_t *cxt, const char *cname)
  */
 static SV *retrieve_hook(pTHX_ stcxt_t *cxt, const char *cname)
 {
-	I32 len;
+	U32 len;
 	char buf[LG_BLESS + 1];		/* Avoid malloc() if possible */
 	char *classname = buf;
 	unsigned int flags;
@@ -4253,6 +4256,10 @@ static SV *retrieve_hook(pTHX_ stcxt_t *cxt, const char *cname)
 		else
 			GETMARK(len);
 
+		if (len > I32_MAX) {
+			CROAK(("Corrupted classname length"));
+		}
+
 		if (len > LG_BLESS) {
 			TRACEME(("** allocating %d bytes for class name", len+1));
 			New(10003, classname, len+1, char);
diff --git a/dist/Storable/t/store.t b/dist/Storable/t/store.t
index 3a4b9dca8a..b25dbd238f 100644
--- a/dist/Storable/t/store.t
+++ b/dist/Storable/t/store.t
@@ -19,7 +19,7 @@ sub BEGIN {
 
 use Storable qw(store retrieve store_fd nstore_fd fd_retrieve);
 
-use Test::More tests => 24;
+use Test::More tests => 25;
 
 $a = 'toto';
 $b = \$a;
@@ -101,5 +101,15 @@ isnt($@, '');
     }
 }
 
+{
+
+    my $frozen =
+      "\x70\x73\x74\x30\x04\x0a\x08\x31\x32\x33\x34\x35\x36\x37\x38\x04\x08\x08\x08\x03\xff\x00\x00\x00\x19\x08\xff\x00\x00\x00\x08\x08\xf9\x16\x16\x13\x16\x10\x10\x10\xff\x15\x16\x16\x16\x1e\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x13\xf0\x16\x16\x16\xfe\x16\x41\x41\x41\x41\xe8\x03\x41\x41\x41\x41\x41\x41\x41\x41\x51\x41\xa9\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xb8\xac\xac\xac\xac\xac\xac\xac\xac\x9a\xac\xac\xac\xac\xac\xac\xac\xac\xac\x93\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\x00\x64\xac\xa8\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\xac\x2c\xac\x41\x41\x41\x41\x41\x41\x41\x41\x41\x00\x80\x41\x80\x41\x41\x41\x41\x41\x41\x51\x41\xac\xac\xac";
+    open my $fh, '<', \$frozen;
+    eval { Storable::fd_retrieve($fh); };
+    pass('RT 130635:  no stack smashing error when retrieving hook');
+
+}
+
 close OUT or die "Could not close: $!";
 END { 1 while unlink 'store' }
-- 
2.11.0

@p5pRT
Copy link
Author

p5pRT commented Jan 30, 2017

From @tonycoz

On Sat, 28 Jan 2017 16​:41​:50 -0800, john@​nixnuts.net wrote​:

On Thu, 2017-01-26 at 13​:48 -0800, James E Keenan via RT wrote​:

As previously reported, I configure with​:

"-des -Dusedevel -Duseithreads -Doptimize='-O2 -pipe -fstack-
protector -fno-
strict-aliasing' -DDEBUGGING"

... because that gets us very close to the way that the FreeBSD port
of perl
is configured.

Excellent, thanks.

The problem turned out to be that the AFL generated payload was
hitting two
other memory allocation errors before it even entered retrieve_hook().
That
combination of flags on FreeBSD seems to crash whenever Storable tries
to
allocate too much memory.

I adjusted the test data to use more realistic sizes when it enters
retrieve_hash() and retrieve_flag_hash() so that it's only focusing on
the stack
overflow in retrieve_hook().

I also cleaned up the test output formatting a bit.

An updated patch is attached.

Won't the allocated buffer leak if the load_module() fails, no STORABLE_thaw is defined, or if STORABLE_thaw dies?

Tony

@p5pRT
Copy link
Author

p5pRT commented Jan 30, 2017

From @lightsey

On Sun, 2017-01-29 at 21​:12 -0800, Tony Cook via RT wrote​:

On Sat, 28 Jan 2017 16​:41​:50 -0800, john@​nixnuts.net wrote​:

An updated patch is attached.

Won't the allocated buffer leak if the load_module() fails, no STORABLE_thaw
is defined, or if STORABLE_thaw dies?

Yes, Storable has several codepaths where it leaks memory or crashes while
allocating memory. I'd be happy to try and fix these problems more
systematically, but IMHO they are separate issues from the stack overflow.

@p5pRT
Copy link
Author

p5pRT commented Jan 30, 2017

From @jkeenan

On Sun, 29 Jan 2017 00​:41​:50 GMT, john@​nixnuts.net wrote​:

On Thu, 2017-01-26 at 13​:48 -0800, James E Keenan via RT wrote​:

As previously reported, I configure with​:

"-des -Dusedevel -Duseithreads -Doptimize='-O2 -pipe -fstack-
protector -fno-
strict-aliasing' -DDEBUGGING"

... because that gets us very close to the way that the FreeBSD port
of perl
is configured.

Excellent, thanks.

The problem turned out to be that the AFL generated payload was
hitting two
other memory allocation errors before it even entered retrieve_hook().
That
combination of flags on FreeBSD seems to crash whenever Storable tries
to
allocate too much memory.

I adjusted the test data to use more realistic sizes when it enters
retrieve_hash() and retrieve_flag_hash() so that it's only focusing on
the stack
overflow in retrieve_hook().

I also cleaned up the test output formatting a bit.

An updated patch is attached.

The third patch you submitted differs from the second patch submitted only in some test input data in t/store.t. You haven't changed any source code. Are you sure you're not simply fudging the test?

Thank you very much.

--
James E Keenan (jkeenan@​cpan.org)

@p5pRT
Copy link
Author

p5pRT commented Jan 30, 2017

From @lightsey

On Mon, 2017-01-30 at 09​:58 -0800, James E Keenan via RT wrote​:

On Sun, 29 Jan 2017 00​:41​:50 GMT, john@​nixnuts.net wrote​:

On Thu, 2017-01-26 at 13​:48 -0800, James E Keenan via RT wrote​:

As previously reported, I configure with​:

"-des -Dusedevel -Duseithreads -Doptimize='-O2 -pipe -fstack-
protector -fno-
strict-aliasing' -DDEBUGGING"

... because that gets us very close to the way that the FreeBSD port
of perl
is configured.

 
Excellent, thanks.
 
The problem turned out to be that the AFL generated payload was
hitting two
other memory allocation errors before it even entered retrieve_hook().
That
combination of flags on FreeBSD seems to crash whenever Storable tries
to
allocate too much memory.
 
I adjusted the test data to use more realistic sizes when it enters
retrieve_hash() and retrieve_flag_hash() so that it's only focusing on
the stack
overflow in retrieve_hook().
 
I also cleaned up the test output formatting a bit.
 
An updated patch is attached.

The third patch you submitted differs from the second patch submitted only in
some test input data in t/store.t.  You haven't changed any source code.  Are
you sure you're not simply fudging the test?

Yes, that's accurate. The original payload generated by AFL hit three different
errors​:

- memory allocation failure in retrieve_hash
- memory allocation failure in retrieve_flag_hash
- stack overflow in retrieve_hook

The retrieve_hash() crash, for instance, is essentially this code​:

RLEN(len);
hv = newHV();
hv_ksplit(hv, len + 1);

For some reason, FreeBSD with the flags you provided can't handle this when len
is too large to allocate.

My patch only fixes the stack overflow, so I changed the test payload to only
generate the stack overflow error.

@p5pRT
Copy link
Author

p5pRT commented Jan 30, 2017

From @tonycoz

On Mon, 30 Jan 2017 09​:33​:44 -0800, john@​nixnuts.net wrote​:

On Sun, 2017-01-29 at 21​:12 -0800, Tony Cook via RT wrote​:

On Sat, 28 Jan 2017 16​:41​:50 -0800, john@​nixnuts.net wrote​:

An updated patch is attached.

Won't the allocated buffer leak if the load_module() fails, no
STORABLE_thaw
is defined, or if STORABLE_thaw dies?

Yes, Storable has several codepaths where it leaks memory or crashes
while
allocating memory. I'd be happy to try and fix these problems more
systematically, but IMHO they are separate issues from the stack
overflow.

There's no need to add more leaks though.

You can use SAVEFREEPV() to free the allocated memory on scope exit.

Tony

@p5pRT
Copy link
Author

p5pRT commented Jan 30, 2017

From @lightsey

On Mon, 2017-01-30 at 14​:17 -0800, Tony Cook via RT wrote​:

On Mon, 30 Jan 2017 09​:33​:44 -0800, john@​nixnuts.net wrote​:

On Sun, 2017-01-29 at 21​:12 -0800, Tony Cook via RT wrote​:

On Sat, 28 Jan 2017 16​:41​:50 -0800, john@​nixnuts.net wrote​:

An updated patch is attached.

Won't the allocated buffer leak if the load_module() fails, no
STORABLE_thaw
is defined, or if STORABLE_thaw dies?

 
Yes, Storable has several codepaths where it leaks memory or crashes
while
allocating  memory. I'd be happy to try and fix these problems more
systematically, but IMHO they are separate issues from the stack
overflow.

There's no need to add more leaks though.

You can use SAVEFREEPV() to free the allocated memory on scope exit.

It's not clear to me what memory you believe my patch is leaking that wasn't
leaking in the original code.

The two assertions I added are before the matching calls to New().

Which variable have I created a new memory leak on?

@p5pRT
Copy link
Author

p5pRT commented Feb 6, 2017

From @tonycoz

On Mon, 30 Jan 2017 14​:53​:44 -0800, john@​nixnuts.net wrote​:

It's not clear to me what memory you believe my patch is leaking that wasn't
leaking in the original code.

The two assertions I added are before the matching calls to New().

Which variable have I created a new memory leak on?

You're right.

Thanks, I've applied your patch as 3e998dd.

I've fixed that specific leak in da1ec2b.

Tony

@p5pRT
Copy link
Author

p5pRT commented Feb 6, 2017

@tonycoz - Status changed from 'open' to 'pending release'

@p5pRT
Copy link
Author

p5pRT commented May 30, 2017

From @khwilliamson

Thank you for filing this report. You have helped make Perl better.

With the release today of Perl 5.26.0, this and 210 other issues have been
resolved.

Perl 5.26.0 may be downloaded via​:
https://metacpan.org/release/XSAWYERX/perl-5.26.0

If you find that the problem persists, feel free to reopen this ticket.

@p5pRT
Copy link
Author

p5pRT commented May 30, 2017

@khwilliamson - Status changed from 'pending release' to 'resolved'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant