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

#2 AddressSanitizer: heap-use-after-free on address 0x606000046d00 at pc 0x000000ef95ec bp 0x7ffe2a7856f0 sp 0x7ffe2a7856e8 READ of size 8 #16610

Open
p5pRT opened this issue Jul 5, 2018 · 7 comments
Labels

Comments

@p5pRT
Copy link

p5pRT commented Jul 5, 2018

Migrated from rt.perl.org#133334 (status was 'open')

Searchable as RT133334$

@p5pRT
Copy link
Author

p5pRT commented Jul 5, 2018

From superbsegfault@gmail.com

=================================================================
==25825==ERROR​: AddressSanitizer​: heap-use-after-free on address
0x606000046d00 at pc 0x000000ef95ec bp 0x7ffe2a7856f0 sp 0x7ffe2a7856e8
READ of size 8 at 0x606000046d00 thread T0
  #0 0xef95eb in S_mro_gather_and_rename
/home/perl_asan/perl-5.28.0/mro_core.c​:930​:9
  #1 0xeefbc4 in S_mro_gather_and_rename
/home/perl_asan/perl-5.28.0/mro_core.c​:1186​:4
  #2 0xed6dd3 in Perl_mro_package_moved
/home/perl_asan/perl-5.28.0/mro_core.c​:851​:5
  #3 0x113bc64 in S_glob_assign_glob
/home/perl_asan/perl-5.28.0/sv.c​:3886​:6
  #4 0x111cc3d in Perl_sv_setsv_flags
/home/perl_asan/perl-5.28.0/sv.c​:4365​:7
  #5 0xfef1cb in Perl_pp_sassign
/home/perl_asan/perl-5.28.0/pp_hot.c​:226​:5
  #6 0xfeb0f0 in Perl_runops_standard
/home/perl_asan/perl-5.28.0/run.c​:41​:26
  #7 0x71328f in S_run_body /home/perl_asan/perl-5.28.0/perl.c
  #8 0x71328f in perl_run /home/perl_asan/perl-5.28.0/perl.c​:2617
  #9 0x52b888 in main /home/perl_asan/perl-5.28.0/perlmain.c​:122​:9
  #10 0x7f4814d5cb96 in __libc_start_main
/build/glibc-OTsEL5/glibc-2.27/csu/../csu/libc-start.c​:310
  #11 0x43c109 in _start (/home/perl_asan/perl-5.28.0/perl+0x43c109)

0x606000046d00 is located 0 bytes inside of 64-byte region
[0x606000046d00,0x606000046d40)
freed by thread T0 here​:
  #0 0x4f1b85 in realloc (/home/perl_asan/perl-5.28.0/perl+0x4f1b85)
  #1 0xdf7fe2 in Perl_safesysrealloc
/home/perl_asan/perl-5.28.0/util.c​:271​:18

previously allocated by thread T0 here​:
  #0 0x4f1b85 in realloc (/home/perl_asan/perl-5.28.0/perl+0x4f1b85)
  #1 0xdf7fe2 in Perl_safesysrealloc
/home/perl_asan/perl-5.28.0/util.c​:271​:18

SUMMARY​: AddressSanitizer​: heap-use-after-free
/home/perl_asan/perl-5.28.0/mro_core.c​:930​:9 in S_mro_gather_and_rename
Shadow bytes around the buggy address​:
  0x0c0c80000d50​: fd fd fd fd fa fa fa fa fd fd fd fd fd fd fd fa
  0x0c0c80000d60​: fa fa fa fa fd fd fd fd fd fd fd fd fa fa fa fa
  0x0c0c80000d70​: fd fd fd fd fd fd fd fa fa fa fa fa fd fd fd fd
  0x0c0c80000d80​: fd fd fd fd fa fa fa fa fd fd fd fd fd fd fd fd
  0x0c0c80000d90​: fa fa fa fa 00 00 00 00 00 00 00 00 fa fa fa fa
=>0x0c0c80000da0​:[fd]fd fd fd fd fd fd fd fa fa fa fa 00 00 00 00
  0x0c0c80000db0​: 00 00 00 00 fa fa fa fa 00 00 00 00 00 00 00 fa
  0x0c0c80000dc0​: fa fa fa fa 00 00 00 00 00 00 00 fa fa fa fa fa
  0x0c0c80000dd0​: 00 00 00 00 00 00 00 00 fa fa fa fa fd fd fd fd
  0x0c0c80000de0​: fd fd fd fa fa fa fa fa fd fd fd fd fd fd fd fa
  0x0c0c80000df0​: fa fa fa fa 00 00 00 00 00 00 00 00 fa fa fa 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
  Freed heap region​: fd
  Stack left redzone​: f1
  Stack mid redzone​: f2
  Stack right redzone​: f3
  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
==25825==ABORTING

@p5pRT
Copy link
Author

p5pRT commented Jul 5, 2018

@p5pRT
Copy link
Author

p5pRT commented Jul 13, 2018

From @hvds

This reduces at least to​:
  ./miniperl -Ilib -e '*L​:: = *0; $x = *L​::; *S​:: = *​::; %$x = *S​::; *S​:: = *​::'

At the point of failure, we're hitting this code from mro_core.c​:935​:
  SvREFCNT_inc_simple_NN((SV *)meta->isa)
.. but meta->isa is 0x1​:
{
  mro_linear_all = 0xa5bd40,
  mro_linear_current = 0xa5ef60,
  mro_nextmethod = 0x0,
  cache_gen = 10885104,
  pkg_gen = 0,
  mro_which = 0x5300000000,
  isa = 0x1,
  super = 0x2,
  destroy = 0x1001,
  destroy_gen = 0
}

A hardware watchpoint on the location of isa shows it getting set during Perl_init_stacks () at perl.c​:4307​:
  Newx(PL_tmps_stack,REASONABLE(128),SV*);
.. which I assume means it's malloc metadata and got to us via a wild pointer, but I don't know for sure.

Here's a run with -Do​:
% ./miniperl -Do -Ilib -e '*L​:: = *0; $x = *L​::; *S​:: = *​::; %$x = *S​::; *S​:: = *​::'
(-e​:0) sv_upgrade clearing PL_stashcache
(-e​:0) sv_upgrade clearing PL_stashcache
(-e​:0) sv_upgrade clearing PL_stashcache
(-e​:0) sv_upgrade clearing PL_stashcache
(-e​:0) sv_upgrade clearing PL_stashcache
(-e​:2) Looking for DESTROY method for IO​::File
(-e​:2) Looking for method DESTROY in package IO​::File
(-e​:2) Looking for method DESTROY in package UNIVERSAL
(-e​:2) Looking for method AUTOLOAD in package IO​::File
(-e​:2) Looking for method AUTOLOAD in package UNIVERSAL
(-e​:2) Set cached DESTROY method 0 for IO​::File

EXECUTING...

(-e​:1) gp_free clearing PL_stashcache for 'L'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'L'
(-e​:1) hv_undef_flags clearing PL_stashcache for 'L'
(-e​:1) gp_free clearing PL_stashcache for 'S'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S'
(-e​:1) hv_undef_flags clearing PL_stashcache for 'S'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S​::S'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S​::Exporter'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S​::S​::Exporter'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S​::version'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S​::S​::version'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S​::constant'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S​::S​::constant'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S​::Internals'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S​::S​::Internals'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S​::Regexp'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S​::S​::Regexp'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S​::CORE'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S​::S​::CORE'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S​::CORE​::GLOBAL'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S​::S​::CORE​::GLOBAL'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S​::re'
(-e​:1) mro_gather_and_rename clearing PL_stashcache for 'S​::S​::re'
Segmentation fault (core dumped)

I don't know enough about the mro stuff to guess whether this is simply "don't do that" territory or whether there's something we could or should be fixing.

Hugo

@p5pRT
Copy link
Author

p5pRT commented Jul 13, 2018

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

@p5pRT
Copy link
Author

p5pRT commented Jul 17, 2018

From @hvds

On Fri, 13 Jul 2018 03​:39​:46 -0700, hv wrote​:

This reduces at least to​:
./miniperl -Ilib -e '*L​:: = *0; $x = *L​::; *S​:: = *​::; %$x = *S​::; *S​:: = *​::'

I feel I've got a little further with this. I set PERL_HASH_SEED for stability.

This still SEGVs in the same place​:
% PERL_HASH_SEED=1 ./miniperl -Do -e '
  *bad_stash​:: = *non_stash;
  delete @​​::{grep !/^(bad_stash|recursive_stash)​::$/, keys %​::};
  *recursive_stash​:: = \%​::;
  %{*non_stash} = ();
  *recursive_stash​:: = \%​::
'

I suspect that it boils down to something like​: both the initial *bad_stash​::=*non_stash and the later %{*non_stash}=() leave %bad_stash​:: as something not properly initialised as a stash; it does get properly initialised at the point it is needed, but in this case we are reaching it twice in the same operation (as recursive_stash​::bad_stash and recursive_stash​::recursive_stash​::bad_stash) and the first and second attempt are interacting badly.

If that's correct, it may well be that the bad interaction relates to the seen_stashes logic in mro_gather_and_rename​:
  /* We use the seen_stashes hash to keep track of which packages have
  been encountered so far. [...]
.. possibly along with the isa-restoration in mro_package_moved (comment starting "We have to restore the original meta->isa").

Nothing I've seen so far suggests to me there is a credible security issue here, but I'm far from understanding it well enough to call it.

Hugo

@p5pRT
Copy link
Author

p5pRT commented Aug 3, 2018

From @tonycoz

On Tue, 17 Jul 2018 07​:02​:45 -0700, hv wrote​:

On Fri, 13 Jul 2018 03​:39​:46 -0700, hv wrote​:

This reduces at least to​:
./miniperl -Ilib -e '*L​:: = *0; $x = *L​::; *S​:: = *​::; %$x = *S​::;
*S​:: = *​::'

I feel I've got a little further with this. I set PERL_HASH_SEED for
stability.

This still SEGVs in the same place​:
% PERL_HASH_SEED=1 ./miniperl -Do -e '
*bad_stash​:: = *non_stash;
delete @​​::{grep !/^(bad_stash|recursive_stash)​::$/, keys %​::};
*recursive_stash​:: = \%​::;
%{*non_stash} = ();
*recursive_stash​:: = \%​::
'

I suspect that it boils down to something like​: both the initial
*bad_stash​::=*non_stash and the later %{*non_stash}=() leave
%bad_stash​:: as something not properly initialised as a stash; it does
get properly initialised at the point it is needed, but in this case
we are reaching it twice in the same operation (as
recursive_stash​::bad_stash and
recursive_stash​::recursive_stash​::bad_stash) and the first and second
attempt are interacting badly.

If that's correct, it may well be that the bad interaction relates to
the seen_stashes logic in mro_gather_and_rename​:
/* We use the seen_stashes hash to keep track of which packages
have
been encountered so far. [...]
.. possibly along with the isa-restoration in mro_package_moved
(comment starting "We have to restore the original meta->isa").

Nothing I've seen so far suggests to me there is a credible security
issue here, but I'm far from understanding it well enough to call it.

I don't think this is a security issue.

I can see three cases​:

a) a perl program always does this type of contortion and crashes. This has nothing to do with attacker inputs, so isn't a security issue.

b) a perl program does this type of contortion conditionally on attacher inputs, always using the same names. Since the names are the same, presumably this is by design, so control by an attacker is irrelevant.

c) a perl program does this type of contortion using named supplied by an attacker. I'd think such a program is already a security mess, having this code crash can only improve security.

As to the cause, I added some sv_dump() to S_mro_gather_and_rename() and stash and oldstash both point to the same hash, which *isn't* a stash (assuming I know what a stash is)​:

stash
SV = PVHV(0x5626fd726a00) at 0x5626fd742f18
  REFCNT = 1
  FLAGS = (SHAREKEYS)
  ARRAY = 0x5626fd734dd0 (0​:7, 1​:1)
  hash quality = 100.0%
  KEYS = 1
  FILL = 1
  MAX = 7
oldstash
SV = PVHV(0x5626fd726a00) at 0x5626fd742f18
  REFCNT = 1
  FLAGS = (SHAREKEYS)
  ARRAY = 0x5626fd734dd0 (0​:7, 1​:1)
  hash quality = 100.0%
  KEYS = 1
  FILL = 1
  MAX = 7
Segmentation fault

Just to check I added an assertion to HvAUX()​:

#define HvAUX(hv) (assert(SvOOK(hv)), (struct xpvhv_aux*)&(HvARRAY(hv)[HvMAX(hv)+1]))

which failed in the same place the segmentation fault occurs​:

stash
SV = PVHV(0x5590eb2ada00) at 0x5590eb2c9f28
  REFCNT = 1
  FLAGS = (SHAREKEYS)
  ARRAY = 0x5590eb2bbdd0 (0​:7, 1​:1)
  hash quality = 100.0%
  KEYS = 1
  FILL = 1
  MAX = 7
oldstash
SV = PVHV(0x5590eb2ada00) at 0x5590eb2c9f28
  REFCNT = 1
  FLAGS = (SHAREKEYS)
  ARRAY = 0x5590eb2bbdd0 (0​:7, 1​:1)
  hash quality = 100.0%
  KEYS = 1
  FILL = 1
  MAX = 7
miniperl​: mro_core.c​:934​: S_mro_gather_and_rename​: Assertion `SvOOK(oldstash)' failed.
Aborted

Tony

@p5pRT
Copy link
Author

p5pRT commented Feb 11, 2019

From @tonycoz

On Thu, 02 Aug 2018 22​:07​:02 -0700, tonyc wrote​:

On Tue, 17 Jul 2018 07​:02​:45 -0700, hv wrote​:

On Fri, 13 Jul 2018 03​:39​:46 -0700, hv wrote​:

This reduces at least to​:
./miniperl -Ilib -e '*L​:: = *0; $x = *L​::; *S​:: = *​::; %$x =
*S​::;
*S​:: = *​::'

I feel I've got a little further with this. I set PERL_HASH_SEED for
stability.

This still SEGVs in the same place​:
% PERL_HASH_SEED=1 ./miniperl -Do -e '
*bad_stash​:: = *non_stash;
delete @​​::{grep !/^(bad_stash|recursive_stash)​::$/, keys %​::};
*recursive_stash​:: = \%​::;
%{*non_stash} = ();
*recursive_stash​:: = \%​::
'

I suspect that it boils down to something like​: both the initial
*bad_stash​::=*non_stash and the later %{*non_stash}=() leave
%bad_stash​:: as something not properly initialised as a stash; it
does
get properly initialised at the point it is needed, but in this case
we are reaching it twice in the same operation (as
recursive_stash​::bad_stash and
recursive_stash​::recursive_stash​::bad_stash) and the first and second
attempt are interacting badly.

If that's correct, it may well be that the bad interaction relates to
the seen_stashes logic in mro_gather_and_rename​:
/* We use the seen_stashes hash to keep track of which packages
have
been encountered so far. [...]
.. possibly along with the isa-restoration in mro_package_moved
(comment starting "We have to restore the original meta->isa").

Nothing I've seen so far suggests to me there is a credible security
issue here, but I'm far from understanding it well enough to call it.

I don't think this is a security issue.

No objections heard, so now public.

Tony

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

No branches or pull requests

2 participants