Hi, After further tracing around the sources, I think I've figure out the root cause of the fault: Tcl interpreter (not Tcl.so, which is compiled from Tcl.xs) has changed global environment variable of the process while Perl is not aware of it. In more detail: 1. Tcl allocated a chunk of memory space to store string 'FOO=bar' while the code 'set env(FOO) bar' gets executed 2. Tcl modifies the global variable 'environ' (exported by glibc with type: char**). Then it points to a pointer array with one element points to the newly allocated string 'FOO=bar'. 3. On exiting of Perl, the destructor of the Tcl interpreter object gets invoked. The memory chunk with string 'FOO=bar' gets freed by Tcl. However, the global variable 'environ' still points to the same location and is still has the element points to the address 'FOO=bar'. 4. Finally Perl thinks the strings pointed by 'environ' was allocated by itself and calls free() on each element, where one points to string 'FOO=bar', who has already been freed...
To demo the Perl/Tcl behavior, execute perl -MTcl -e 'Tcl::new->Eval("set env(FOO) bar"); print "FOO=$ENV{FOO}\n"; system("echo FOO=\$FOO");' We get the following output before segmentation fault FOO= FOO=bar Here Perl's $ENV{FOO} is still undefined while the true environment of the process is practically affected because the child process inherits 'FOO=bar'. Moreover, if we have $ENV{FOO}="bar1" after Eval("set env...."), we will get segmentation fault on it immediately, before exiting of Perl. So far I am not able to come out a simple way to get it fixed. I think either Tcl should not change the environment globally, or there should be some way to inform Perl that environment variables has been changed. SJ S.J. Luo <sja...@gmail.com> 於 2020年3月22日 週日 下午10:06寫道: > > Hi, > I'd like to share my finding. > I also duplicated the error on CentOS 7, who comes with perl 5.16 > installed and tcl-8.5. > while on my Cygwin build there is no problem. > After some tracing, I found the error point is at the perl_destruct() > calling Perl_safesysfree() in perl.c: > > > https://perl5.git.perl.org/perl5.git/blob/06d742c02d396d6515581002f376b55ab1972c1b:/perl.c#l811 > > There is a 'char **environ' for the environment variable string list > and calling Perl_safesysfree(environ[i]) to free the memory space. > However, I found the pointer value passed into Perl_safesysfree() > differs from that reported by gdb 'p environ[0]' command. > Here I have a demo of the gdb tracing result. There are two > 'environ[0]'s: One is at address 0x7fffffffe70e (report by gdb) > and the other is at 0x618330 (actually passed into Perl_safesysfree). > Both of them have the same string content "USER=...". > However, later has one additional item in its string list: "FOO=bar" > while gdb reported one does not have it. > Freeing this additional string leads to the error. > Its seems to be issue of compiler optimization. I am going to digging > into it later. > > -------------------- > (gdb) set breakpoint pending on > (gdb) b perl.c:811 > Breakpoint 1 (perl.c:811) pending. > (gdb) r > Starting program: /usr/bin/perl -MTcl -e Tcl::new-\>Eval\(\"set\ > env\(FOO\)\ bar\"\) > [Thread debugging using libthread_db enabled] > Using host libthread_db library "/lib64/libthread_db.so.1". > [New Thread 0x7fffefa8f700 (LWP 6045)] > [Thread 0x7fffefa8f700 (LWP 6045) exited] > > Breakpoint 1, perl_destruct (my_perl=0x603010) at perl.c:811 > 811 safesysfree(environ[i]); > Missing separate debuginfos, use: debuginfo-install > libgcc-4.8.5-39.el7.x86_64 nss-softokn-freebl-3.44.0-8.el7_7.x86_64 > tcl-8.5.13-8.el7.x86 > (gdb) p environ > $1 = (char **) 0x7fffffffe460 > (gdb) p environ[0] > $2 = 0x7fffffffe70e "USER=crystal" > (gdb) p environ[1] > $3 = 0x7fffffffe71b "LOGNAME=crystal" > (gdb) step > Perl_safesysfree (where=0x618330) at util.c:254 > 254 if (where) { > (gdb) p (char*)where > $4 = 0x618330 "USER=crystal" > (gdb) finish > Run till exit from #0 Perl_safesysfree (where=0x618330) at util.c:254 > perl_destruct (my_perl=0x603010) at perl.c:810 > 810 for (i = 0; environ[i]; i++) > (gdb) step > > Breakpoint 1, perl_destruct (my_perl=0x603010) at perl.c:811 > 811 safesysfree(environ[i]); > (gdb) step > Perl_safesysfree (where=0x618350) at util.c:254 > 254 if (where) { > (gdb) p (char*)where > $5 = 0x618350 "LOGNAME=crystal" > -------------------- > SJ > > Konovalov, Vadim <vadim.konova...@dell.com> wrote > > > > You're ingenious! > > > > vad@bonita:~$ perl -MTcl -we 'my $i=new Tcl;$i->Init;$i->Eval("set env(FOO) > > bar");' > > free(): invalid size > > Aborted (core dumped) > > vad@bonita:~$ > > > > and even $i->Init not needed: > > > > vad@bonita:~$ perl -MTcl -we 'Tcl::new->Eval("set env(FOO) bar");' > > free(): invalid size > > Aborted (core dumped) > > > > Haven't looked deeper though, but the picture now is much more clear! > > Thanks! > > > > -----Original Message----- > > From: Christopher Chavez <chrischa...@gmx.us> > > Sent: Tuesday, March 17, 2020 6:08 AM > > To: tcltk@perl.org > > Subject: Re: Possible bug in Tcl.pm exposed by Tktable? > > > > > > [EXTERNAL EMAIL] > > > > On 3/16/2020 4:09 AM, Christopher Chavez wrote: > > > To partially answer my own question: > > > there are some compile-time techniques to try like -DPURIFY > > > https://wiki.tcl-lang.org/page/How+to+debug+memory+faults+in+Tcl+and+e > > > xtensions > > After build from upstream sources (Tcl/Tk core-8-6-branch and TkTable 2.11 > > -- no Debian/Ubuntu Tcl/Tk packages) with -DPURIFY, the error is revealed > > to be a double free. The addresses involved correspond to strings allocated > > for environment variable(s) set by Tktable (tkTableInitScript.h). > > > > A simpler program without Tk or Tktable reveals the same issue: > > > > use Tcl; > > > > my $i = new Tcl; > > $i->Init; > > $i->Eval('set env(FOO) bar'); > > > > > > Command line output: > > > > free(): double free detected in tcache 2 Aborted (core dumped) > > > > Valgrind output: > > > > ==13666== Invalid free() / delete / delete[] / realloc() > > ==13666== at 0x48369AB: free (vg_replace_malloc.c:530) > > ==13666== by 0x162304: perl_destruct (in /usr/bin/perl) > > ==13666== by 0x13C3DB: main (in /usr/bin/perl) > > ==13666== Address 0x5229a20 is 0 bytes inside a block of size 8 free'd > > ==13666== at 0x48369AB: free (vg_replace_malloc.c:530) > > ==13666== by 0x543EE86: TclpFree (tclAlloc.c:722) > > ==13666== by 0x5517935: TclFinalizeEnvironment (tclEnv.c:768) > > ==13666== by 0x5519268: Tcl_Finalize (tclEvent.c:1151) > > ==13666== by 0x485123D: XS_Tcl__Finalize (Tcl.xs:1449) > > ==13666== by 0x1F4360: Perl_pp_entersub (in /usr/bin/perl) > > ==13666== by 0x1EA685: Perl_runops_standard (in /usr/bin/perl) > > ==13666== by 0x15DF61: Perl_call_sv (in /usr/bin/perl) > > ==13666== by 0x160AC3: Perl_call_list (in /usr/bin/perl) > > ==13666== by 0x16235E: perl_destruct (in /usr/bin/perl) > > ==13666== by 0x13C3DB: main (in /usr/bin/perl) > > ==13666== Block was alloc'd at > > ==13666== at 0x4837D7B: realloc (vg_replace_malloc.c:826) > > ==13666== by 0x543EEAA: TclpRealloc (tclAlloc.c:747) > > ==13666== by 0x5456E8D: Tcl_Realloc (tclCkalloc.c:1147) > > ==13666== by 0x55172A9: TclSetEnv (tclEnv.c:317) > > ==13666== by 0x5517688: EnvTraceProc (tclEnv.c:636) > > ==13666== by 0x55A0B69: TclCallVarTraces (tclTrace.c:2678) > > ==13666== by 0x55A0860: TclObjCallVarTraces (tclTrace.c:2564) > > ==13666== by 0x55AAFDB: TclPtrSetVarIdx (tclVar.c:2001) > > ==13666== by 0x55AA957: Tcl_ObjSetVar2 (tclVar.c:1770) > > ==13666== by 0x55AA609: Tcl_SetObjCmd (tclVar.c:1529) > > ==13666== by 0x544A42A: Dispatch (tclBasic.c:4456) > > ==13666== by 0x544A4B0: TclNRRunCallbacks (tclBasic.c:4492) > > ==13666== by 0x5449D83: Tcl_EvalObjv (tclBasic.c:4215) > > ==13666== by 0x544C1AB: TclEvalEx (tclBasic.c:5361) > > ==13666== by 0x544B571: Tcl_EvalEx (tclBasic.c:5026) > > ==13666== by 0x48525A8: XS_Tcl_Eval (Tcl.xs:1097) > > ==13666== by 0x1F4360: Perl_pp_entersub (in /usr/bin/perl) > > ==13666== by 0x1EA685: Perl_runops_standard (in /usr/bin/perl) > > ==13666== by 0x166116: perl_run (in /usr/bin/perl) > > ==13666== by 0x13C401: main (in /usr/bin/perl) > > ==13666== > > ==13666== Invalid free() / delete / delete[] / realloc() > > ==13666== at 0x48369AB: free (vg_replace_malloc.c:530) > > ==13666== by 0x162322: perl_destruct (in /usr/bin/perl) > > ==13666== by 0x13C3DB: main (in /usr/bin/perl) > > ==13666== Address 0x5229810 is 0 bytes inside a block of size 376 free'd > > ==13666== at 0x48369AB: free (vg_replace_malloc.c:530) > > ==13666== by 0x543EE86: TclpFree (tclAlloc.c:722) > > ==13666== by 0x5517983: TclFinalizeEnvironment (tclEnv.c:776) > > ==13666== by 0x5519268: Tcl_Finalize (tclEvent.c:1151) > > ==13666== by 0x485123D: XS_Tcl__Finalize (Tcl.xs:1449) > > ==13666== by 0x1F4360: Perl_pp_entersub (in /usr/bin/perl) > > ==13666== by 0x1EA685: Perl_runops_standard (in /usr/bin/perl) > > ==13666== by 0x15DF61: Perl_call_sv (in /usr/bin/perl) > > ==13666== by 0x160AC3: Perl_call_list (in /usr/bin/perl) > > ==13666== by 0x16235E: perl_destruct (in /usr/bin/perl) > > ==13666== by 0x13C3DB: main (in /usr/bin/perl) > > ==13666== Block was alloc'd at > > ==13666== at 0x483577F: malloc (vg_replace_malloc.c:299) > > ==13666== by 0x543EE6C: TclpAlloc (tclAlloc.c:699) > > ==13666== by 0x5456D99: Tcl_Alloc (tclCkalloc.c:1059) > > ==13666== by 0x5517074: TclSetEnv (tclEnv.c:263) > > ==13666== by 0x5517688: EnvTraceProc (tclEnv.c:636) > > ==13666== by 0x55A0B69: TclCallVarTraces (tclTrace.c:2678) > > ==13666== by 0x55A0860: TclObjCallVarTraces (tclTrace.c:2564) > > ==13666== by 0x55AAFDB: TclPtrSetVarIdx (tclVar.c:2001) > > ==13666== by 0x55AA957: Tcl_ObjSetVar2 (tclVar.c:1770) > > ==13666== by 0x55AA609: Tcl_SetObjCmd (tclVar.c:1529) > > ==13666== by 0x544A42A: Dispatch (tclBasic.c:4456) > > ==13666== by 0x544A4B0: TclNRRunCallbacks (tclBasic.c:4492) > > ==13666== by 0x5449D83: Tcl_EvalObjv (tclBasic.c:4215) > > ==13666== by 0x544C1AB: TclEvalEx (tclBasic.c:5361) > > ==13666== by 0x544B571: Tcl_EvalEx (tclBasic.c:5026) > > ==13666== by 0x48525A8: XS_Tcl_Eval (Tcl.xs:1097) > > ==13666== by 0x1F4360: Perl_pp_entersub (in /usr/bin/perl) > > ==13666== by 0x1EA685: Perl_runops_standard (in /usr/bin/perl) > > ==13666== by 0x166116: perl_run (in /usr/bin/perl) > > ==13666== by 0x13C401: main (in /usr/bin/perl) > > > > > > > > (Note that I built Tcl/Tk with threads, since that is how Debian/Ubuntu > > Tcl/Tk are built; not yet sure if that affects this issue. Using system > > Perl vs self-compiled Perl should not be a factor, since Travis CI uses > > Perlbrew whereas I've used the system Perl.) > > > > > > Christopher A. Chavez