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

Segfault (memory access violation) in Perl__invlist_intersection_maybe_complement_2nd in Perl 5.30.0 #17154

Closed
p5pRT opened this issue Sep 17, 2019 · 20 comments

Comments

@p5pRT
Copy link

p5pRT commented Sep 17, 2019

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

Searchable as RT134439$

@p5pRT
Copy link
Author

p5pRT commented Sep 17, 2019

From steven.bush@3ds.com

Content-Type​: multipart/mixed; boundary="------------1.41.perlbug"
Reply-To​: steven.bush@​3ds.com
Subject​: Segfault (memory access violation) in Perl__invlist_intersection_maybe_complement_2nd in Perl 5.30.0
Message-Id​: <5.30.0_14012_1568754224@​mailhost.emea.3ds.com>
MIME-Version​: 1.0
To​: perlbug@​perl.org
From​: steven.bush@​3ds.com

This is a multi-part message in MIME format.
--------------1.41.perlbug
Content-Type​: text/plain; format=fixed
Content-Transfer-Encoding​: 8bit

This is a bug report for perl from steven.bush@​3ds.com,
generated with the help of perlbug 1.41 running under perl 5.30.0.

Summary​: Segfault (memory access violation) in Perl__invlist_intersection_maybe_complement_2nd during eval() after Perl parser embedded in C++ program is destructed pe_destruct() after upgrading from perl-5.26.3 to perl-5.30.0. Also seen in perl-5.28.2

Description​:
Our C++ based application embeds a perl parser as described here​: https://perldoc.perl.org/perlembed.html
We build with -Dmultiplicity
1. We call perl_alloc(), perl_construct(), evaluate a particular script, then perl_destruct(), perl_free()
2. Later, we again perl_alloc(), perl_construct(), evaluate, perl_destruct(), perl_free()

With Perl-5.26.3 and earlier, this works successfully.
However, with Perl-5.30.0 (and later tested and also seen with Perl-5.28.2)​:
During step 1, a simple script parses and executes successfully.
During step 2, an identical script crashes with a segfault on linux (memory access violation on Windows)
The stack trace from gdb is​:

#0 0x000000336d689a54 in memcpy () from /lib64/libc.so.6
#1 0x00007fe26c858512 in Perl__invlist_intersection_maybe_complement_2nd ()
  from /home/scitegicuser/dev/plp/platform/pc_i/PI/scitegic_root/apps/scitegic/core/packages_linux64/perl/perl-5.30.0/lib/5.30.0/x86_64-linux-thread-multi/CORE/libperl.so
#2 0x00007fe26c858767 in S_populate_ANYOF_from_invlist () from /home/scitegicuser/dev/plp/platform/pc_i/PI/scitegic_root/apps/scitegic/core/packages_linux64/perl/perl-5.30.0/lib/5.30.0/x86_64-linux-thread-multi/CORE/libperl.so
#3 0x00007fe26c874236 in S_regclass () from /home/scitegicuser/dev/plp/platform/pc_i/PI/scitegic_root/apps/scitegic/core/packages_linux64/perl/perl-5.30.0/lib/5.30.0/x86_64-linux-thread-multi/CORE/libperl.so
#4 0x00007fe26c8619ee in S_regpiece () from /home/scitegicuser/dev/plp/platform/pc_i/PI/scitegic_root/apps/scitegic/core/packages_linux64/perl/perl-5.30.0/lib/5.30.0/x86_64-linux-thread-multi/CORE/libperl.so
#5 0x00007fe26c866126 in S_regbranch () from /home/scitegicuser/dev/plp/platform/pc_i/PI/scitegic_root/apps/scitegic/core/packages_linux64/perl/perl-5.30.0/lib/5.30.0/x86_64-linux-thread-multi/CORE/libperl.so
#6 0x00007fe26c8667f4 in S_reg () from /home/scitegicuser/dev/plp/platform/pc_i/PI/scitegic_root/apps/scitegic/core/packages_linux64/perl/perl-5.30.0/lib/5.30.0/x86_64-linux-thread-multi/CORE/libperl.so
<snip>
#40 0x00007fe26c812e88 in Perl_eval_pv () from /home/scitegicuser/dev/plp/platform/pc_i/PI/scitegic_root/apps/scitegic/core/packages_linux64/perl/perl-5.30.0/lib/5.30.0/x86_64-linux-thread-multi/CORE/libperl.so

I compiled perl-5.30.0 with debugging enabled and tracked the problem down as follows​:

The crash is occurring with regex expressions containing character sets "[abc]"

During second Perl evaluation, the debugger broke on an assert inside S__invlist_len which is called from Perl__invlist_intersection_maybe_complement_2nd.

  if ((len_a == 0) || ((len_b = _invlist_len(b)) == 0)) {

In this case, "b" is an SV struct which is invalid​:
  b 0x0000000001d13d98
  sv_any 0x0000000000000000 void *
  sv_refcnt 0 unsigned long
  sv_flags 0 unsigned long

Looking further up the stack to S_populate_ANYOF_from_invlist(), b has been passed in PL_InBitmap which is a global variable defined in embedvar.h

PL_InBitmap is initialized within Perl_re_op_compile() which has changed within regcomp.c from perl-5.26.1 to perl-5.30.0 as follows​:
5.26.1​:
  if (! PL_AboveLatin1) {
  #ifdef DEBUGGING
  char * dump_len_string;
  #endif

  PL_AboveLatin1 = _new_invlist_C_array(AboveLatin1_invlist);
  PL_Latin1 = _new_invlist_C_array(Latin1_invlist);
  PL_UpperLatin1 = _new_invlist_C_array(UpperLatin1_invlist);
  PL_utf8_foldable = _new_invlist_C_array(_Perl_Any_Folds_invlist);
  PL_HasMultiCharFold =
  _new_invlist_C_array(_Perl_Folds_To_Multi_Char_invlist);

  /* This is calculated here, because the Perl program that generates the
  * static global ones doesn't currently have access to
  * NUM_ANYOF_CODE_POINTS */
  PL_InBitmap = _new_invlist(2);
  PL_InBitmap = _add_range_to_invlist(PL_InBitmap, 0,
  NUM_ANYOF_CODE_POINTS - 1);

5.30.0/5.28.2​:
  if (! PL_InBitmap) {
  #ifdef DEBUGGING
  char * dump_len_string;
  #endif

  /* This is calculated here, because the Perl program that generates the
  * static global ones doesn't currently have access to
  * NUM_ANYOF_CODE_POINTS */
  PL_InBitmap = _new_invlist(2);
  PL_InBitmap = _add_range_to_invlist(PL_InBitmap, 0,
  NUM_ANYOF_CODE_POINTS - 1);

In 5.30.0, PL_AboveLatin1 is initialized in a function at the bottom of regcomp.c​:
  void
  Perl_init_uniprops(pTHX)
  {
  //.........
  PL_AboveLatin1 = _new_invlist_C_array(AboveLatin1_invlist);

On the first call to Perl_re_op_compile() during the first script evaluation, PL_InBitmap = NULL and the initializer is called and PL_InBitmap's contents are valid.

However, after calling perl_destruct(), PL_InBitmap is no longer NULL, but the contents of PL_InBitmap have been wiped out.

Stepping through perl_destruct() in perl.c after the first script eval, PL_InBitmap becomes corrupt when this code executes​:
  /* the 2 is for PL_fdpid and PL_strtab */
  while (sv_clean_all() > 2) /// <<<<<<<<------ wipes out contents of PL_inBitmap
  ;

This actually wipes out the contents of all of the PL_ globals (such as PL_AboveLatin1, PL_Latin1, PL_UpperLatin1), however those globals are reinitialized regardless of current status during perl_construct() when the function calls init_uniprops()

I modified the code in perl_destruct() as follows​:
  /* the 2 is for PL_fdpid and PL_strtab */
  while (sv_clean_all() > 2) // <<<<<<<<------ wipes out contents of PL_inBitmap
  ;
  PL_InBitmap = NULL; // <<<<<<<<------ Null pointer to force reinitialization during Perl_re_op_compile()

After recompiling, all of our product tests passed successfully.


Flags​:
  category=core
  severity=critical


Site configuration information for perl 5.30.0​:

Configured by scitegicuser at Thu Jun 6 13​:23​:20 2019.

Summary of my perl5 (revision 5 version 30 subversion 0) configuration​:

  Platform​:
  osname=MSWin32
  osvers=6.1.7601
  archname=MSWin32-x64-multi-thread
  uname=''
  config_args='undef'
  hint=recommended
  useposix=true
  d_sigaction=undef
  useithreads=define
  usemultiplicity=define
  use64bitint=define
  use64bitall=undef
  uselongdouble=undef
  usemymalloc=n
  default_inc_excludes_dot=define
  bincompat5005=undef
  Compiler​:
  cc='cl'
  ccflags ='-nologo -GF -W3 -O1 -MD -Zi -DNDEBUG -GL -fp​:precise -DWIN32 -D_CONSOLE -DNO_STRICT -DWIN64 -DCONSERVATIVE -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -D_WINSOCK_DEPRECATED_NO_WARNINGS -DPERL_TEXTMODE_SCRIPTS -DPERL_IMPLICIT_CONTEXT -DPERL_IMPLICIT_SYS'
  optimize='-O1 -MD -Zi -DNDEBUG -GL -fp​:precise'
  cppflags='-DWIN32'
  ccversion='19.00.24240'
  gccversion=''
  gccosandvers=''
  intsize=4
  longsize=4
  ptrsize=8
  doublesize=8
  byteorder=12345678
  doublekind=3
  d_longlong=undef
  longlongsize=8
  d_longdbl=define
  longdblsize=8
  longdblkind=0
  ivtype='__int64'
  ivsize=8
  nvtype='double'
  nvsize=8
  Off_t='__int64'
  lseeksize=8
  alignbytes=8
  prototype=define
  Linker and Libraries​:
  ld='link'
  ldflags ='-nologo -nodefaultlib -debug -opt​:ref,icf -ltcg -libpath​:"C​:\dev\plp\platform\pc_i\PI\scitegic root\apps\scitegic\core\packages_win64\perl\perl-5.30.0\lib\CORE" -machine​:AMD64 -subsystem​:console,"5.02"'
  libpth="C​:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib\amd64"
  libs=oldnames.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib netapi32.lib uuid.lib ws2_32.lib mpr.lib winmm.lib version.lib odbc32.lib odbccp32.lib comctl32.lib msvcrt.lib vcruntime.lib ucrt.lib
  perllibs=oldnames.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib netapi32.lib uuid.lib ws2_32.lib mpr.lib winmm.lib version.lib odbc32.lib odbccp32.lib comctl32.lib msvcrt.lib vcruntime.lib ucrt.lib
  libc=ucrt.lib
  so=dll
  useshrplib=true
  libperl=perl530.lib
  gnulibc_version=''
  Dynamic Linking​:
  dlsrc=dl_win32.xs
  dlext=dll
  d_dlsymun=undef
  ccdlflags=' '
  cccdlflags=' '
  lddlflags='-dll -nologo -nodefaultlib -debug -opt​:ref,icf -ltcg -libpath​:"C​:\dev\plp\platform\pc_i\PI\scitegic root\apps\scitegic\core\packages_win64\perl\perl-5.30.0\lib\CORE" -machine​:AMD64 -subsystem​:console,"5.02"'


@​INC for perl 5.30.0​:
  c​:/dev/plp/platform/pc_i/PI/scitegic root/apps/scitegic/core/packages_win64/perl/perl-5.30.0/site/lib
  c​:/dev/plp/platform/pc_i/PI/scitegic root/apps/scitegic/core/packages_win64/perl/perl-5.30.0/lib


Environment for perl 5.30.0​:
  HOME (unset)
  LANG (unset)
  LANGUAGE (unset)
  LD_LIBRARY_PATH (unset)
  LOGDIR (unset)
  PATH=C​:\Program Files\Microsoft MPI\Bin\;C​:\Windows\system32;C​:\Windows;C​:\Windows\System32\Wbem;C​:\Windows\System32\WindowsPowerShell\v1.0\;C​:\Program Files\Perforce;C​:\Program Files (x86)\Windows Kits\8.1\Windows Performance Toolkit\;C​:\Program Files\Microsoft SQL Server\110\Tools\Binn\;C​:\Program Files (x86)\Microsoft SDKs\TypeScript\1.0\;C​:\Program Files\Microsoft SQL Server\120\Tools\Binn\;C​:\Program Files (x86)\Xoreax\IncrediBuild;C​:\Program Files\Perforce\DVCS\;C​:\Users\sbh1\.dnx\bin;C​:\Program Files\Microsoft DNX\Dnvm\;C​:\Program Files\Microsoft SQL Server\130\Tools\Binn\;C​:\Program Files\Microsoft MPI\Bin\;C​:\Windows\system32;C​:\Windows;C​:\Windows\System32\Wbem;C​:\Windows\System32\WindowsPowerShell\v1.0\;C​:\Program Files\Perforce;C​:\Program Files (x86)\Windows Kits\8.1\Windows Performance Toolkit\;C​:\Program Files\Microsoft SQL Server\110\Tools\Binn\;C​:\Program Files
(x86)\Microsoft SDKs\TypeScript\1.0\;C​:\Program Files\Microsoft SQL Server\120\Tools\Binn\;C​:\Program Files (x86)\Xoreax\IncrediBuild;C​:\Program Files\Perforce\DVCS\;C​:\Users\sbh1\.dnx\bin;C​:\Program Files\Microsoft DNX\Dnvm\;C​:\Program Files\Microsoft SQL Server\130\Tools\Binn\;c​:\dev\plp\platform\pc_i\PI\scitegic root\apps\scitegic\core\packages_win64\perl\perl-5.30.0\bin;c​:\bats;c​:\python27;C​:\Program Files (x86)\nasm;C​:\Program Files (x86)\CMake\bin;D​:\dev\plp\platform\trunk\PI\scitegic root\apps\scitegic\openssl\bin\win64
  PERL_BADLANG (unset)
  SHELL (unset)

--------------1.41.perlbug
Content-Type​: text/x-patch; name="perl.c-patch"
Content-Transfer-Encoding​: 8bit
Content-Disposition​: attachment; filename="perl.c-patch"

--- C​:/dev/perl.c Tue Sep 17 13​:21​:31 2019

+++ C​:/dev/perl.c-FIXED Tue Sep 17 12​:12​:36 2019

@​@​ -1254,6 +1254,7 @​@​

  /* the 2 is for PL_fdpid and PL_strtab */

  while (sv_clean_all() > 2)

  ;

+ PL_InBitmap = NULL;

#ifdef USE_ITHREADS

  Safefree(PL_stashpad); /* must come after sv_clean_all */

--------------1.41.perlbug--
This email and any attachments are intended solely for the use of the individual or entity to whom it is addressed and may be confidential and/or privileged.

If you are not one of the named recipients or have received this email in error,

(i) you should not read, disclose, or copy it,

(ii) please notify sender of your receipt by reply email and delete this email and all attachments,

(iii) Dassault Systèmes does not accept or assume any liability or responsibility for any use of or reliance on this email.

Please be informed that your personal data are processed according to our data privacy policy as described on our website. Should you have any questions related to personal data protection, please contact 3DS Data Protection Officer at 3DS.compliance-privacy@​3ds.com<mailto​:3DS.compliance-privacy@​3ds.com>

For other languages, go to https://www.3ds.com/terms/email-disclaimer

@p5pRT
Copy link
Author

p5pRT commented Sep 18, 2019

From @tonycoz

On Tue, 17 Sep 2019 14​:50​:07 -0700, steven.bush@​3ds.com wrote​:

The crash is occurring with regex expressions containing character
sets "[abc]"

Do you have a more complete example of this?

I've tried some simple code that exercises regexps with [abc], but haven't managed to make it fault, even with valgrind running the executable.

My test code is running the code twice and PL_InBitmap is the same after each run, so I can see there is a problem, but I'm not seeing the crash, which I'd like for a regression test.

Thanks,
Tony

@p5pRT
Copy link
Author

p5pRT commented Sep 18, 2019

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

@p5pRT
Copy link
Author

p5pRT commented Sep 18, 2019

From @khwilliamson

On Tue, 17 Sep 2019 23​:03​:05 -0700, tonyc wrote​:

On Tue, 17 Sep 2019 14​:50​:07 -0700, steven.bush@​3ds.com wrote​:

The crash is occurring with regex expressions containing character
sets "[abc]"

Do you have a more complete example of this?

I've tried some simple code that exercises regexps with [abc], but
haven't managed to make it fault, even with valgrind running the
executable.

My test code is running the code twice and PL_InBitmap is the same
after each run, so I can see there is a problem, but I'm not seeing
the crash, which I'd like for a regression test.

Thanks,
Tony

It would be great to have the data for a regression test, but Steven has done an admirable job of diagnosing the problem. I prefer the attached patch, as it keeps the initialization of all the similar variables in the same place
--
Karl Williamson

@p5pRT
Copy link
Author

p5pRT commented Sep 18, 2019

From @khwilliamson

0005-PATCH-perl-134339-Segfault-in-embedded-use.patch
From e2af073f0263f3725a7f775de425bfd9f0a5dcbd Mon Sep 17 00:00:00 2001
From: Karl Williamson <khw@cpan.org>
Date: Wed, 18 Sep 2019 09:18:56 -0600
Subject: [PATCH 5/5] PATCH: [perl #134339] Segfault in embedded use.

Insert verbiage here
---
 regcomp.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/regcomp.c b/regcomp.c
index 531cf306c9..f176c472ea 100644
--- a/regcomp.c
+++ b/regcomp.c
@@ -22191,6 +22191,8 @@ Perl_init_uniprops(pTHX)
     PL_Latin1 = _new_invlist_C_array(Latin1_invlist);
     PL_UpperLatin1 = _new_invlist_C_array(UpperLatin1_invlist);
 
+    PL_InBitmap = NULL; /* Force run-time initialization [perl #134439] */
+
     PL_Assigned_invlist = _new_invlist_C_array(uni_prop_ptrs[UNI_ASSIGNED]);
 
     PL_utf8_perl_idstart = _new_invlist_C_array(uni_prop_ptrs[UNI__PERL_IDSTART]);
-- 
2.17.1

@p5pRT
Copy link
Author

p5pRT commented Sep 18, 2019

From steven.bush@3ds.com

Apologies if my message is duplicated. I replied in e-mail to the comments earlier today, but the response did not appear on the site (maybe I'm not patient enough).

I agree that khw's patch is better than my initial patch​: it has the same behavior but keeps things centralized.

I put together the following C++ test harness that reliably replicates the crashing behavior on my laptop. It requires File​::Spec. The crash occurs while parsing the regex from PathTools Cwd.pm​: "^[ET1]"

#include <EXTERN.h>
#include <perl.h>

int main(int argc, char* argv[])
{
  static const char* TestPerlScript = "use File​::Spec;\n";
  char *args[] =
  {
  "",
  "-e",
  "0",
  0
  };
  int args_c = sizeof(args) / sizeof(*args);
  char** args_v = args;

  PERL_SYS_INIT3(&args_c, &args_v, NULL);
  PerlInterpreter* my_perl = perl_alloc();

  perl_construct(my_perl);
  perl_parse(my_perl, NULL, 3, args, NULL);
  perl_run(my_perl);

  eval_pv(TestPerlScript, TRUE);

  perl_destruct(my_perl);
  perl_free(my_perl);
  my_perl = NULL;
 
  printf("PASS 1 Success\n----\n");

  my_perl = perl_alloc();
  perl_construct(my_perl);
  perl_parse(my_perl, NULL, 3, args, NULL);
  perl_run(my_perl);

/* CRASHES HERE */
  eval_pv(TestPerlScript, TRUE);

  perl_destruct(my_perl);
  perl_free(my_perl);
  printf("PASS 2 Success\n----\n");

  PERL_SYS_TERM();
  return 0;
}

On Wed, 18 Sep 2019 08​:23​:38 -0700, khw wrote​:

On Tue, 17 Sep 2019 23​:03​:05 -0700, tonyc wrote​:

On Tue, 17 Sep 2019 14​:50​:07 -0700, steven.bush@​3ds.com wrote​:

The crash is occurring with regex expressions containing character
sets "[abc]"

Do you have a more complete example of this?

I've tried some simple code that exercises regexps with [abc], but
haven't managed to make it fault, even with valgrind running the
executable.

My test code is running the code twice and PL_InBitmap is the same
after each run, so I can see there is a problem, but I'm not seeing
the crash, which I'd like for a regression test.

Thanks,
Tony

It would be great to have the data for a regression test, but Steven
has done an admirable job of diagnosing the problem. I prefer the
attached patch, as it keeps the initialization of all the similar
variables in the same place

@p5pRT
Copy link
Author

p5pRT commented Sep 19, 2019

From @tonycoz

On Wed, Sep 18, 2019 at 08​:23​:38AM -0700, Karl Williamson via RT wrote​:

On Tue, 17 Sep 2019 23​:03​:05 -0700, tonyc wrote​:

On Tue, 17 Sep 2019 14​:50​:07 -0700, steven.bush@​3ds.com wrote​:

The crash is occurring with regex expressions containing character
sets "[abc]"

Do you have a more complete example of this?

I've tried some simple code that exercises regexps with [abc], but
haven't managed to make it fault, even with valgrind running the
executable.

My test code is running the code twice and PL_InBitmap is the same
after each run, so I can see there is a problem, but I'm not seeing
the crash, which I'd like for a regression test.

Thanks,
Tony

It would be great to have the data for a regression test, but Steven has done an admirable job of diagnosing the problem. I prefer the attached patch, as it keeps the initialization of all the similar variables in the same place

I wonder if this is just generally unsafe.

Could PL_InBitmap be initialized by a child thread?

This would leave PL_InBitmap invalid once the child thread exits.

It's also possible a CPAN module could call perl_construct(), and
threads​::lite does just that, which would overwrite PL_InBitmap with a
new SV that would no longer be valid once the thread exits.

The other inversion list globals have similar issues, as far as I can
see.

Tony

@p5pRT
Copy link
Author

p5pRT commented Sep 20, 2019

From steven.bush@3ds.com

Hi Tony,

I've had the same problem of trying to create a simple test harness for this. The perl regcomp code crashes consistently within our full sized application, but the script we are loading is moderately complex and includes a number of other modules. In our case, from stepping through the debugger on Windows, the regex that is causing problems is coming from Cwd.pm​:
  $unix_rpt = $env_unix_rpt =~ /^[ET1]/i;

But when I try that in a test harness, it does not crash

The code that we have that pulls in the code is this​:
use strict;
use File​::Spec; <<<--- this is the one that brings in the regex from Cwd.pm "^[ET1]"
use File​::Path;

I put together the following test C++ based test harness that seems to do the trick. This was wrapped into a simple main() with #include <EXTERN.h> and <perl.h> at the top

  static const char* TestPerlScript = "use File​::Spec;\n";
//Works okay​: static const char* TestPerlScript = "use strict;\n";
//Works okay​: static const char* TestPerlScript = "$a = 1;\n";
  char *args[] =
  {
  "",
  "-e",
  "0",
  0
  };
  int argc = sizeof(args) / sizeof(*args);
  char** argv = args;

  PERL_SYS_INIT3(&argc, &argv, NULL);
  PerlInterpreter* my_perl = perl_alloc();

  perl_construct(my_perl);
  perl_parse(my_perl, NULL, 3, args, NULL);
  perl_run(my_perl);

  eval_pv(TestPerlScript, TRUE);

  perl_destruct(my_perl);
  perl_free(my_perl);
  my_perl = NULL;

  printf("PASS 1 Success\n----\n");

  my_perl = perl_alloc();
  perl_construct(my_perl);
  perl_parse(my_perl, NULL, 3, args, NULL);
  perl_run(my_perl);

  eval_pv(TestPerlScript, TRUE); //<-------- SEGFAULTS HERE

  perl_destruct(my_perl);
  perl_free(my_perl);
  printf("PASS 2 Success\n----\n");

  PERL_SYS_TERM();

-----Original Message-----
From​: Tony Cook via RT [mailto​:perlbug-followup@​perl.org]
Sent​: Tuesday, September 17, 2019 11​:03 PM
To​: BUSH Steve
Subject​: [perl #134439] Segfault (memory access violation) in Perl__invlist_intersection_maybe_complement_2nd in Perl 5.30.0

On Tue, 17 Sep 2019 14​:50​:07 -0700, steven.bush@​3ds.com wrote​:

The crash is occurring with regex expressions containing character
sets "[abc]"

Do you have a more complete example of this?

I've tried some simple code that exercises regexps with [abc], but haven't managed to make it fault, even with valgrind running the executable.

My test code is running the code twice and PL_InBitmap is the same after each run, so I can see there is a problem, but I'm not seeing the crash, which I'd like for a regression test.

Thanks,
Tony
This email and any attachments are intended solely for the use of the individual or entity to whom it is addressed and may be confidential and/or privileged.

If you are not one of the named recipients or have received this email in error,

(i) you should not read, disclose, or copy it,

(ii) please notify sender of your receipt by reply email and delete this email and all attachments,

(iii) Dassault Systèmes does not accept or assume any liability or responsibility for any use of or reliance on this email.

Please be informed that your personal data are processed according to our data privacy policy as described on our website. Should you have any questions related to personal data protection, please contact 3DS Data Protection Officer at 3DS.compliance-privacy@​3ds.com<mailto​:3DS.compliance-privacy@​3ds.com>

For other languages, go to https://www.3ds.com/terms/email-disclaimer

@khwilliamson
Copy link
Contributor

Looking at perl.c, I see that it skips cleaning up the ENV variables if it isn't the main thread. Could that work for this issue as well?

@tonycoz
Copy link
Contributor

tonycoz commented Nov 26, 2019

The problem is all SVs are released when the interpreter (per thread) is released, so a PL_InBitmap allocated by a child thread is released when child thread ends. Similarly in this embedded case where a process creates an interpreter, populates PL_InBitmap, and releases the interpreter, freeing the SV, leaving PL_InBitmap invalid.

I can only see two fixes:

  • make PL_InBitmap part of the interpreter rather than global, so each interpreter creates it's own copy (which looks fairly cheap)
  • don't use SVs for invlists, and either allocate the new structure (with safesysalloc(), not Safealloc()), or statically initialize PL_InBitmap.

The current initialization has a race condition too - it's possible for two child threads to reach the initialization in re_op_compile() and both try to initialize PL_InBitmap.

@khwilliamson
Copy link
Contributor

I already have a patch prepared that makes PL_InBitmap work just like all the other inversion list globals. Earlier you said "The other inversion list globals have similar issues, as far as I can
see."

Would those issues go away if they were only cleaned up by the main thread?

@tonycoz
Copy link
Contributor

tonycoz commented Nov 27, 2019

The problem the other inversion list globals is with code like threads::lite that call perl_construct().

Normal threading code calls perl_clone() which duplicates the parent interpreter and doesn't call init_uniprops() so it leaves the inversion list globals alone. Since the parent waits on the child threads there's no lifetime issue.

threads::lite, and possibly other code (mod_perl?) calls perl_construct() which initializes PL_AboveLatin1 etc, so you can get a sequence of events like:

  1. main interpreter initializes PL_AboveLatin1 to an SV it owns
  2. loads threads::lite and creates a new thread/interpreter which initializes PL_AboveLatin1 to a SV owned by the new interpreter
  3. threads::lite child interpreter finishes, freeing all of its SVs, PL_AboveLatin1 is now invalid
  4. main interpreter uses a regexp that relies on PL_AboveLatin1, dies horribly.

threads::lite isn't doing anything horrible by calling perl_construct(), this should be safe for it to do, so it seems like a fault in the way those variables are handled.

They're all initialized from static arrays and don't copy those arrays, so they're relatively cheap to create, so I could see them as interpreter variables, but there are a lot of them

Also, using globals is broken if the code ever iterates over them, though I don't know if any code ever does. (Moving the iterator state out of the SV would fix that.)

khwilliamson added a commit that referenced this issue Nov 27, 2019
This is part of fixing gh #17154

This scenario from the ticket
(#17154 (comment))
shows why this fix is necessary:

    main interpreter initializes PL_AboveLatin1 to an SV it owns

    loads threads::lite and creates a new thread/interpreter which
    initializes PL_AboveLatin1 to a SV owned by the new interpreter

    threads::lite child interpreter finishes, freeing all of its SVs,
    PL_AboveLatin1 is now invalid

    main interpreter uses a regexp that relies on PL_AboveLatin1, dies
    horribly.

By making these interpreter level variables, this is avoided.  There is
extra copying, but it is just the SV headers, as the real data is kept
as static C arrays.
khwilliamson added a commit that referenced this issue Nov 27, 2019
This makes it consistent with the other inversion lists for this sort of
thing, and finishes the fix for GH #17154
@khwilliamson
Copy link
Contributor

This is now fixed in blead by the two commits referred to above. I'm working on a patch suitable for backporting to 5.30. Steve, let me know if you want that.

@dur-randir
Copy link
Member

Changing interpreter struct size breaks BOOT checks, so can't be backported.

khwilliamson added a commit that referenced this issue Nov 29, 2019
This makes it consistent with the other inversion lists for this sort of
thing, and finishes the fix for GH #17154
@mvohlken
Copy link

mvohlken commented Mar 2, 2020

Changing interpreter struct size breaks BOOT checks, so can't be backported.

I noticed that commit ffffbf0 is not on the bleed branch and is not tagged with v5.31.9. Is that intentional? Is ffffbf0 needed to fix this issue in 5.32?

@khwilliamson
Copy link
Contributor

I looked at blead and found that commit. It is needed to fix 5.32, and it is in blead:

commit ffffbf0
Author: Karl Williamson khw@cpan.org
AuthorDate: Sun Nov 24 21:21:39 2019 -0700
Commit: Karl Williamson khw@cpan.org
CommitDate: Fri Nov 29 12:01:03 2019 -0700

 Move data for PL_InBitmap to charclass_invlists.h
 
 This makes it consistent with the other inversion lists for this sort of
 thing, and finishes the fix for GH #17154

@soig
Copy link

soig commented Mar 31, 2020

I would be interested in a backported fix for 5.30 as it affects nginx: See https://bugs.mageia.org/show_bug.cgi?id=26397

@jacquesg
Copy link
Contributor

jacquesg commented May 4, 2020

Also affected by this on 5.30.

@khwilliamson
Copy link
Contributor

I am willing to worth with people to get a patch available for earlier versions, even if it can't be an official backport.

khwilliamson added a commit that referenced this issue Jun 1, 2020
This resolves #17774.

This ticket is because the fixes in GH #17154 failed to get every case,
leaving this one outlier to be fixed by this commit.

The text in #17154 gives extensive
details as to the problem.  But briefly, in an attempt to speed up
interpreter cloning, I moved certain SVs from interpreter level to
global level in e80a011 (v5.27.11,
March 2018).  This was doable, we thought, because the content of these
SVs is constant throughout the life of the program, so no need to copy
them when cloning a new interpreter or thread.  However when an
interpreter exits, all its SVs get cleaned up, which caused these to
become garbage in applications where another interpreter remains
running.  This circumstance is rare enough that the bug wasn't reported
until September 2019,  #17154.  I made an initial attempt to fix the
problem, and closed that ticket, but I overlooked one of the variables,
which was reported in #17774, which this commit addresses.

Effectively the behavior is reverted to the way it was before
e80a011.
xsawyerx pushed a commit that referenced this issue Jun 2, 2020
This resolves #17774.

This ticket is because the fixes in GH #17154 failed to get every case,
leaving this one outlier to be fixed by this commit.

The text in #17154 gives extensive
details as to the problem.  But briefly, in an attempt to speed up
interpreter cloning, I moved certain SVs from interpreter level to
global level in e80a011 (v5.27.11,
March 2018).  This was doable, we thought, because the content of these
SVs is constant throughout the life of the program, so no need to copy
them when cloning a new interpreter or thread.  However when an
interpreter exits, all its SVs get cleaned up, which caused these to
become garbage in applications where another interpreter remains
running.  This circumstance is rare enough that the bug wasn't reported
until September 2019,  #17154.  I made an initial attempt to fix the
problem, and closed that ticket, but I overlooked one of the variables,
which was reported in #17774, which this commit addresses.

Effectively the behavior is reverted to the way it was before
e80a011.
@khwilliamson
Copy link
Contributor

@soig @mvohlken

I have worked up a patch to apply to 5.30.2 that fixes both this and #17774 . Should I try a 5.28 one too? The file extension here is .txt because github won't let me upload a .patch file.
17154.txt

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

8 participants