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

AddressSanitizer: heap-use-after-free in Perl_pp_rv2sv #15893

Closed
p5pRT opened this issue Feb 25, 2017 · 12 comments
Closed

AddressSanitizer: heap-use-after-free in Perl_pp_rv2sv #15893

p5pRT opened this issue Feb 25, 2017 · 12 comments

Comments

@p5pRT
Copy link

p5pRT commented Feb 25, 2017

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

Searchable as RT130861$

@p5pRT
Copy link
Author

p5pRT commented Feb 25, 2017

From mtowalski@pentest.net.pl

Hello,

I've attached the poc and the asan log.
Tested on git version of perl.

Configure options​:

“./Configure -des -Dusedevel -DDEBUGGING -Dcc=clang -Doptimize=-O2 -Accflags="-fsanitize=address -fsanitize-coverage=edge" -Aldflags="-fsanitize=address -fsanitize-coverage=edge" -Alddlflags=-shared"

Information about configuration​:

Distributor ID​: Ubuntu
Description​: Ubuntu 16.10
Release​: 16.10
Codename​: yakkety
Arch​: x86_64

Best Regards,
Marcin T.

@p5pRT
Copy link
Author

p5pRT commented Feb 25, 2017

@p5pRT
Copy link
Author

p5pRT commented Feb 25, 2017

From mtowalski@pentest.net.pl

perl​: warning​: Setting locale failed.
perl​: warning​: Please check that your locale settings​:
  LANGUAGE = (unset),
  LC_ALL = (unset),
  LC_CTYPE = "UTF-8",
  LANG = "en_US.UTF-8"
  are supported and installed on your system.
perl​: warning​: Falling back to a fallback locale ("en_US.UTF-8").

==708==ERROR​: AddressSanitizer​: heap-use-after-free on address 0x619000009a78 at pc 0x000000a7c685 bp 0x7fff554408f0 sp 0x7fff554408e8
WRITE of size 8 at 0x619000009a78 thread T0
  #0 0xa7c684 in Perl_pp_rv2sv /home/mtowalski/Fuzzing/Programs/perl-git/pp.c​:406​:5
  #1 0x85d546 in Perl_runops_debug /home/mtowalski/Fuzzing/Programs/perl-git/dump.c​:2450​:23
  #2 0x5eaf07 in S_run_body /home/mtowalski/Fuzzing/Programs/perl-git/perl.c​:2524​:2
  #3 0x5eaf07 in perl_run /home/mtowalski/Fuzzing/Programs/perl-git/perl.c​:2447
  #4 0x503205 in main /home/mtowalski/Fuzzing/Programs/perl-git/perlmain.c​:123​:9
  #5 0x7fef44a433f0 in __libc_start_main /build/glibc-jxM2Ev/glibc-2.24/csu/../csu/libc-start.c​:291
  #6 0x433a89 in _start (/home/mtowalski/Fuzzing/Programs/perl-git/perl+0x433a89)

0x619000009a78 is located 1016 bytes inside of 1024-byte region [0x619000009680,0x619000009a80)
freed by thread T0 here​:
  #0 0x4d2410 in realloc (/home/mtowalski/Fuzzing/Programs/perl-git/perl+0x4d2410)
  #1 0x863fe8 in Perl_safesysrealloc /home/mtowalski/Fuzzing/Programs/perl-git/util.c​:274​:18

previously allocated by thread T0 here​:
  #0 0x4d2038 in malloc (/home/mtowalski/Fuzzing/Programs/perl-git/perl+0x4d2038)
  #1 0x863661 in Perl_safesysmalloc /home/mtowalski/Fuzzing/Programs/perl-git/util.c​:153​:21

SUMMARY​: AddressSanitizer​: heap-use-after-free /home/mtowalski/Fuzzing/Programs/perl-git/pp.c​:406​:5 in Perl_pp_rv2sv
Shadow bytes around the buggy address​:
  0x0c327fff92f0​: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c327fff9300​: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c327fff9310​: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c327fff9320​: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c327fff9330​: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x0c327fff9340​: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd[fd]
  0x0c327fff9350​: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c327fff9360​: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c327fff9370​: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c327fff9380​: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c327fff9390​: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes)​:
  Addressable​: 00
  Partially addressable​: 01 02 03 04 05 06 07
  Heap left redzone​: fa
  Heap right redzone​: fb
  Freed heap region​: fd
  Stack left redzone​: f1
  Stack mid redzone​: f2
  Stack right redzone​: f3
  Stack partial redzone​: f4
  Stack after return​: f5
  Stack use after scope​: f8
  Global redzone​: f9
  Global init order​: f6
  Poisoned by user​: f7
  Container overflow​: fc
  Array cookie​: ac
  Intra object redzone​: bb
  ASan internal​: fe
  Left alloca redzone​: ca
  Right alloca redzone​: cb
==708==ABORTING

@p5pRT
Copy link
Author

p5pRT commented Feb 26, 2017

From @arc

On 25 February 2017 at 23​:56, via RT <perl5-security-report@​perl.org> wrote​:

I've attached the poc and the asan log.

That asan log is​:

==708==ERROR​: AddressSanitizer​: heap-use-after-free on address
0x619000009a78 at pc 0x000000a7c685 bp 0x7fff554408f0 sp
0x7fff554408e8
WRITE of size 8 at 0x619000009a78 thread T0
  #0 0xa7c684 in Perl_pp_rv2sv
/home/mtowalski/Fuzzing/Programs/perl-git/pp.c​:406​:5
  #1 0x85d546 in Perl_runops_debug
/home/mtowalski/Fuzzing/Programs/perl-git/dump.c​:2450​:23
  #2 0x5eaf07 in S_run_body
/home/mtowalski/Fuzzing/Programs/perl-git/perl.c​:2524​:2
  #3 0x5eaf07 in perl_run
/home/mtowalski/Fuzzing/Programs/perl-git/perl.c​:2447
  #4 0x503205 in main
/home/mtowalski/Fuzzing/Programs/perl-git/perlmain.c​:123​:9
  #5 0x7fef44a433f0 in __libc_start_main
/build/glibc-jxM2Ev/glibc-2.24/csu/../csu/libc-start.c​:291
  #6 0x433a89 in _start
(/home/mtowalski/Fuzzing/Programs/perl-git/perl+0x433a89)

0x619000009a78 is located 1016 bytes inside of 1024-byte region
[0x619000009680,0x619000009a80)
freed by thread T0 here​:
  #0 0x4d2410 in realloc
(/home/mtowalski/Fuzzing/Programs/perl-git/perl+0x4d2410)
  #1 0x863fe8 in Perl_safesysrealloc
/home/mtowalski/Fuzzing/Programs/perl-git/util.c​:274​:18

previously allocated by thread T0 here​:
  #0 0x4d2038 in malloc
(/home/mtowalski/Fuzzing/Programs/perl-git/perl+0x4d2038)
  #1 0x863661 in Perl_safesysmalloc
/home/mtowalski/Fuzzing/Programs/perl-git/util.c​:153​:21

which isn't terribly helpful. However, I get something much more
detailed and interesting on Mac OS​:

==75998==ERROR​: AddressSanitizer​: heap-use-after-free on address
0x619000007c70 at pc 0x0001059f5ffa bp 0x7fff5a6bbe30 sp
0x7fff5a6bbe28
WRITE of size 8 at 0x619000007c70 thread T0
  #0 0x1059f5ff9 in Perl_pp_rv2sv (perl+0x1004b2ff9)
  #1 0x1058f5e02 in Perl_runops_standard (perl+0x1003b2e02)
  #2 0x1056235e9 in perl_run (perl+0x1000e05e9)
  #3 0x105543bcf in main (perl+0x100000bcf)
  #4 0x7fff924075fc in start (libdyld.dylib+0x35fc)
  #5 0x2 (<unknown module>)

0x619000007c70 is located 1008 bytes inside of 1024-byte region
[0x619000007880,0x619000007c80)
freed by thread T0 here​:
  #0 0x105f18b07 in wrap_realloc (libclang_rt.asan_osx_dynamic.dylib+0x46b07)
  #1 0x1058359db in Perl_safesysrealloc (perl+0x1002f29db)
  #2 0x1058e60f7 in Perl_av_extend_guts (perl+0x1003a30f7)
  #3 0x1058e5394 in Perl_av_extend (perl+0x1003a2394)
  #4 0x105a75877 in Perl_stack_grow (perl+0x100532877)
  #5 0x105625b98 in Perl_call_sv (perl+0x1000e2b98)
  #6 0x105669c92 in S_require_tie_mod (perl+0x100126c92)
  #7 0x10564bca0 in Perl_gv_fetchpvn_flags (perl+0x100108ca0)
  #8 0x10564f252 in Perl_gv_fetchsv (perl+0x10010c252)
  #9 0x1059f4ecb in Perl_softref2xv (perl+0x1004b1ecb)
  #10 0x1059f5767 in Perl_pp_rv2sv (perl+0x1004b2767)
  #11 0x1058f5e02 in Perl_runops_standard (perl+0x1003b2e02)
  #12 0x1056235e9 in perl_run (perl+0x1000e05e9)
  #13 0x105543bcf in main (perl+0x100000bcf)
  #14 0x7fff924075fc in start (libdyld.dylib+0x35fc)
  #15 0x2 (<unknown module>)

previously allocated by thread T0 here​:
  #0 0x105f18770 in wrap_malloc (libclang_rt.asan_osx_dynamic.dylib+0x46770)
  #1 0x105835611 in Perl_safesysmalloc (perl+0x1002f2611)
  #2 0x1058e59f9 in Perl_av_extend_guts (perl+0x1003a29f9)
  #3 0x1058e5394 in Perl_av_extend (perl+0x1003a2394)
  #4 0x105a759bf in Perl_new_stackinfo (perl+0x1005329bf)
  #5 0x105608489 in perl_construct (perl+0x1000c5489)
  #6 0x105543b1c in main (perl+0x100000b1c)
  #7 0x7fff924075fc in start (libdyld.dylib+0x35fc)
  #8 0x2 (<unknown module>)

That is​: the freed memory was allocated by Perl_new_stackinfo, and
freed by Perl_stack_grow, which suggests strongly to me that something
has extended the stack, and that pp_rv2sv is trying to write through
the old stack pointer.

The attached patch fixes this for me, and includes a slightly reduced
version of the failing program as a test case, but I'd like at least
one more pair of eyes on it, because I don't have a detailed analysis
of what causes the crash. (That also means I don't have a sense for
how easy it would be for an attacker to trigger this.)

Also, if someone can find a better reduction, that'd be great; I found
that most of the changes I made to the large string literal in the
failing program concealed the error. In particular, the failing
program ends up dereferencing the string "[", which loads arybase,
which means that miniperl can't reproduce the error. Indeed, the
presence of S_require_tie_mod in asan's "freed by" suggests that it's
perhaps loading arybase that triggers the stack reallocation.

--
Aaron Crane ** http​://aaroncrane.co.uk/

@p5pRT
Copy link
Author

p5pRT commented Feb 26, 2017

From @arc

0001-pp_rv2sv-SPAGAIN-after-doing-things-that-could-exten.patch
From 3952d952ed32364a95c9bdd8fd28c2ecd986f999 Mon Sep 17 00:00:00 2001
From: Aaron Crane <arc@cpan.org>
Date: Sun, 26 Feb 2017 11:36:17 +0000
Subject: [PATCH] pp_rv2sv: SPAGAIN after doing things that could extend the
 stack

---
 pp.c       |  1 +
 t/op/ref.t | 14 +++++++++++++-
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/pp.c b/pp.c
index 62316fc8b4..833dabb70a 100644
--- a/pp.c
+++ b/pp.c
@@ -403,6 +403,7 @@ PP(pp_rv2sv)
 	else if (PL_op->op_private & OPpDEREF)
 	    sv = vivify_ref(sv, PL_op->op_private & OPpDEREF);
     }
+    SPAGAIN;           /* in case chasing soft refs reallocated the stack */
     SETs(sv);
     RETURN;
 }
diff --git a/t/op/ref.t b/t/op/ref.t
index 65d50b67a2..0ab9883d3e 100644
--- a/t/op/ref.t
+++ b/t/op/ref.t
@@ -8,7 +8,7 @@ BEGIN {
 
 use strict qw(refs subs);
 
-plan(236);
+plan(237);
 
 # Test this first before we extend the stack with other operations.
 # This caused an asan failure due to a bad write past the end of the stack.
@@ -820,6 +820,18 @@ for ("4eounthouonth") {
 	'[perl #109746] referential identity of \literal under threads+mad'
 }
 
+# RT#130861: heap-use-after-free in pp_rv2sv, from asan fuzzing
+SKIP: {
+    skip_if_miniperl("no dynamic loading on miniperl, so can't load arybase", 1);
+    my $code = <<'EOF';
+@q = "-1ocT\xC2\xA8gZZZZZZZZZZZZZZZZZZZZZZZZZZZYT`gZYT`gZYT`gZYT`gZYT`gZYT`gZYT`gZY\xD4`cV\xC2\\m;(\\d+|.);g;
+\xC2\xA8xcV\\\xC2\xA8`ge\$_[0.07\x9E\xF6\xD8\xFCF\xE6U\xFA\x16\xF9E}VVVVVVV"=~m;(\d+|.);g;
+() = map $$$$$$_, @q;
+EOF
+    fresh_perl_like($code, qr/^Not a SCALAR reference /, { stderr => 1 },
+                    'rt#130861: heap uaf in pp_rv2sv');
+}
+
 # Bit of a hack to make test.pl happy. There are 3 more tests after it leaves.
 $test = curr_test();
 curr_test($test + 3);
-- 
2.11.0

@p5pRT
Copy link
Author

p5pRT commented Feb 26, 2017

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

@p5pRT
Copy link
Author

p5pRT commented Feb 27, 2017

From @tonycoz

On Sun, 26 Feb 2017 04​:13​:44 -0800, arc wrote​:

On 25 February 2017 at 23​:56, via RT <perl5-security-report@​perl.org>
wrote​:

I've attached the poc and the asan log.

That asan log is​:

==708==ERROR​: AddressSanitizer​: heap-use-after-free on address
0x619000009a78 at pc 0x000000a7c685 bp 0x7fff554408f0 sp
0x7fff554408e8
WRITE of size 8 at 0x619000009a78 thread T0
#0 0xa7c684 in Perl_pp_rv2sv
/home/mtowalski/Fuzzing/Programs/perl-git/pp.c​:406​:5
#1 0x85d546 in Perl_runops_debug
/home/mtowalski/Fuzzing/Programs/perl-git/dump.c​:2450​:23
#2 0x5eaf07 in S_run_body
/home/mtowalski/Fuzzing/Programs/perl-git/perl.c​:2524​:2
#3 0x5eaf07 in perl_run
/home/mtowalski/Fuzzing/Programs/perl-git/perl.c​:2447
#4 0x503205 in main
/home/mtowalski/Fuzzing/Programs/perl-git/perlmain.c​:123​:9
#5 0x7fef44a433f0 in __libc_start_main
/build/glibc-jxM2Ev/glibc-2.24/csu/../csu/libc-start.c​:291
#6 0x433a89 in _start
(/home/mtowalski/Fuzzing/Programs/perl-git/perl+0x433a89)

0x619000009a78 is located 1016 bytes inside of 1024-byte region
[0x619000009680,0x619000009a80)
freed by thread T0 here​:
#0 0x4d2410 in realloc
(/home/mtowalski/Fuzzing/Programs/perl-git/perl+0x4d2410)
#1 0x863fe8 in Perl_safesysrealloc
/home/mtowalski/Fuzzing/Programs/perl-git/util.c​:274​:18

previously allocated by thread T0 here​:
#0 0x4d2038 in malloc
(/home/mtowalski/Fuzzing/Programs/perl-git/perl+0x4d2038)
#1 0x863661 in Perl_safesysmalloc
/home/mtowalski/Fuzzing/Programs/perl-git/util.c​:153​:21

which isn't terribly helpful. However, I get something much more
detailed and interesting on Mac OS​:

==75998==ERROR​: AddressSanitizer​: heap-use-after-free on address
0x619000007c70 at pc 0x0001059f5ffa bp 0x7fff5a6bbe30 sp
0x7fff5a6bbe28
WRITE of size 8 at 0x619000007c70 thread T0
#0 0x1059f5ff9 in Perl_pp_rv2sv (perl+0x1004b2ff9)
#1 0x1058f5e02 in Perl_runops_standard (perl+0x1003b2e02)
#2 0x1056235e9 in perl_run (perl+0x1000e05e9)
#3 0x105543bcf in main (perl+0x100000bcf)
#4 0x7fff924075fc in start (libdyld.dylib+0x35fc)
#5 0x2 (<unknown module>)

0x619000007c70 is located 1008 bytes inside of 1024-byte region
[0x619000007880,0x619000007c80)
freed by thread T0 here​:
#0 0x105f18b07 in wrap_realloc
(libclang_rt.asan_osx_dynamic.dylib+0x46b07)
#1 0x1058359db in Perl_safesysrealloc (perl+0x1002f29db)
#2 0x1058e60f7 in Perl_av_extend_guts (perl+0x1003a30f7)
#3 0x1058e5394 in Perl_av_extend (perl+0x1003a2394)
#4 0x105a75877 in Perl_stack_grow (perl+0x100532877)
#5 0x105625b98 in Perl_call_sv (perl+0x1000e2b98)
#6 0x105669c92 in S_require_tie_mod (perl+0x100126c92)
#7 0x10564bca0 in Perl_gv_fetchpvn_flags (perl+0x100108ca0)
#8 0x10564f252 in Perl_gv_fetchsv (perl+0x10010c252)
#9 0x1059f4ecb in Perl_softref2xv (perl+0x1004b1ecb)
#10 0x1059f5767 in Perl_pp_rv2sv (perl+0x1004b2767)
#11 0x1058f5e02 in Perl_runops_standard (perl+0x1003b2e02)
#12 0x1056235e9 in perl_run (perl+0x1000e05e9)
#13 0x105543bcf in main (perl+0x100000bcf)
#14 0x7fff924075fc in start (libdyld.dylib+0x35fc)
#15 0x2 (<unknown module>)

previously allocated by thread T0 here​:
#0 0x105f18770 in wrap_malloc
(libclang_rt.asan_osx_dynamic.dylib+0x46770)
#1 0x105835611 in Perl_safesysmalloc (perl+0x1002f2611)
#2 0x1058e59f9 in Perl_av_extend_guts (perl+0x1003a29f9)
#3 0x1058e5394 in Perl_av_extend (perl+0x1003a2394)
#4 0x105a759bf in Perl_new_stackinfo (perl+0x1005329bf)
#5 0x105608489 in perl_construct (perl+0x1000c5489)
#6 0x105543b1c in main (perl+0x100000b1c)
#7 0x7fff924075fc in start (libdyld.dylib+0x35fc)
#8 0x2 (<unknown module>)

That is​: the freed memory was allocated by Perl_new_stackinfo, and
freed by Perl_stack_grow, which suggests strongly to me that something
has extended the stack, and that pp_rv2sv is trying to write through
the old stack pointer.

The attached patch fixes this for me, and includes a slightly reduced
version of the failing program as a test case, but I'd like at least
one more pair of eyes on it, because I don't have a detailed analysis
of what causes the crash. (That also means I don't have a sense for
how easy it would be for an attacker to trigger this.)

Also, if someone can find a better reduction, that'd be great; I found
that most of the changes I made to the large string literal in the
failing program concealed the error. In particular, the failing
program ends up dereferencing the string "[", which loads arybase,
which means that miniperl can't reproduce the error. Indeed, the
presence of S_require_tie_mod in asan's "freed by" suggests that it's
perhaps loading arybase that triggers the stack reallocation.

Here's a slightly different minification.

Changes to the literal that preserve the number of matches against the regexp were safe, so compressing sequences of digits, replacing non-ASCII characters and removing the new lines from the literal worked for me.

Changing the literal to a generated value ("1" . ("x" x 123)) didn't reproduce the issue.

WRT arybase, perhaps require_tie_mod() needs to work with a new stack, perhaps gv_fetchpvn_flags() reallocating the stack is a bug.

Tony

@p5pRT
Copy link
Author

p5pRT commented Feb 27, 2017

From @tonycoz

130861b.pl

@p5pRT
Copy link
Author

p5pRT commented Mar 14, 2017

From @iabyn

On Sun, Feb 26, 2017 at 08​:22​:01PM -0800, Tony Cook via RT wrote​:

On Sun, 26 Feb 2017 04​:13​:44 -0800, arc wrote​:

On 25 February 2017 at 23​:56, via RT <perl5-security-report@​perl.org>
That is​: the freed memory was allocated by Perl_new_stackinfo, and
freed by Perl_stack_grow, which suggests strongly to me that something
has extended the stack, and that pp_rv2sv is trying to write through
the old stack pointer.

The attached patch fixes this for me, and includes a slightly reduced
version of the failing program as a test case, but I'd like at least
one more pair of eyes on it, because I don't have a detailed analysis
of what causes the crash. (That also means I don't have a sense for
how easy it would be for an attacker to trigger this.)

Also, if someone can find a better reduction, that'd be great; I found
that most of the changes I made to the large string literal in the
failing program concealed the error. In particular, the failing
program ends up dereferencing the string "[", which loads arybase,
which means that miniperl can't reproduce the error. Indeed, the
presence of S_require_tie_mod in asan's "freed by" suggests that it's
perhaps loading arybase that triggers the stack reallocation.

Here's a slightly different minification.

Changes to the literal that preserve the number of matches against the regexp were safe, so compressing sequences of digits, replacing non-ASCII characters and removing the new lines from the literal worked for me.

Changing the literal to a generated value ("1" . ("x" x 123)) didn't reproduce the issue.

WRT arybase, perhaps require_tie_mod() needs to work with a new stack, perhaps gv_fetchpvn_flags() reallocating the stack is a bug.

I attach an alternative patch which I suggest should supersede Aaron's one.
It has a much simplified test case, and adds a SAVESTACKi() to
S_require_tie_mod() in addition to the SPAGAIN in pp_rv2sv.

I don't think it really counts as a security issue​: it involves
using one of - + ! [ as a run-time symbolic reference, at a point where
the stack is almost exactly full.

So I think this ticket should be moved to the public queue soonish, then
my patch should be applied to blead post-5.26 release.

--
Dave's first rule of Opera​:
If something needs saying, say it​: don't warble it.

@p5pRT
Copy link
Author

p5pRT commented Mar 14, 2017

From @iabyn

0001-S_require_tie_mod-use-a-new-stack.patch
>From cd763c7f0f5abd08207c7852a2cba1c24d2904b8 Mon Sep 17 00:00:00 2001
From: David Mitchell <davem@iabyn.com>
Date: Tue, 14 Mar 2017 09:19:15 +0000
Subject: [PATCH] S_require_tie_mod(): use a new stack

RT #130861

This function is used to load a module associated with various magic vars,
like $[ and %+. Since it can be called 'unexpectedly', it should use a new
stack. The issue in this ticket was equivalent to

    my $var = '[';
    $$var;

where the symbolic dereference triggered a run-time load of arybase.pm,
which grew the stack, invalidating the SP in pp_rv2sv.

Note that most of the stuff which S_require_tie_mod() calls, such as
load_module(), will do its own PUSHSTACK(); but S_require_tie_mod() also
does a bit of stack manipulation itself.

The test case includes a magic number, 125, which happens to be the exact
size necessary to trigger a stack realloc in S_require_tie_mod(). In later
perl versions this value may well change. But it seemed too expensive
to call fresh_perl_is() 100's of times with different values of $n.

This commit also adds a SPAGAIN to pp_rv2sv on the 'belt and braces'
principle.

This commit is based on an earlier effort by Aaron Crane.
---
 gv.c       |  2 ++
 pp.c       |  1 +
 t/op/ref.t | 20 +++++++++++++++++++-
 3 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/gv.c b/gv.c
index d32a9c5..581211f 100644
--- a/gv.c
+++ b/gv.c
@@ -1338,6 +1338,7 @@ S_require_tie_mod(pTHX_ GV *gv, const char varname, const char * name,
       GV **gvp;
       dSP;
 
+      PUSHSTACKi(PERLSI_MAGIC);
       ENTER;
 
 #define HV_FETCH_TIE_FUNC (GV **)hv_fetchs(stash, "_tie_it", 0)
@@ -1367,6 +1368,7 @@ S_require_tie_mod(pTHX_ GV *gv, const char varname, const char * name,
       PUTBACK;
       call_sv((SV *)*gvp, G_VOID|G_DISCARD);
       LEAVE;
+      POPSTACK;
     }
 }
 
diff --git a/pp.c b/pp.c
index a640995..1275969 100644
--- a/pp.c
+++ b/pp.c
@@ -403,6 +403,7 @@ PP(pp_rv2sv)
 	else if (PL_op->op_private & OPpDEREF)
 	    sv = vivify_ref(sv, PL_op->op_private & OPpDEREF);
     }
+    SPAGAIN; /* in case chasing soft refs reallocated the stack */
     SETs(sv);
     RETURN;
 }
diff --git a/t/op/ref.t b/t/op/ref.t
index 65d50b6..44047ae 100644
--- a/t/op/ref.t
+++ b/t/op/ref.t
@@ -8,7 +8,7 @@ BEGIN {
 
 use strict qw(refs subs);
 
-plan(236);
+plan(237);
 
 # Test this first before we extend the stack with other operations.
 # This caused an asan failure due to a bad write past the end of the stack.
@@ -820,6 +820,24 @@ for ("4eounthouonth") {
 	'[perl #109746] referential identity of \literal under threads+mad'
 }
 
+# RT#130861: heap-use-after-free in pp_rv2sv, from asan fuzzing
+SKIP: {
+    skip_if_miniperl("no dynamic loading on miniperl, so can't load arybase", 1);
+    # this value is critical - its just enough so that the stack gets
+    # grown which loading/calling arybase
+    my $n = 125;
+
+    my $code = <<'EOF';
+$ary = '[';
+my @a = map $$ary, 1..NNN;
+print "@a\n";
+EOF
+    $code =~ s/NNN/$n/g;
+    my @exp = ("0") x $n;
+    fresh_perl_is($code, "@exp", { stderr => 1 },
+                    'rt#130861: heap uaf in pp_rv2sv');
+}
+
 # Bit of a hack to make test.pl happy. There are 3 more tests after it leaves.
 $test = curr_test();
 curr_test($test + 3);
-- 
2.4.11

@p5pRT
Copy link
Author

p5pRT commented Jun 5, 2017

From @iabyn

On Tue, Mar 14, 2017 at 09​:40​:54AM +0000, Dave Mitchell wrote​:

On Sun, Feb 26, 2017 at 08​:22​:01PM -0800, Tony Cook via RT wrote​:

On Sun, 26 Feb 2017 04​:13​:44 -0800, arc wrote​:

On 25 February 2017 at 23​:56, via RT <perl5-security-report@​perl.org>
That is​: the freed memory was allocated by Perl_new_stackinfo, and
freed by Perl_stack_grow, which suggests strongly to me that something
has extended the stack, and that pp_rv2sv is trying to write through
the old stack pointer.

The attached patch fixes this for me, and includes a slightly reduced
version of the failing program as a test case, but I'd like at least
one more pair of eyes on it, because I don't have a detailed analysis
of what causes the crash. (That also means I don't have a sense for
how easy it would be for an attacker to trigger this.)

Also, if someone can find a better reduction, that'd be great; I found
that most of the changes I made to the large string literal in the
failing program concealed the error. In particular, the failing
program ends up dereferencing the string "[", which loads arybase,
which means that miniperl can't reproduce the error. Indeed, the
presence of S_require_tie_mod in asan's "freed by" suggests that it's
perhaps loading arybase that triggers the stack reallocation.

Here's a slightly different minification.

Changes to the literal that preserve the number of matches against the regexp were safe, so compressing sequences of digits, replacing non-ASCII characters and removing the new lines from the literal worked for me.

Changing the literal to a generated value ("1" . ("x" x 123)) didn't reproduce the issue.

WRT arybase, perhaps require_tie_mod() needs to work with a new stack, perhaps gv_fetchpvn_flags() reallocating the stack is a bug.

I attach an alternative patch which I suggest should supersede Aaron's one.
It has a much simplified test case, and adds a SAVESTACKi() to
S_require_tie_mod() in addition to the SPAGAIN in pp_rv2sv.

I don't think it really counts as a security issue​: it involves
using one of - + ! [ as a run-time symbolic reference, at a point where
the stack is almost exactly full.

So I think this ticket should be moved to the public queue soonish, then
my patch should be applied to blead post-5.26 release.

Now pushed as v5.27.0-118-g655f5b2. I'll move this ticket to the public
queue now.

--
Wesley Crusher gets beaten up by his classmates for being a smarmy git,
and consequently has a go at making some friends of his own age for a
change.
  -- Things That Never Happen in "Star Trek" #18

@p5pRT p5pRT closed this as completed Jun 5, 2017
@p5pRT
Copy link
Author

p5pRT commented Jun 5, 2017

@iabyn - Status changed from 'open' to 'resolved'

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

No branches or pull requests

1 participant