Navigation Menu

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-buffer-overflow Perl_re_intuit_start (regexec.c:809:37) #15554

Closed
p5pRT opened this issue Aug 25, 2016 · 12 comments
Closed

heap-buffer-overflow Perl_re_intuit_start (regexec.c:809:37) #15554

p5pRT opened this issue Aug 25, 2016 · 12 comments

Comments

@p5pRT
Copy link

p5pRT commented Aug 25, 2016

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

Searchable as RT129085$

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2016

From @geeknik

The attached script triggers a heap-buffer-overflow Perl_re_intuit_start
regexec.c​:809​:37. This was found with AFL, ASAN and libdislocator and
affects v5.25.4-10-g8d168aa. I was not able to trigger this on a non-ASAN
build.

==17057==ERROR​: AddressSanitizer​: heap-buffer-overflow on address
0x60800000b978 at pc 0x0000004a9201 bp 0x7ffe97551890 sp 0x7ffe97551048
READ of size 61 at 0x60800000b978 thread T0
  #0 0x4a9200 in __interceptor_memcmp (/root/perl/perl+0x4a9200)
  #1 0xb46135 in Perl_re_intuit_start /root/perl/regexec.c​:809​:37
  #2 0xb3a3e5 in Perl_regexec_flags /root/perl/regexec.c​:2988​:6
  #3 0x8be7f9 in Perl_pp_match /root/perl/pp_hot.c​:1836​:10
  #4 0x7f1dd3 in Perl_runops_debug /root/perl/dump.c​:2234​:23
  #5 0x5a1234 in S_run_body /root/perl/perl.c​:2525​:2
  #6 0x5a1234 in perl_run /root/perl/perl.c​:2448
  #7 0x4de85d in main /root/perl/perlmain.c​:123​:9
  #8 0x7f8899228b44 in __libc_start_main
/build/glibc-uPj9cH/glibc-2.19/csu/libc-start.c​:287
  #9 0x4de4cc in _start (/root/perl/perl+0x4de4cc)

0x60800000b978 is located 0 bytes to the right of 88-byte region
[0x60800000b920,0x60800000b978)
allocated by thread T0 here​:
  #0 0x4c113e in realloc (/root/perl/perl+0x4c113e)
  #1 0x7f6306 in Perl_safesysrealloc /root/perl/util.c​:274​:18

SUMMARY​: AddressSanitizer​: heap-buffer-overflow ??​:0 __interceptor_memcmp
Shadow bytes around the buggy address​:
  0x0c107fff96d0​: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c107fff96e0​: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c107fff96f0​: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c107fff9700​: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c107fff9710​: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c107fff9720​: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00[fa]
  0x0c107fff9730​: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 06 fa
  0x0c107fff9740​: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
  0x0c107fff9750​: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
  0x0c107fff9760​: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
  0x0c107fff9770​: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
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
==17057==ABORTING

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2016

From @geeknik

overflow22.gz

@p5pRT
Copy link
Author

p5pRT commented Aug 29, 2016

From @tonycoz

On Thu Aug 25 12​:04​:57 2016, brian.carpenter@​gmail.com wrote​:

The attached script triggers a heap-buffer-overflow Perl_re_intuit_start
regexec.c​:809​:37. This was found with AFL, ASAN and libdislocator and
affects v5.25.4-10-g8d168aa. I was not able to trigger this on a non-ASAN
build.

I didn't need libdisallocator to reproduce this.

valgrind didn't complain about this, but I guess it's just letting
memcmp() do it's work, and that's stopping on the first byte mismatch
preventing it from detecting an error.

Here's a simplified/shortened version​:

$_ = "\x{f2}\x{140}\x{fe}\x{ff}\x{ff}\x{ff}";
m/^0000.\34500\376\377\377\377/;

This caused by Perl_re_intuit_start() attempting to match against the fixed
"\34500\376\377\377\377" string at an offset into the value in $_​:

  if (slen && (*SvPVX_const(check) != *s
  || (slen > 1 && memNE(SvPVX_const(check), s, slen))))
  {
  DEBUG_EXECUTE_r(Perl_re_printf( aTHX_
  " String not equal...\n"));
  goto fail_finish;
  }

Here s is a pointer to the sixth character in $_ (the *last* character),
slen is the length of the check string (12 in the simplified case, since
\377 expands to 2 bytes in UTF-8).

The end of the string is only 2 bytes after s, and we're attemptting to
compare 12 bytes at that location, producing the error ASAN reports.

We might argue that we expect memcmp() to step through the two strings byte
by byte and stop at the first mismatch (which maybe the original author
expected), but I don't think the wording of the standard allows us to make
that assumption.

See also​:

http​://trust-in-soft.com/memcmp-requires-pointers-to-fully-valid-buffers/

Patch attached.

It *might* be possible to use this as a hard to trigger denial of service
attack, eg. if the memcmp() went past the end of a page into unmapped
memory.

Tony

@p5pRT
Copy link
Author

p5pRT commented Aug 29, 2016

From @tonycoz

0001-perl-129085-avoid-memcmp-past-the-end-of-a-string.patch
From 4b772b77a4eb75b062f40cc32018e0f34fac601e Mon Sep 17 00:00:00 2001
From: Tony Cook <tony@develop-help.com>
Date: Mon, 29 Aug 2016 15:04:55 +1000
Subject: (perl #129085) avoid memcmp() past the end of a string

When a match is anchored against the start of a string, the regexp
can be compiled to include a fixed string match against a fixed
offset in the string.

In some cases, where the matched against string included UTF-8 before
the fixed offset, this could result in attempting a memcmp() which
overlaps the end of the string and potentially past the end of the
allocated memory.
---
 regexec.c            | 5 +++--
 t/re/pat_rt_report.t | 9 ++++++++-
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/regexec.c b/regexec.c
index fad8876..bba3b81 100644
--- a/regexec.c
+++ b/regexec.c
@@ -805,8 +805,9 @@ Perl_re_intuit_start(pTHX_
                     /* Now should match s[0..slen-2] */
                     slen--;
                 }
-                if (slen && (*SvPVX_const(check) != *s
-                    || (slen > 1 && memNE(SvPVX_const(check), s, slen))))
+                if (slen && (strend - s < slen
+                    || *SvPVX_const(check) != *s
+                    || (slen > 1 && (memNE(SvPVX_const(check), s, slen)))))
                 {
                     DEBUG_EXECUTE_r(Perl_re_printf( aTHX_
                                     "  String not equal...\n"));
diff --git a/t/re/pat_rt_report.t b/t/re/pat_rt_report.t
index addb3e2..bee1b19 100644
--- a/t/re/pat_rt_report.t
+++ b/t/re/pat_rt_report.t
@@ -20,7 +20,7 @@ use warnings;
 use 5.010;
 use Config;
 
-plan tests => 2501;  # Update this when adding/deleting tests.
+plan tests => 2502;  # Update this when adding/deleting tests.
 
 run_tests() unless caller;
 
@@ -1123,6 +1123,13 @@ EOP
         ok($s !~ /00000?\x80\x80\x80/, "RT #129012");
     }
 
+    {
+        # RT #129085 heap-buffer-overflow Perl_re_intuit_start
+        # this did fail under ASAN, but didn't under valgrind
+        my $s = "\x{f2}\x{140}\x{fe}\x{ff}\x{ff}\x{ff}";
+        ok($s !~ /^0000.\34500\376\377\377\377/, "RT #129085");
+    }
+
 } # End of sub run_tests
 
 1;
-- 
2.1.4

@p5pRT
Copy link
Author

p5pRT commented Aug 29, 2016

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

@p5pRT
Copy link
Author

p5pRT commented Aug 29, 2016

From @jhi

On Monday-201608-29 1​:08, Tony Cook via RT wrote​:

We might argue that we expect memcmp() to step through the two strings byte
by byte and stop at the first mismatch (which maybe the original author
expected), but I don't think the wording of the standard allows us to make
that assumption.

This is craziness.

Yet another victim in the altar of speed.

What is the more general "fix"? (not referring to Tony's spot fix here)
Every string needs to be padded at the end by up to (alignment-1)
bytes?

@p5pRT
Copy link
Author

p5pRT commented Aug 30, 2016

From @tonycoz

On Mon Aug 29 04​:42​:35 2016, jhi wrote​:

On Monday-201608-29 1​:08, Tony Cook via RT wrote​:

We might argue that we expect memcmp() to step through the two
strings byte
by byte and stop at the first mismatch (which maybe the original
author
expected), but I don't think the wording of the standard allows us to
make
that assumption.

This is craziness.

Yet another victim in the altar of speed.

What is the more general "fix"? (not referring to Tony's spot fix
here)
Every string needs to be padded at the end by up to (alignment-1)
bytes?

This seems like an overreaction, or a reaction to a problem that doesn't exist.

The bug ASAN is detecting is that we're supplying a region of memory to
memcmp() that overlaps past the end of an allocated block.

A lesser case might be us comparing within the allocated block, but beyond
the end of the actual string. An attacker that can control the regexp
*might* be able to use that to examine the contents of memory beyond
the terminating NUL, which would be critical if that previously held a
password or anything else sensitive.

Such an attack probably isn't practical, but I might be wrong.

Tony

@p5pRT
Copy link
Author

p5pRT commented Sep 6, 2016

From @iabyn

On Mon, Aug 29, 2016 at 05​:48​:19PM -0700, Tony Cook via RT wrote​:

On Mon Aug 29 04​:42​:35 2016, jhi wrote​:

On Monday-201608-29 1​:08, Tony Cook via RT wrote​:

We might argue that we expect memcmp() to step through the two
strings byte
by byte and stop at the first mismatch (which maybe the original
author
expected), but I don't think the wording of the standard allows us to
make
that assumption.

This is craziness.

Yet another victim in the altar of speed.

What is the more general "fix"? (not referring to Tony's spot fix
here)
Every string needs to be padded at the end by up to (alignment-1)
bytes?

This seems like an overreaction, or a reaction to a problem that doesn't exist.

The bug ASAN is detecting is that we're supplying a region of memory to
memcmp() that overlaps past the end of an allocated block.

Yep, definitely a bug at our end. In theory a match like /ab\0X/
could be tried against the string "ab" which happens to be followed
by the usual null byte then a random byte "X". We've asked memcmp
to compare four bytes, and that what it does.

A lesser case might be us comparing within the allocated block, but beyond
the end of the actual string. An attacker that can control the regexp
*might* be able to use that to examine the contents of memory beyond
the terminating NUL, which would be critical if that previously held a
password or anything else sensitive.

Such an attack probably isn't practical, but I might be wrong.

This particular test is intended as a quick fail. The worst that can
happen (apart from a SEGV from accessing unmapped memory) is that
in very unlikely circumstances (like above where the string contains
the 2 chars "ab", terminated by \0 and X, the quick fail doesn't trigger,
and the full NFA has to run to decide that the match fails.

i.e. in extremely unlikely circumstances, an optimisation is skipped.

I don't think this is a security issue any more, and I think your patch
should be applied.

--
Nothing ventured, nothing lost.

@p5pRT
Copy link
Author

p5pRT commented Oct 31, 2016

From @tonycoz

On Tue Sep 06 01​:40​:31 2016, davem wrote​:

I don't think this is a security issue any more, and I think your
patch
should be applied.

I've made the ticket public and applied my fix as
26fb231.

Tony

@p5pRT
Copy link
Author

p5pRT commented Oct 31, 2016

@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'

@p5pRT p5pRT closed this as completed May 30, 2017
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