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

SIGSEGV in hek_eq_pvn_flags #15307

Closed
p5pRT opened this issue May 6, 2016 · 8 comments
Closed

SIGSEGV in hek_eq_pvn_flags #15307

p5pRT opened this issue May 6, 2016 · 8 comments

Comments

@p5pRT
Copy link

p5pRT commented May 6, 2016

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

Searchable as RT128086$

@p5pRT
Copy link
Author

p5pRT commented May 6, 2016

From @dcollinsn

Greetings Porters,

I have compiled bleadperl with the afl-gcc compiler using​:

./Configure -Dusedevel -Dprefix='/usr/local/perl-afl' -Dcc='ccache afl-gcc' -Uuselongdouble -Duse64bitall -Doptimize=-g -Uversiononly -Uman1dir -Uman3dir -Dusequadmath -des
AFL_HARDEN=1 make && make test

And then fuzzed the resulting binary using​:

AFL_NO_VAR_CHECK=1 afl-fuzz -i in -o out bin/perl @​@​

After reducing testcases using `afl-tmin` and performing additional minimization by hand, I have located the following testcase that triggers a segmentation fault in the perl interpreter. The testcase is the 25-character file​:

/${*​::​::​::=0}$0{*​::​::=0}/

On normal and debug builds, this crashes with a segmentation fault.

dcollins@​nightshade64​:~/perl$ ~/perl/perl -e '/${*​::​::​::=0}$0{*​::​::=0}/'
Segmentation fault
dcollins@​nightshade64​:~/perl$ ~/perldebug/perl -e '/${*​::​::​::=0}$0{*​::​::=0}/'
Segmentation fault

Debugging tool output is below. I don't think this has been reported yet, strategic searches for "S_mro_gather_and_rename" don't seem to yield any past reports. It's dereferencing something that isn't a pointer.

**GDB**

dcollins@​nightshade64​:~/perl$ gdb --args ~/perl/perl -e '/${*​::​::​::=0}$0{*​::​::=0}/'
GNU gdb (GDB) 7.10
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+​: GNU GPL version 3 or later <http​://gnu.org/licenses/gpl.html>
This is free software​: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see​:
<http​://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at​:
<http​://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /home/dcollins/perl/perl...done.
(gdb) run
Starting program​: /home/dcollins/perl/perl -e /\$\{\*​::​::​::=0\}\$0\{\*​::​::=0\}/
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
0x0000000000773478 in hek_eq_pvn_flags (flags=<optimized out>, pvlen=<optimized out>, pv=<optimized out>, hek=<optimized out>) at hv.c​:2349
2349 return (bytes_cmp_utf8(
(gdb) bt
#0 0x0000000000773478 in hek_eq_pvn_flags (flags=<optimized out>, pvlen=<optimized out>, pv=<optimized out>, hek=<optimized out>) at hv.c​:2349
#1 Perl_hv_ename_delete (hv=0xe99d58, name=name@​entry=0xe92860 "​::", len=2, flags=flags@​entry=0) at hv.c​:2488
#2 0x000000000073020c in S_mro_gather_and_rename (stashes=stashes@​entry=0xe99470, seen_stashes=seen_stashes@​entry=0xe99500, stash=stash@​entry=0x0,
  oldstash=oldstash@​entry=0xe99d58, namesv=namesv@​entry=0xe994e8) at mro_core.c​:962
#3 0x000000000072d09a in S_mro_gather_and_rename (stashes=stashes@​entry=0xe99470, seen_stashes=0xe99500, stash=stash@​entry=0xe99d58, oldstash=oldstash@​entry=0xe99cf8,
  namesv=namesv@​entry=0xe99458) at mro_core.c​:1186
#4 0x0000000000738e6b in Perl_mro_package_moved (stash=0xe99d58, oldstash=oldstash@​entry=0xe99cf8, gv=gv@​entry=0xe99ce0, flags=flags@​entry=0) at mro_core.c​:851
#5 0x00000000007fd515 in Perl_sv_setsv_flags (dstr=dstr@​entry=0xe99ce0, sstr=sstr@​entry=0xe99d40, flags=flags@​entry=1538) at sv.c​:4561
#6 0x00000000007949d0 in Perl_pp_sassign () at pp_hot.c​:226
#7 0x000000000079148b in Perl_runops_standard () at run.c​:41
#8 0x00000000004ebf9f in S_run_body (oldscope=1) at perl.c​:2483
#9 perl_run (my_perl=<optimized out>) at perl.c​:2406
#10 0x00000000004276a8 in main (argc=3, argv=0x7fffffffe338, env=0x7fffffffe358) at perlmain.c​:116
(gdb) info locals
No locals.
(gdb) l
2344 if (flags & SVf_UTF8)
2345 return (bytes_cmp_utf8(
2346 (const U8*)HEK_KEY(hek), HEK_LEN(hek),
2347 (const U8*)pv, pvlen) == 0);
2348 else
2349 return (bytes_cmp_utf8(
2350 (const U8*)pv, pvlen,
2351 (const U8*)HEK_KEY(hek), HEK_LEN(hek)) == 0);
2352 }
2353 else
(gdb)

**VALGRIND**

dcollins@​nightshade64​:~/perl$ valgrind ~/perl/perl -e '/${*​::​::​::=0}$0{*​::​::=0}/'
==9173== Memcheck, a memory error detector
==9173== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==9173== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==9173== Command​: /home/dcollins/perl/perl -e /${*​::​::​::=0}$0{*​::​::=0}/
==9173==
==9173== Invalid read of size 4
==9173== at 0x773478​: hek_eq_pvn_flags (hv.c​:2349)
==9173== by 0x773478​: Perl_hv_ename_delete (hv.c​:2488)
==9173== by 0x73020B​: S_mro_gather_and_rename (mro_core.c​:962)
==9173== by 0x72D099​: S_mro_gather_and_rename (mro_core.c​:1186)
==9173== by 0x738E6A​: Perl_mro_package_moved (mro_core.c​:851)
==9173== by 0x7FD514​: Perl_sv_setsv_flags (sv.c​:4561)
==9173== by 0x7949CF​: Perl_pp_sassign (pp_hot.c​:226)
==9173== by 0x79148A​: Perl_runops_standard (run.c​:41)
==9173== by 0x4EBF9E​: S_run_body (perl.c​:2483)
==9173== by 0x4EBF9E​: perl_run (perl.c​:2406)
==9173== by 0x4276A7​: main (perlmain.c​:116)
==9173== Address 0x4 is not stack'd, malloc'd or (recently) free'd
==9173==
==9173==
==9173== Process terminating with default action of signal 11 (SIGSEGV)
==9173== Access not within mapped region at address 0x4
==9173== at 0x773478​: hek_eq_pvn_flags (hv.c​:2349)
==9173== by 0x773478​: Perl_hv_ename_delete (hv.c​:2488)
==9173== by 0x73020B​: S_mro_gather_and_rename (mro_core.c​:962)
==9173== by 0x72D099​: S_mro_gather_and_rename (mro_core.c​:1186)
==9173== by 0x738E6A​: Perl_mro_package_moved (mro_core.c​:851)
==9173== by 0x7FD514​: Perl_sv_setsv_flags (sv.c​:4561)
==9173== by 0x7949CF​: Perl_pp_sassign (pp_hot.c​:226)
==9173== by 0x79148A​: Perl_runops_standard (run.c​:41)
==9173== by 0x4EBF9E​: S_run_body (perl.c​:2483)
==9173== by 0x4EBF9E​: perl_run (perl.c​:2406)
==9173== by 0x4276A7​: main (perlmain.c​:116)
==9173== If you believe this happened as a result of a stack
==9173== overflow in your program's main thread (unlikely but
==9173== possible), you can try to increase the size of the
==9173== main thread stack using the --main-stacksize= flag.
==9173== The main thread stack size used in this run was 8388608.
==9173==
==9173== HEAP SUMMARY​:
==9173== in use at exit​: 121,264 bytes in 656 blocks
==9173== total heap usage​: 770 allocs, 114 frees, 138,239 bytes allocated
==9173==
==9173== LEAK SUMMARY​:
==9173== definitely lost​: 328 bytes in 1 blocks
==9173== indirectly lost​: 2,631 bytes in 39 blocks
==9173== possibly lost​: 0 bytes in 0 blocks
==9173== still reachable​: 118,305 bytes in 616 blocks
==9173== suppressed​: 0 bytes in 0 blocks
==9173== Rerun with --leak-check=full to see details of leaked memory
==9173==
==9173== For counts of detected and suppressed errors, rerun with​: -v
==9173== ERROR SUMMARY​: 1 errors from 1 contexts (suppressed​: 0 from 0)
Segmentation fault

**PERL -V**

dcollins@​nightshade64​:~/perl$ ./perl -Ilib -V
Summary of my perl5 (revision 5 version 24 subversion 0) configuration​:
  Commit id​: 10d36cf
  Platform​:
  osname=linux, osvers=3.16.0-4-amd64, archname=x86_64-linux-ld
  uname='linux nightshade64 3.16.0-4-amd64 #1 smp debian 3.16.7-ckt20-1+deb8u3 (2016-01-17) x86_64 gnulinux '
  config_args='-Dusedevel -Dprefix=/usr/local/perl-afl -Dcc=ccache afl-gcc -Duselongdouble -Duse64bitall -Doptimize=-g -Uversiononly -Uman1dir -Uman3dir -des'
  hint=recommended, useposix=true, d_sigaction=define
  useithreads=undef, usemultiplicity=undef
  use64bitint=define, use64bitall=define, uselongdouble=define
  usemymalloc=n, bincompat5005=undef
  Compiler​:
  cc='ccache afl-gcc', ccflags ='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
  optimize='-g',
  cppflags='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include'
  ccversion='', gccversion='5.2.0', 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='long double', nvsize=16, Off_t='off_t', lseeksize=8
  alignbytes=16, prototype=define
  Linker and Libraries​:
  ld='ccache afl-gcc', ldflags =' -fstack-protector-strong -L/usr/local/lib'
  libpth=/usr/local/lib /usr/local/lib/gcc/x86_64-unknown-linux-gnu/5.2.0/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.19.so, so=so, useshrplib=false, libperl=libperl.a
  gnulibc_version='2.19'
  Dynamic Linking​:
  dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E'
  cccdlflags='-fPIC', lddlflags='-shared -g -L/usr/local/lib -fstack-protector-strong'

Characteristics of this binary (from libperl)​:
  Compile-time options​: HAS_TIMES PERLIO_LAYERS PERL_COPY_ON_WRITE
  PERL_DONT_CREATE_GVSV
  PERL_HASH_FUNC_ONE_AT_A_TIME_HARD PERL_MALLOC_WRAP
  PERL_PRESERVE_IVUV PERL_USE_DEVEL USE_64_BIT_ALL
  USE_64_BIT_INT USE_LARGE_FILES USE_LOCALE
  USE_LOCALE_COLLATE USE_LOCALE_CTYPE
  USE_LOCALE_NUMERIC USE_LOCALE_TIME USE_LONG_DOUBLE
  USE_PERLIO USE_PERL_ATOF
  Locally applied patches​:
  RC3
  Built under linux
  Compiled at Apr 30 2016 15​:50​:19
  @​INC​:
  lib
  /usr/local/perl-afl/lib/site_perl/5.24.0/x86_64-linux-ld
  /usr/local/perl-afl/lib/site_perl/5.24.0
  /usr/local/perl-afl/lib/5.24.0/x86_64-linux-ld
  /usr/local/perl-afl/lib/5.24.0
  .

@p5pRT
Copy link
Author

p5pRT commented May 10, 2016

From @hvds

This appears to be a precedence error, fixable with an extra pair of parens as below. I don't understand this area well though, and I'd like to add a test that's less obscure than the reported test case.

Any suggestions?

Hugo

Inline Patch
diff --git a/hv.c b/hv.c
index 7b5ad95..5523475 100644
--- a/hv.c
+++ b/hv.c
@@ -2476,9 +2476,10 @@ Perl_hv_ename_delete(pTHX_ HV *hv, const char *name, U32 
                return;
            }
        if (
-           count > 0 && (HEK_UTF8(*namep) || (flags & SVf_UTF8)) 
+           count > 0 && ((HEK_UTF8(*namep) || (flags & SVf_UTF8)) 
                 ? hek_eq_pvn_flags(aTHX_ *namep, name, (I32)len, flags)
                : (HEK_LEN(*namep) == (I32)len && memEQ(HEK_KEY(*namep), name, l
+            )
        ) {
            aux->xhv_name_count = -count;
        }

@p5pRT
Copy link
Author

p5pRT commented May 10, 2016

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

@p5pRT
Copy link
Author

p5pRT commented May 11, 2016

From @cpansprout

On Tue May 10 01​:00​:01 2016, hv wrote​:

This appears to be a precedence error, fixable with an extra pair of
parens as below. I don't understand this area well though, and I'd
like to add a test that's less obscure than the reported test case.

Any suggestions?

I think there may be more to this bug than meets the eye.

If I add the debugging code in the attached patch, I get this​:

SV = PVHV(0x7fda9280aec0) at 0x7fda9282a938
  REFCNT = 1
  FLAGS = (OOK,SHAREKEYS,OVERLOAD)
  AUX_FLAGS = 0
  ARRAY = 0x7fda924098e0
  KEYS = 0
  FILL = 0 (cached = 0)
  MAX = 7
  RITER = -1
  EITER = 0x0
  RAND = 0x2da71023
  NAMECOUNT = -3
  ENAME = "​::​::", (null)

etc.

ENAME should never have null in it. That’s a bug, possibly elsewhere. I need to dig deeper.

--

Father Chrysostomos

@p5pRT
Copy link
Author

p5pRT commented May 11, 2016

From @cpansprout

Inline Patch
diff --git a/hv.c b/hv.c
index 7b5ad95..697a0aa 100644
--- a/hv.c
+++ b/hv.c
@@ -2478,7 +2478,7 @@ Perl_hv_ename_delete(pTHX_ HV *hv, const char *name, U32 len, U32 flags)
 	if (
 	    count > 0 && (HEK_UTF8(*namep) || (flags & SVf_UTF8)) 
                 ? hek_eq_pvn_flags(aTHX_ *namep, name, (I32)len, flags)
-	        : (HEK_LEN(*namep) == (I32)len && memEQ(HEK_KEY(*namep), name, len))
+	        : (*namep || (Perl_sv_dump(aTHX_ (SV*)hv),0), (HEK_LEN(*namep) == (I32)len && memEQ(HEK_KEY(*namep), name, len)))
 	) {
 	    aux->xhv_name_count = -count;
 	}

@p5pRT
Copy link
Author

p5pRT commented May 14, 2016

From @cpansprout

On Tue May 10 21​:13​:32 2016, sprout wrote​:

On Tue May 10 01​:00​:01 2016, hv wrote​:

This appears to be a precedence error, fixable with an extra pair of
parens as below. I don't understand this area well though, and I'd
like to add a test that's less obscure than the reported test case.

Any suggestions?

This is the simplest test case I can come up with​:

%h; *​::​::​::=*h; delete $​::{"​::"}

It has to do with two code paths dividing up and reconstructing names like :​::​::​: differently.

Perl keeps track of all the effective names of a stash, if it gets aliased or reassigned. That is what HvENAME is for, whereas HvNAME is the original, canonical name of the stash.

In the short case above, %h must be vivified first, before the assignment (any compile-time mention will work). Then, when the assignment happens, it gets the *effective* name :​::​: assigned to it.

Internally, a stash may have a list of names. If the canonical name (the first name in the list) is not one of the effective names, then the name count is negative, to indicate that. After the assignment above, we have​:

NULL, "​::​::"

(with a name count of -2) such that HvNAME is NULL, but HvENAME is a hek containing "​::​::".

The count>0 check in hv_ename_delete protects against reading that null, but this is where the precedence problem prevents it and causes a crash.

I think you can go ahead and apply your fix, for this reason.

I think there may be more to this bug than meets the eye.

If I add the debugging code in the attached patch, I get this​:

SV = PVHV(0x7fda9280aec0) at 0x7fda9282a938
REFCNT = 1
FLAGS = (OOK,SHAREKEYS,OVERLOAD)
AUX_FLAGS = 0
ARRAY = 0x7fda924098e0
KEYS = 0
FILL = 0 (cached = 0)
MAX = 7
RITER = -1
EITER = 0x0
RAND = 0x2da71023
NAMECOUNT = -3
ENAME = "​::​::", (null)

etc.

ENAME should never have null in it. That’s a bug, possibly elsewhere.
I need to dig deeper.

Actually two bugs. First, *​::​::=anything is assigning the empty string as a name to whatever hash is put there. Secondly, dump.c is dumping the empty string as (null), which should only be used for a null pointer. (The null-handling code was added when ename supported was not partially written and nulls were appearing in the array due to bugs elsewhere that were fixed before the code was merged.)

--

Father Chrysostomos

@p5pRT
Copy link
Author

p5pRT commented May 15, 2016

From @cpansprout

On Sat May 14 10​:24​:41 2016, sprout wrote​:

On Tue May 10 21​:13​:32 2016, sprout wrote​:

On Tue May 10 01​:00​:01 2016, hv wrote​:

This appears to be a precedence error, fixable with an extra pair
of
parens as below. I don't understand this area well though, and I'd
like to add a test that's less obscure than the reported test case.

Any suggestions?

This is the simplest test case I can come up with​:

%h; *​::​::​::=*h; delete $​::{"​::"}
...
I think you can go ahead and apply your fix, for this reason.

I have applied your patch in 60a26c7. Thank you.

I have applied the test in 7f1bd06.

I think there may be more to this bug than meets the eye.

If I add the debugging code in the attached patch, I get this​:

SV = PVHV(0x7fda9280aec0) at 0x7fda9282a938
REFCNT = 1
FLAGS = (OOK,SHAREKEYS,OVERLOAD)
AUX_FLAGS = 0
ARRAY = 0x7fda924098e0
KEYS = 0
FILL = 0 (cached = 0)
MAX = 7
RITER = -1
EITER = 0x0
RAND = 0x2da71023
NAMECOUNT = -3
ENAME = "​::​::", (null)

etc.

ENAME should never have null in it. That’s a bug, possibly
elsewhere.
I need to dig deeper.

Actually two bugs. First, *​::​::=anything is assigning the empty
string as a name to whatever hash is put there. Secondly, dump.c is
dumping the empty string as (null), which should only be used for a
null pointer.

I have fixed dump.c in 8e5993c.

As for the discrepancy in how :​::​: names are parsed, I think we can leave things as they are unless some other real bug shows up as a result.

I am marking this ticket as resolved.

--

Father Chrysostomos

@p5pRT
Copy link
Author

p5pRT commented May 15, 2016

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