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

heap uaf in Perl_yylex() - toke.c:5143 #15878

Closed
p5pRT opened this issue Feb 19, 2017 · 21 comments
Closed

heap uaf in Perl_yylex() - toke.c:5143 #15878

p5pRT opened this issue Feb 19, 2017 · 21 comments

Comments

@p5pRT
Copy link

p5pRT commented Feb 19, 2017

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

Searchable as RT130814$

@p5pRT
Copy link
Author

p5pRT commented Feb 19, 2017

From @geeknik

Triggered with Perl v5.25.10 (v5.25.9-154-gd8f2fe0) while fuzzing with AFL.
READ of size 1, most likely not exploitable in any sort of meaningful way.
afl-tmin minimized the testcase to 8200B.

==30048==ERROR​: AddressSanitizer​: heap-use-after-free on address
0x62500000790b at pc 0x00000065fac5 bp 0x7ffd7d3312f0 sp 0x7ffd7d3312e8
READ of size 1 at 0x62500000790b thread T0
  #0 0x65fac4 in Perl_yylex /home/geeknik/perl/toke.c​:5143​:5
  #1 0x6b8307 in Perl_yyparse /home/geeknik/perl/perly.c​:340​:34
  #2 0x59d611 in S_parse_body /home/geeknik/perl/perl.c​:2376​:9
  #3 0x593890 in perl_parse /home/geeknik/perl/perl.c​:1691​:2
  #4 0x4dea05 in main /home/geeknik/perl/perlmain.c​:121​:18
  #5 0x7f5b2845ab44 in __libc_start_main
/build/glibc-qK83Be/glibc-2.19/csu/libc-start.c​:287
  #6 0x4de69c in _start (/home/geeknik/perl/perl+0x4de69c)

0x62500000790b is located 11 bytes inside of 8200-byte region
[0x625000007900,0x625000009908)
freed by thread T0 here​:
  #0 0x4c130e in realloc (/home/geeknik/perl/perl+0x4c130e)
  #1 0x8056f6 in Perl_safesysrealloc /home/geeknik/perl/util.c​:274​:18

previously allocated by thread T0 here​:
  #0 0x4c130e in realloc (/home/geeknik/perl/perl+0x4c130e)
  #1 0x8056f6 in Perl_safesysrealloc /home/geeknik/perl/util.c​:274​:18

SUMMARY​: AddressSanitizer​: heap-use-after-free
/home/geeknik/perl/toke.c​:5143 Perl_yylex
Shadow bytes around the buggy address​:
  0x0c4a7fff8ed0​: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c4a7fff8ee0​: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c4a7fff8ef0​: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c4a7fff8f00​: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c4a7fff8f10​: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c4a7fff8f20​: fd[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c4a7fff8f30​: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c4a7fff8f40​: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c4a7fff8f50​: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c4a7fff8f60​: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c4a7fff8f70​: 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
  ASan internal​: fe
==30048==ABORTING

@p5pRT
Copy link
Author

p5pRT commented Feb 19, 2017

From @geeknik

test000.gz

@p5pRT
Copy link
Author

p5pRT commented Feb 19, 2017

From @hvds

On Sat, 18 Feb 2017 22​:20​:53 -0800, brian.carpenter@​gmail.com wrote​:

Triggered with Perl v5.25.10 (v5.25.9-154-gd8f2fe0) while fuzzing with AFL.
READ of size 1, most likely not exploitable in any sort of meaningful way.
afl-tmin minimized the testcase to 8200B.

==30048==ERROR​: AddressSanitizer​: heap-use-after-free on address
0x62500000790b at pc 0x00000065fac5 bp 0x7ffd7d3312f0 sp 0x7ffd7d3312e8
READ of size 1 at 0x62500000790b thread T0
#0 0x65fac4 in Perl_yylex /home/geeknik/perl/toke.c​:5143​:5
#1 0x6b8307 in Perl_yyparse /home/geeknik/perl/perly.c​:340​:34
#2 0x59d611 in S_parse_body /home/geeknik/perl/perl.c​:2376​:9
#3 0x593890 in perl_parse /home/geeknik/perl/perl.c​:1691​:2
#4 0x4dea05 in main /home/geeknik/perl/perlmain.c​:121​:18
#5 0x7f5b2845ab44 in __libc_start_main

Short-form reproducer is​:
perl -we 'print " foreach m0\n\$" . ("0" x 0x2000)' | valgrind ./miniperl

The critical part of the stacktrace for the realloc is​:
==4675== by 0x47A41E​: S_skipspace_flags (toke.c​:1897)
==4675== by 0x4A08A7​: Perl_yylex (toke.c​:7930)

There are multiple opportunities for PL_lineptr to get realloced while we're checking for the "Missing $ on loop variable" diagnostic, and we need to update 's' in that case before it is used by the OPERATOR() macro.

I have a fix for this, just putting a test together.

Hugo

@p5pRT
Copy link
Author

p5pRT commented Feb 19, 2017

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

@p5pRT
Copy link
Author

p5pRT commented Feb 19, 2017

From @hvds

On Sun, 19 Feb 2017 03​:04​:21 -0800, hv wrote​:

I have a fix for this, just putting a test together.

Does the below seem like a sensible place to add the test? If so, I'll apply the patches attached after checking a couple of test runs.

Hugo

Inline Patch
diff --git a/t/comp/parser_run.t b/t/comp/parser_run.t
new file mode 100644
index 0000000..2543f49
--- /dev/null
+++ b/t/comp/parser_run.t
@@ -0,0 +1,28 @@
+#!./perl
+
+# Parser tests that want test.pl, eg to use runperl() for tests to show
+# reads through invalid pointers.
+# Note that this should still be runnable under miniperl.
[...]

@p5pRT
Copy link
Author

p5pRT commented Feb 19, 2017

From @hvds

0001-perl-130814-Add-testcase-and-new-testfile-t-comp-par.patch
From ec0e84fc5b799950ae441739c1ed0ae3ead07185 Mon Sep 17 00:00:00 2001
From: Hugo van der Sanden <hv@crypt.org>
Date: Sun, 19 Feb 2017 11:15:38 +0000
Subject: [PATCH 1/2] [perl #130814] Add testcase, and new testfile
 t/comp/parser_run.t

Sometimes it's useful to have test.pl around, but it seems inappropriate
to pollute the existing t/comp/parser.t with that.
---
 t/comp/parser_run.t | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 t/comp/parser_run.t

diff --git a/t/comp/parser_run.t b/t/comp/parser_run.t
new file mode 100644
index 0000000..2543f49
--- /dev/null
+++ b/t/comp/parser_run.t
@@ -0,0 +1,28 @@
+#!./perl
+
+# Parser tests that want test.pl, eg to use runperl() for tests to show
+# reads through invalid pointers.
+# Note that this should still be runnable under miniperl.
+
+BEGIN {
+    @INC = qw(. ../lib );
+    chdir 't' if -d 't';
+}
+
+require './test.pl';
+plan(1);
+
+# [perl #130814] can reallocate lineptr while looking ahead for
+# "Missing $ on loop variable" diagnostic.
+my $result = runperl(
+    prog => " foreach m0\n\$" . ("0" x 0x2000),
+    stderr => 1,
+);
+is($result, <<EXPECT);
+syntax error at -e line 3, near "foreach m0
+"
+Identifier too long at -e line 3.
+EXPECT
+
+__END__
+# ex: set ts=8 sts=4 sw=4 et:
-- 
2.10.2

@p5pRT
Copy link
Author

p5pRT commented Feb 19, 2017

From @hvds

0002-perl-130814-update-pointer-into-PL_linestr-after-loo.patch
From 4f0e3ba399282a4329994bbff5c4fa75b0a961d0 Mon Sep 17 00:00:00 2001
From: Hugo van der Sanden <hv@crypt.org>
Date: Sun, 19 Feb 2017 10:46:09 +0000
Subject: [PATCH 2/2] [perl #130814] update pointer into PL_linestr after
 lookahead

Looking ahead for the "Missing $ on loop variable" diagnostic can reallocate
PL_linestr, invalidating our pointer. Save the offset so we can update it
in that case.
---
 toke.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/toke.c b/toke.c
index 5a711d3..cf3163e 100644
--- a/toke.c
+++ b/toke.c
@@ -7913,6 +7913,7 @@ Perl_yylex(pTHX)
                 && isIDFIRST_lazy_if_safe(s, PL_bufend, UTF))
             {
 		char *p = s;
+                SSize_t s_off = s - SvPVX(PL_linestr);
 
 		if ((PL_bufend - p) >= 3
                     && strEQs(p, "my") && isSPACE(*(p + 2)))
@@ -7930,6 +7931,9 @@ Perl_yylex(pTHX)
 		}
 		if (*p != '$' && *p != '\\')
 		    Perl_croak(aTHX_ "Missing $ on loop variable");
+
+                /* The buffer may have been reallocated, update s */
+                s = SvPVX(PL_linestr) + s_off;
 	    }
 	    OPERATOR(FOR);
 
-- 
2.10.2

@p5pRT
Copy link
Author

p5pRT commented Feb 21, 2017

From @hvds

On Sun, 19 Feb 2017 03​:22​:29 -0800, hv wrote​:

On Sun, 19 Feb 2017 03​:04​:21 -0800, hv wrote​:

I have a fix for this, just putting a test together.

Does the below seem like a sensible place to add the test? If so, I'll
apply the patches attached after checking a couple of test runs.

Silence has implied consent, now pushed as​:

d7186ad [perl #130814] Add testcase, and new testfile t/comp/parser_run.t
90f2cc9 [perl #130814] update pointer into PL_linestr after lookahead

Hugo

@p5pRT
Copy link
Author

p5pRT commented Feb 21, 2017

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

@p5pRT
Copy link
Author

p5pRT commented Feb 21, 2017

From @jkeenan

On Tue, 21 Feb 2017 12​:00​:55 GMT, hv wrote​:

On Sun, 19 Feb 2017 03​:22​:29 -0800, hv wrote​:

On Sun, 19 Feb 2017 03​:04​:21 -0800, hv wrote​:

I have a fix for this, just putting a test together.

Does the below seem like a sensible place to add the test? If so, I'll
apply the patches attached after checking a couple of test runs.

Silence has implied consent, now pushed as​:

d7186ad [perl #130814] Add testcase, and new testfile t/comp/parser_run.t
90f2cc9 [perl #130814] update pointer into PL_linestr after lookahead

Hugo

The humans were silent but the machines were not.

In http​://perl5.test-smoke.org/report/54176, we got​:

#####
MANIFEST messages​:
  MANIFEST did not declare 't/comp/parser_run.t'
#####

I manually added t/comp/parser_run.t to MANIFEST and ran 'make test_porting'. I then got failures in t/porting/test_bootstrap.t. These I corrected in​:

#####
commit 36aa5d0
Author​: James E Keenan <jkeenan@​cpan.org>
AuthorDate​: Tue Feb 21 10​:16​:37 2017
Commit​: James E Keenan <jkeenan@​cpan.org>
CommitDate​: Tue Feb 21 10​:16​:37 2017
#####

Please review.

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

@p5pRT
Copy link
Author

p5pRT commented Feb 21, 2017

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

@p5pRT
Copy link
Author

p5pRT commented Feb 21, 2017

From @hvds

On Tue, 21 Feb 2017 07​:43​:53 -0800, jkeenan wrote​:

On Tue, 21 Feb 2017 12​:00​:55 GMT, hv wrote​:

On Sun, 19 Feb 2017 03​:22​:29 -0800, hv wrote​:
Silence has implied consent, now pushed as​:

d7186ad [perl #130814] Add testcase, and new testfile t/comp/parser_run.t
90f2cc9 [perl #130814] update pointer into PL_linestr after lookahead

The humans were silent but the machines were not.

In http​://perl5.test-smoke.org/report/54176, we got​:

#####
MANIFEST messages​:
MANIFEST did not declare 't/comp/parser_run.t'
#####

I manually added t/comp/parser_run.t to MANIFEST and ran 'make
test_porting'. I then got failures in t/porting/test_bootstrap.t.
These I corrected in​:

#####
commit 36aa5d0
Author​: James E Keenan <jkeenan@​cpan.org>
AuthorDate​: Tue Feb 21 10​:16​:37 2017
Commit​: James E Keenan <jkeenan@​cpan.org>
CommitDate​: Tue Feb 21 10​:16​:37 2017
#####

Please review.

Thanks, I saw this on the smoke results but you got in there quicker than I could. Your changes look good to me; I've additionally tweaked the comment about exceptions in test_bootstrap.

Hugo

@p5pRT
Copy link
Author

p5pRT commented Feb 24, 2017

From @craigberry

On Sun, Feb 19, 2017 at 5​:22 AM, Hugo van der Sanden via RT
<perlbug-followup@​perl.org> wrote​:

On Sun, 19 Feb 2017 03​:04​:21 -0800, hv wrote​:

I have a fix for this, just putting a test together.

Does the below seem like a sensible place to add the test? If so, I'll apply the patches attached after checking a couple of test runs.

+# [perl #130814] can reallocate lineptr while looking ahead for
+# "Missing $ on loop variable" diagnostic.
+my $result = runperl(
+ prog => " foreach m0\n\$" . ("0" x 0x2000),
+ stderr => 1,
+);

How long does that token need to be to exercise the bug? The total
command line length is limited to 8192 on VMS and a token of 8192 plus
the command itself makes it go over just a bit. I could skip the test
but if a somewhat shorter token would be just as good, that might be a
better solution.

@p5pRT
Copy link
Author

p5pRT commented Feb 25, 2017

From @hvds

On Fri, 24 Feb 2017 14​:48​:34 -0800, craig.a.berry@​gmail.com wrote​:

On Sun, Feb 19, 2017 at 5​:22 AM, Hugo van der Sanden via RT <perlbug-followup@​perl.org> wrote​:

+# [perl #130814] can reallocate lineptr while looking ahead for
+# "Missing $ on loop variable" diagnostic.
+my $result = runperl(
+ prog => " foreach m0\n\$" . ("0" x 0x2000),
+ stderr => 1,
+);

How long does that token need to be to exercise the bug? The total
command line length is limited to 8192 on VMS and a token of 8192 plus
the command itself makes it go over just a bit. I could skip the test
but if a somewhat shorter token would be just as good, that might be a
better solution.

It looks like 0x1ffb is the first multiple to trigger valgrind, which doesn't help you.

Since it's a regression test that's only expected to show failure under valgrind, and only if we mess up and reintroduce the bug, I think it'd be entirely reasonable to skip it on VMS.

Alternatively, since there's nothing about the bug that requires the code to be supplied via -e, it could just be put in a progfile. I don't see a function (or runperl() param) that'll do that for you and deal with the cleanup, but I could see such a thing being handy (especially if miniperl-compatible).

Hugo

@p5pRT
Copy link
Author

p5pRT commented Feb 25, 2017

From @hvds

On Fri, 24 Feb 2017 17​:27​:10 -0800, hv wrote​:

I don't see a function (or runperl() param) that'll do that for you

.. because I'm blind. The attached should do it, I reckon.

Hugo

@p5pRT
Copy link
Author

p5pRT commented Feb 25, 2017

From @hvds

0001-fix-VMS-test-fail.patch
From 31bdffadc0a3947b09513a413a55cbfe82a2b59a Mon Sep 17 00:00:00 2001
From: Hugo van der Sanden <hv@crypt.org>
Date: Sat, 25 Feb 2017 10:42:17 +0000
Subject: [PATCH] fix VMS test fail

d7186add added a runperl() test that breaks command line length limits for
VMS. Switch to fresh_perl() instead, so the prog is put in a file for us.
---
 t/comp/parser_run.t | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/t/comp/parser_run.t b/t/comp/parser_run.t
index 2543f49..e74644d 100644
--- a/t/comp/parser_run.t
+++ b/t/comp/parser_run.t
@@ -14,14 +14,14 @@ plan(1);
 
 # [perl #130814] can reallocate lineptr while looking ahead for
 # "Missing $ on loop variable" diagnostic.
-my $result = runperl(
-    prog => " foreach m0\n\$" . ("0" x 0x2000),
-    stderr => 1,
+my $result = fresh_perl(
+    " foreach m0\n\$" . ("0" x 0x2000),
+    { stderr => 1 },
 );
-is($result, <<EXPECT);
-syntax error at -e line 3, near "foreach m0
+is($result . "\n", <<EXPECT);
+syntax error at - line 3, near "foreach m0
 "
-Identifier too long at -e line 3.
+Identifier too long at - line 3.
 EXPECT
 
 __END__
-- 
2.10.2

@p5pRT
Copy link
Author

p5pRT commented Feb 25, 2017

From @craigberry

On Sat, Feb 25, 2017 at 4​:47 AM, Hugo van der Sanden via RT
<perlbug-followup@​perl.org> wrote​:

On Fri, 24 Feb 2017 17​:27​:10 -0800, hv wrote​:

I don't see a function (or runperl() param) that'll do that for you

.. because I'm blind. The attached should do it, I reckon.

fresh_perl() indeed does the trick and your patch tests out for me. Thanks.

@p5pRT
Copy link
Author

p5pRT commented Feb 26, 2017

From @hvds

On Sat, 25 Feb 2017 07​:43​:40 -0800, craig.a.berry@​gmail.com wrote​:

fresh_perl() indeed does the trick and your patch tests out for me. Thanks.

Now pushed as bce4a2a.

Hugo

@p5pRT
Copy link
Author

p5pRT commented Feb 26, 2017

@hvds - 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 p5pRT closed this as completed May 30, 2017
@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
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant