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

older MS CRTs/libc putenv'es/Time::Piece cause memory corruption #14782

Closed
p5pRT opened this issue Jul 2, 2015 · 20 comments
Closed

older MS CRTs/libc putenv'es/Time::Piece cause memory corruption #14782

p5pRT opened this issue Jul 2, 2015 · 20 comments

Comments

@p5pRT
Copy link

p5pRT commented Jul 2, 2015

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

Searchable as RT125529$

@p5pRT
Copy link
Author

p5pRT commented Jul 2, 2015

From @bulk88

Created by @bulk88

TLDR​: Time​::Piece causes random memory corruption due to an interaction
between CRT and Perl about env vars, first reported in
https://groups.google.com/forum/#!topic/microsoft.public.vc.language/q44IweCm6Rg
"Crash in C runtime library on Windows XP" in 2003

Win32 Perl minimally uses MS CRT's _putenv function. It is only used in
ansify_path()
http​://perl5.git.perl.org/perl.git/blob/HEAD​:/win32/win32.c#l4391 .
Instead perl uses win32_putenv,
http​://perl5.git.perl.org/perl.git/blob/HEAD​:/win32/win32.c#l1834 which
manipulated the env directly to the Win32 API, not the libc API in the
CRT, and therefore perl never touches the CRT/libc. There is a big
warning from the 1990s not to use the CRT env funcs.

Perl sets ENV vars like this.
----------------------------------------------------------------------
  kernel32.dll!_SetEnvironmentVariableA@​8() + 0x8
  perl523.dll!win32_putenv(const char * name=0x00c226e8) Line 1862 +
0x18 C
  perl523.dll!Perl_my_setenv(const char * nam=0x00b66ab0, const char *
val=0x0091ce48) Line 2169 + 0x9 C
  perl523.dll!Perl_magic_setenv(sv * sv=0x009f90d8, magic *
mg=0x00c46c90) Line 1201 + 0xd C
  perl523.dll!Perl_mg_set(sv * sv=0x009f90d8) Line 277 + 0xe C
  perl523.dll!Perl_pp_sassign() Line 231 + 0x41 C
  perl523.dll!Perl_runops_debug() Line 2234 + 0x9 C
  perl523.dll!S_run_body(long oldscope=1) Line 2450 C
  perl523.dll!perl_run(interpreter * my_perl=0x00367d58) Line 2374 C
  perl523.dll!RunPerl(int argc=2, char * * argv=0x00364d80, char * *
env=0x00362c88) Line 258 + 0x9 C
  perl.exe!main(int argc=2, char * * argv=0x00364d80, char * *
env=0x00363308) Line 39 + 0x12 C
  perl.exe!mainCRTStartup() Line 398 + 0x11 C
  kernel32.dll!_BaseProcessStart@​4() + 0x23
----------------------------------------------------------------------

Time​::Piece directly calls _putenv to update the CRT's copy of the env
http​://perl5.git.perl.org/perl.git/blob/HEAD​:/cpan/Time-Piece/Piece.xs#l79
since supposedly (not verified) SetEnvironmentVariable doesn't update
the CRT's env vars.

----------------------------------------------------------------------
  kernel32.dll!_SetEnvironmentVariableA@​8() + 0x8
  msvcr71d.dll!__crtsetenv(char * * poption=0x0012fb04, const int
primary=1) Line 316 + 0x18 C
  msvcr71d.dll!_putenv_lk(const char * option=0x00c21c98) Line 181 + 0xb C
  msvcr71d.dll!_putenv(const char * option=0x00c21c98) Line 77 + 0x9 C
  Piece.dll!fix_win32_tzenv() Line 159 + 0xa C
  Piece.dll!my_tzset() Line 183 C
  Piece.dll!XS_Time__Piece__tzset(cv * cv=0x00c1d5b8) Line 1256 C
  perl523.dll!Perl_pp_entersub() Line 3270 + 0xc C
  perl523.dll!Perl_runops_debug() Line 2234 + 0x9 C
  perl523.dll!S_run_body(long oldscope=1) Line 2450 C
  perl523.dll!perl_run(interpreter * my_perl=0x00367d58) Line 2374 C
  perl523.dll!RunPerl(int argc=2, char * * argv=0x00364d80, char * *
env=0x00362c88) Line 258 + 0x9 C
  perl.exe!main(int argc=2, char * * argv=0x00364d80, char * *
env=0x00363308) Line 39 + 0x12 C
  perl.exe!mainCRTStartup() Line 398 + 0x11 C
  kernel32.dll!_BaseProcessStart@​4() + 0x23
----------------------------------------------------------------------

running cpan/Time-Piece/t/02core.t under the C debugger eventually
causes a heap corruption report

----------------------------------------------------------------------
  ntdll.dll!_DbgBreakPoint@​0()
  ntdll.dll!_RtlpBreakPointHeap@​4() + 0x28
  ntdll.dll!_RtlpValidateHeapEntry@​12() + 0x113
  ntdll.dll!_RtlValidateHeap@​12() + 0xe0
  kernel32.dll!_HeapValidate@​12() + 0x14
  msvcr71d.dll!_CrtIsValidHeapPointer(const void * pUserData=0x00c60250)
  Line 1818 C
  msvcr71d.dll!_free_dbg_lk(void * pUserData=0x00c60250, int
nBlockUse=2) Line 1143 + 0x9 C
  msvcr71d.dll!_free_dbg(void * pUserData=0x00c60250, int nBlockUse=2)
  Line 1081 + 0xd C
  msvcr71d.dll!_putenv_lk(const char * option=0x009f3b00) Line 186 + 0xb C
  msvcr71d.dll!_putenv(const char * option=0x009f3b00) Line 77 + 0x9 C
  Piece.dll!fix_win32_tzenv() Line 159 + 0xa C
  Piece.dll!my_tzset() Line 183 C
  Piece.dll!XS_Time__Piece__tzset(cv * cv=0x00c1d5b8) Line 1256 C
  perl523.dll!Perl_pp_entersub() Line 3270 + 0xc C
  perl523.dll!Perl_runops_debug() Line 2234 + 0x9 C
  perl523.dll!S_run_body(long oldscope=1) Line 2450 C
  perl523.dll!perl_run(interpreter * my_perl=0x00367d58) Line 2374 C
  perl523.dll!RunPerl(int argc=2, char * * argv=0x00364d80, char * *
env=0x00362c88) Line 258 + 0x9 C
  perl.exe!main(int argc=2, char * * argv=0x00364d80, char * *
env=0x00363308) Line 39 + 0x12 C
  perl.exe!mainCRTStartup() Line 398 + 0x11 C
  kernel32.dll!_BaseProcessStart@​4() + 0x23
----------------------------------------------------------------------

and if execution continues, an assert fail (DEBUGGING) or crash (no
DEBUGGING)

----------------------------------------------------------------------
C​:\perl521\srcnewb4opt>perl.exe cpan/Time-Piece/t/02core.t
1..102
ok 1
ok 2
ok 3
ok 4
ok 5
ok 6
ok 7
ok 8
ok 9
ok 10
ok 11
ok 12
ok 13
ok 14
ok 15
ok 16
ok 17
ok 18
ok 19
ok 20
ok 21
ok 22
ok 23
ok 24
ok 25
ok 26
ok 27
ok 28
ok 29
ok 30
ok 31
ok 32
ok 33
ok 34
ok 35
ok 36
ok 37
ok 38
ok 39
ok 40
ok 41
ok 42 # skip can't strftime %D, %R, %T or %e on Win32
ok 43 # skip can't strftime %D, %R, %T or %e on Win32
ok 44
ok 45
ok 46
ok 47
ok 48 # skip can't strftime %R on Win32 or QNX
ok 49
ok 50 # skip can't strftime %T on Win32
ok 51
ok 52 # skip can't strftime %V on Win32 or QNX or VOS
ok 53
ok 54
ok 55
ok 56
ok 57
ok 58
ok 59
ok 60
ok 61
ok 62
ok 63
ok 64
ok 65
ok 66
ok 67
ok 68
ok 69
ok 70
ok 71
ok 72
ok 73
ok 74
ok 75
ok 76
ok 77
ok 78
ok 79
ok 80
ok 81
ok 82
ok 83
ok 84 - Year is 1945?
ok 85 - Hour is 13?
ok 86
ok 87
ok 88
ok 89
ok 90
ok 91
ok 92
ok 93
ok 94
ok 95
ok 96
ok 97 - day of the year parsing
ok 98
Assertion failed​: SvTYPE(av) == SVt_PVAV, file ..\av.c, line 244

This application has requested the Runtime to terminate it in an unusual
way.
Please contact the application's support team for more information.

C​:\perl521\srcnewb4opt>
----------------------------------------------------------------------

The assert fail comes from.

----------------------------------------------------------------------
  perl523.dll!Perl_av_fetch(av * av=0x00b742c0, int key=393371, long
lval=1) Line 244 + 0x20 C
  perl523.dll!Perl_pad_alloc(long optype=9, unsigned long tmptype=0)
Line 728 + 0x1d C
  perl523.dll!S_pad_alloc_name(padname * name=0x00b742e8, unsigned long
flags=0, hv * typestash=0x00000000, hv * ourstash=0x00000000) Line
549 + 0x9 C
  perl523.dll!S_pad_findlex(const char * namepv=0x00903000, unsigned
int namelen=4, unsigned long flags=0, const cv * cv=0x00b724f0, unsigned
long seq=3524, int Perl_warn=1, sv * * out_capture=0x00000000, padname *
* out_name=0x0012eba0, int * out_flags=0x0012eb9c) Line 1343 + 0x2f C
  perl523.dll!Perl_pad_findmy_pvn(const char * namepv=0x00903000,
unsigned int namelen=4, unsigned long flags=0) Line 971 + 0x2b C
  perl523.dll!S_pending_ident() Line 8278 + 0x16 C
  perl523.dll!Perl_yylex() Line 4347 + 0xb C
  perl523.dll!Perl_yyparse(int gramtype=258) Line 322 + 0x5 C
  perl523.dll!S_doeval(int gimme=2, cv * outside=0x009a6140, unsigned
long seq=657, hv * hh=0x00000000) Line 3495 + 0x32 C
  perl523.dll!Perl_pp_entereval() Line 4306 + 0x15 C
  perl523.dll!Perl_runops_debug() Line 2234 + 0x9 C
  perl523.dll!S_run_body(long oldscope=1) Line 2450 C
  perl523.dll!perl_run(interpreter * my_perl=0x00365db8) Line 2374 C
  perl523.dll!RunPerl(int argc=2, char * * argv=0x00362c40, char * *
env=0x00365148) Line 258 + 0x9 C
  perl.exe!main(int argc=2, char * * argv=0x00362c40, char * *
env=0x00363120) Line 39 + 0x12 C
  perl.exe!mainCRTStartup() Line 398 + 0xe C
  kernel32.dll!_BaseProcessStart@​4() + 0x23
----------------------------------------------------------------------

The assert fail specifically occurs because malloc/win32 heap returns
the same pointer twice in Perl_pad_new due to heap corruption from
putenv. The same memblock is returned the first time, and creates
padnamelist->xpadnl_alloc, then shortly later when malloc is called
again, it returns the same pointer as in padnamelist->xpadnl_alloc which
becomes padlist->xpadl_alloc, so padlist->xpadl_alloc[0]->xpadnl_alloc
== padlist->xpadl_alloc, which is very wrong. Functions that write
PADNAME *s to the PADNAME ** array, trash the AV *s, if any, and a
PADNAME * is not a AV *. The assert fails for that reason.

The bug in the CRT is, in _putenv_lk, there is
----------------------------------------------------------------------
  /* Set requested environment type, primary call */
  if ( __crtsetenv(&newoption, 1) != 0 )
  {
  /* if the set failed, we will free the option only if it
was not consumed */
  if(newoption)
  {
  _free_crt(newoption);
  newoption=NULL;
  }
  return -1;
  }
----------------------------------------------------------------------
in
----------------------------------------------------------------------
#ifdef WPRFLAG
int __cdecl __crtwsetenv (
#else /* WPRFLAG */
int __cdecl __crtsetenv (
#endif /* WPRFLAG */
  _TSCHAR **poption,
  const int primary
  )
{
..........CUT............................
  int remove; /* 1 if variable is to be removed */
  _TSCHAR *option=NULL; /* The option string passed in */
..........CUT............................
  /* Validate poption and dereference it */
  if(poption==NULL)
  {
  /* probable CRT bug; shouldn't happen. Not a public function */
  _ASSERTE(FALSE);
  return -1;
  }
  option=*poption;
..........CUT............................
  if (SetEnvironmentVariable(name, remove ? NULL : value) == 0)
  retval = -1;
#endif /* WPRFLAG */
  _free_crt(name);
  }

  if (remove) {
  /* free option string since it won't be used anymore */
  _free_crt(option);
  }

  return retval;
}
----------------------------------------------------------------------

If SetEnvironmentVariable call fails, __crtsetenv frees var option, and
returns an error, but forgets to NULL out the caller's char **, so
putenv frees the string again.

Time​::Piece's testing makes SetEnviromentVariable fail with

ERROR_ENVVAR_NOT_FOUND
  203 (0xCB)
  The system could not find the environment option that was entered.

That triggers the double free bug.

A different block/branch in __crtsetenv shows NULL being assigned to the
caller's char **, which is correct, but was not done on the "remove"
branch at the end.

--------------------------------------------------------------
  else {
  /*
  * We are asked to remove an environment var that isn't
there.
  * Free the option string and return success.
  */
  _free_crt(option);

  /* we now freed the pointer, so NULL out the incoming
pointer */
  *poption=NULL;

  return 0;
  }
--------------------------------------------------------------

I've written a test case of this double free bug which I attached. You
must run it in a C debugger for most of the exceptions/crashes to
happen. WINXP sp3 msvcrt, 70, 71, one 80 but not a differnt 80 CRT fail.
None of the 90 and up CRTs fail.

Some failing callstacks from the test case.
--------------------------------------------------------------
  ntdll.dll!_DbgBreakPoint@​0()
  ntdll.dll!_RtlpBreakPointHeap@​4() + 0x28
  ntdll.dll!_RtlpValidateHeapEntry@​12() + 0x113
  ntdll.dll!_RtlDebugFreeHeap@​12() + 0x97
  ntdll.dll!_RtlFreeHeapSlowly@​12() + 0x246cf
  ntdll.dll!_RtlFreeHeap@​12() + 0x17646
  msvcrt.dll!_free() + 0xc3
  msvcrt.dll!__putenv_lk() + 0x9f
  msvcrt.dll!__putenv() + 0x20
  putenvsegv.exe!trycrt(const char * libname=0x00407e60) Line 20 + 0x7 C++
  putenvsegv.exe!main(int argc=1, char * * argv=0x003318d8) Line 80
+ 0xc C++
  putenvsegv.exe!mainCRTStartup() Line 259 + 0x12 C
  putenvsegv.exe!mainCRTStartup() Line 150 + 0xc C
--------------------------------------------------------------
  ntdll.dll!_DbgBreakPoint@​0()
  ntdll.dll!_RtlpBreakPointHeap@​4() + 0x28
  ntdll.dll!_RtlpValidateHeapEntry@​12() + 0x113
  ntdll.dll!_RtlDebugFreeHeap@​12() + 0x97
  ntdll.dll!_RtlFreeHeapSlowly@​12() + 0x246cf
  ntdll.dll!_RtlFreeHeap@​12() + 0x17646
  msvcr70.dll!free(void * pBlock=0x00374f58) Line 103 C
  msvcr70.dll!_putenv_lk(const char * option=0x00407ea8) Line 173 + 0x6 C
  msvcr70.dll!_putenv(const char * option=0x00407ea8) Line 77 + 0x8 C
  putenvsegv.exe!trycrt(const char * libname=0x00407e54) Line 20 + 0x7 C++
  putenvsegv.exe!main(int argc=1, char * * argv=0x003318d8) Line 80
+ 0xc C++
  putenvsegv.exe!mainCRTStartup() Line 259 + 0x12 C
  putenvsegv.exe!mainCRTStartup() Line 150 + 0xc C
--------------------------------------------------------------
  ntdll.dll!_DbgBreakPoint@​0()
  ntdll.dll!_RtlpBreakPointHeap@​4() + 0x28
  ntdll.dll!_RtlpValidateHeapEntry@​12() + 0x113
  ntdll.dll!_RtlDebugFreeHeap@​12() + 0x97
  ntdll.dll!_RtlFreeHeapSlowly@​12() + 0x246cf
  ntdll.dll!_RtlFreeHeap@​12() + 0x17646
  msvcr71.dll!free(void * pBlock=0x00384f60) Line 103 C
  msvcr71.dll!_putenv_lk(const char * option=0x00407ea8) Line 186 + 0x8 C
  msvcr71.dll!_putenv(const char * option=0x00407ea8) Line 77 + 0x8 C
  putenvsegv.exe!trycrt(const char * libname=0x00407e48) Line 20 + 0x7 C++
  putenvsegv.exe!main(int argc=1, char * * argv=0x003318d8) Line 80
+ 0xc C++
  putenvsegv.exe!mainCRTStartup() Line 259 + 0x12 C
  putenvsegv.exe!mainCRTStartup() Line 150 + 0xc C
--------------------------------------------------------------
  ntdll.dll!_DbgBreakPoint@​0()
  ntdll.dll!_RtlpBreakPointHeap@​4() + 0x28
  ntdll.dll!_RtlpValidateHeapEntry@​12() + 0x113
  ntdll.dll!_RtlValidateHeap@​12() + 0xe0
  kernel32.dll!_HeapValidate@​12() + 0x14
  msvcr71d.dll!_CrtIsValidHeapPointer(const void *
pUserData=0x00393840) Line 1818 C
  msvcr71d.dll!_free_dbg_lk(void * pUserData=0x00393840, int
nBlockUse=2) Line 1143 + 0x9 C
  msvcr71d.dll!_free_dbg(void * pUserData=0x00393840, int nBlockUse=2)
  Line 1081 + 0xd C
  msvcr71d.dll!_putenv_lk(const char * option=0x00407ea8) Line 186 + 0xb C
  msvcr71d.dll!_putenv(const char * option=0x00407ea8) Line 77 + 0x9 C
  putenvsegv.exe!trycrt(const char * libname=0x00407e38) Line 20 + 0x7 C++
  putenvsegv.exe!main(int argc=1, char * * argv=0x003318d8) Line 80
+ 0xc C++
  putenvsegv.exe!mainCRTStartup() Line 259 + 0x12 C
  putenvsegv.exe!mainCRTStartup() Line 150 + 0xc C
--------------------------------------------------------------
****A popup box with a GUI assert*********
--------------------------------------------------------------
  ntdll.dll!_KiFastSystemCallRet@​0()
  user32.dll!_NtUserWaitMessage@​0() + 0xc
  user32.dll!_InternalDialogBox@​24() + 0xb6
  user32.dll!_SoftModalMessageBox@​4() + 0x677
  user32.dll!_MessageBoxWorker@​4() + 0x175
  user32.dll!_MessageBoxTimeoutW@​24() + 0x7a
  user32.dll!_MessageBoxTimeoutA@​24() + 0x9c
  user32.dll!_MessageBoxExA@​20() + 0x1b
  user32.dll!_MessageBoxA@​16() + 0x45
  msvcr71d.dll!__crtMessageBoxA(const char * lpText=0x0012bd4c, const
char * lpCaption=0x10268444, unsigned int uType=73746) Line 119 C
  msvcr71d.dll!CrtMessageWindow(int nRptType=2, const char *
szFile=0x10267ab0, const char * szLine=0x0012cd90, const char *
szModule=0x00000000, const char * szUserMessage=0x0012cdb0) Line 617
+ 0x16 C
  msvcr71d.dll!_CrtDbgReport(int nRptType=2, const char *
szFile=0x10267ab0, int nLine=1143, const char * szModule=0x00000000,
const char * szFormat=0x10267b90, ...) Line 516 + 0x4c C
  msvcr71d.dll!_free_dbg_lk(void * pUserData=0x00393840, int
nBlockUse=2) Line 1143 + 0x28 C
  msvcr71d.dll!_free_dbg(void * pUserData=0x00393840, int nBlockUse=2)
  Line 1081 + 0xd C
  msvcr71d.dll!_putenv_lk(const char * option=0x00407ea8) Line 186 + 0xb C
  msvcr71d.dll!_putenv(const char * option=0x00407ea8) Line 77 + 0x9 C
  putenvsegv.exe!trycrt(const char * libname=0x00407e38) Line 20 + 0x7 C++
  putenvsegv.exe!main(int argc=1, char * * argv=0x003318d8) Line 80
+ 0xc C++
  putenvsegv.exe!mainCRTStartup() Line 259 + 0x12 C
  putenvsegv.exe!mainCRTStartup() Line 150 + 0xc C
--------------------------------------------------------------
First-chance exception at 0x1020a23b (msvcr71d.dll) in putenvsegv.exe​:
0xC0000005​: Access violation reading location 0x102bc0dc.
--------------------------------------------------------------
  msvcr71d.dll!_free_dbg_lk(void * pUserData=0x00393840, int
nBlockUse=2) Line 1159 + 0x19 C
  msvcr71d.dll!_free_dbg(void * pUserData=0x00393840, int nBlockUse=2)
  Line 1081 + 0xd C
  msvcr71d.dll!_putenv_lk(const char * option=0x00407ea8) Line 186 + 0xb C
  msvcr71d.dll!_putenv(const char * option=0x00407ea8) Line 77 + 0x9 C
  putenvsegv.exe!trycrt(const char * libname=0x00407e38) Line 20 + 0x7 C++
  putenvsegv.exe!main(int argc=1, char * * argv=0x003318d8) Line 80
+ 0xc C++
  putenvsegv.exe!mainCRTStartup() Line 259 + 0x12 C
  putenvsegv.exe!mainCRTStartup() Line 150 + 0xc C
--------------------------------------------------------------
  ntdll.dll!_DbgBreakPoint@​0()
  ntdll.dll!_RtlpBreakPointHeap@​4() + 0x28
  ntdll.dll!_RtlpValidateHeapEntry@​12() + 0x113
  ntdll.dll!_RtlValidateHeap@​12() + 0xe0
  kernel32.dll!_HeapValidate@​12() + 0x14
  msvcr80d.dll!_CrtIsValidHeapPointer(const void *
pUserData=0x003e3a48) Line 1963 C++
  msvcr80d.dll!_free_dbg_nolock(void * pUserData=0x003e3a48, int
nBlockUse=2) Line 1252 + 0x9 C++
  msvcr80d.dll!_free_dbg(void * pUserData=0x003e3a48, int nBlockUse=2)
  Line 1194 + 0xd C++
  msvcr80d.dll!_putenv_helper(const char * name=0x00407ea8, const char
* value=0x00000000) Line 273 + 0xb C
  msvcr80d.dll!_putenv(const char * option=0x00407ea8) Line 78 + 0xb C
  putenvsegv.exe!trycrt(const char * libname=0x00407e1c) Line 20 + 0x7 C++
  putenvsegv.exe!main(int argc=1, char * * argv=0x003318d8) Line 80
+ 0xc C++
  putenvsegv.exe!mainCRTStartup() Line 259 + 0x12 C
  putenvsegv.exe!mainCRTStartup() Line 150 + 0xc C
--------------------------------------------------------------
  ntdll.dll!_KiFastSystemCallRet@​0()
  user32.dll!_NtUserWaitMessage@​0() + 0xc
  user32.dll!_InternalDialogBox@​24() + 0xb6
  user32.dll!_SoftModalMessageBox@​4() + 0x677
  user32.dll!_MessageBoxWorker@​4() + 0x175
  user32.dll!_MessageBoxTimeoutW@​24() + 0x7a
  user32.dll!_MessageBoxExW@​20() + 0x1b
  user32.dll!_MessageBoxW@​16() + 0x45
  msvcr80d.dll!__crtMessageBoxW(const unsigned short *
lpText=0x00122c9c, const unsigned short * lpCaption=0x00abe780, unsigned
int uType=73746) Line 145 C
  msvcr80d.dll!__crtMessageWindowW(int nRptType=2, const wchar_t *
szFile=0x00abfef4, const wchar_t * szLine=0x00127d1c, const wchar_t *
szModule=0x00000000, const wchar_t * szUserMessage=0x00125d1c) Line
420 + 0x16 C++
  msvcr80d.dll!_VCrtDbgReportW(int nRptType=2, const wchar_t *
szFile=0x00abfef4, int nLine=1252, const wchar_t * szModule=0x00000000,
const wchar_t * szFormat=0x00ac0138, char * arglist=0x0012fdbc) Line
664 + 0x28 C
  msvcr80d.dll!_CrtDbgReportWV(int nRptType=2, const wchar_t *
szFile=0x00abfef4, int nLine=1252, const wchar_t * szModule=0x00000000,
const wchar_t * szFormat=0x00ac0138, char * arglist=0x0012fdbc) Line
300 + 0x1d C++
  msvcr80d.dll!_CrtDbgReportW(int nRptType=2, const wchar_t *
szFile=0x00abfef4, int nLine=1252, const wchar_t * szModule=0x00000000,
const wchar_t * szFormat=0x00ac0138, ...) Line 317 + 0x1d C++
  msvcr80d.dll!_free_dbg_nolock(void * pUserData=0x003e3a48, int
nBlockUse=2) Line 1252 + 0x28 C++
  msvcr80d.dll!_free_dbg(void * pUserData=0x003e3a48, int nBlockUse=2)
  Line 1194 + 0xd C++
  msvcr80d.dll!_putenv_helper(const char * name=0x00407ea8, const char
* value=0x00000000) Line 273 + 0xb C
  msvcr80d.dll!_putenv(const char * option=0x00407ea8) Line 78 + 0xb C
  putenvsegv.exe!trycrt(const char * libname=0x00407e1c) Line 20 + 0x7 C++
  putenvsegv.exe!main(int argc=1, char * * argv=0x003318d8) Line 80
+ 0xc C++
  putenvsegv.exe!mainCRTStartup() Line 259 + 0x12 C
  putenvsegv.exe!mainCRTStartup() Line 150 + 0xc C

First-chance exception at 0x00a0086f (msvcr80d.dll) in putenvsegv.exe​:
0xC0000005​: Access violation reading location 0x00b40518.

  msvcr80d.dll!_free_dbg_nolock(void * pUserData=0x003e3a48, int
nBlockUse=2) Line 1269 + 0x19 C++
  msvcr80d.dll!_free_dbg(void * pUserData=0x003e3a48, int nBlockUse=2)
  Line 1194 + 0xd C++
  msvcr80d.dll!_putenv_helper(const char * name=0x00407ea8, const char
* value=0x00000000) Line 273 + 0xb C
  msvcr80d.dll!_putenv(const char * option=0x00407ea8) Line 78 + 0xb C
  putenvsegv.exe!trycrt(const char * libname=0x00407e1c) Line 20 + 0x7 C++
  putenvsegv.exe!main(int argc=1, char * * argv=0x003318d8) Line 80
+ 0xc C++
  putenvsegv.exe!mainCRTStartup() Line 259 + 0x12 C
  putenvsegv.exe!mainCRTStartup() Line 150 + 0xc C
--------------------------------------------------------------

How does the CRT get env var info from the OS? We know it supposedly
never syncronizes Win32 env to match the CRT env, but it will forward
manipulations of the CRT env to the Win32 env.

So where does it initially do the import from Win32 env to CRT env?

This is from studying msvcr71.dll

GetEnvironmentVariableA, only used to lookup __MSVCRT_HEAP_SELECT in
_heap_select.
GetEnvironmentVariableW, never used

GetEnvironmentStringsW/GetEnvironmentStrings, unexported _setenvp called
from _CRTDLL_INIT. The _environ and _wenviron data vars are exported
though. So in theory we can reimplement _setenvp and
__crtGetEnvironmentStringsA ourselves in perl52*.dll and swap out the
pointers in data vars _environ and _wenviron and resync the CRT env and
Win32 env that way. Chalanages include calling _lock( _ENV_LOCK ); to
obtain the MT env lock. Using _lock of CRTs is complicated, see
http​://www.nntp.perl.org/group/perl.perl5.porters/2012/09/msg191776.html

Another idea is, in Time​::Piece always registering empty string env var
"TZ" with SetEnvironmentVariableA before calling putenv("TZ=");, so
SetEnvironmentVariableA inside putenv does not fail with
ERROR_ENVVAR_NOT_FOUND when "TZ" was deleted with %ENV from perl.

Perl Info

Flags:
     category=library
     severity=high
     module=Time::Piece

Site configuration information for perl 5.23.0:

Configured by Owner at Wed Jul  1 06:19:57 2015.

Summary of my perl5 (revision 5 version 23 subversion 0) configuration:
   Derived from: 46096de0a5ea1d3ff02330693905024fd6de579a
   Platform:
     osname=MSWin32, osvers=5.1, archname=MSWin32-x86-perlio
     uname=''
     config_args='undef'
     hint=recommended, useposix=true, d_sigaction=undef
     useithreads=undef, usemultiplicity=undef
     use64bitint=undef, use64bitall=undef, uselongdouble=undef
     usemymalloc=n, bincompat5005=undef
   Compiler:
     cc='cl', ccflags ='-nologo -GF -W3 -Od -MDd -Zi -D_DEBUG 
-DDEBUGGING -DWIN32 -D_CONSOLE -DNO_STRICT  -DPERL_TEXTMODE_SCRIPTS 
-DDEBUG_LEAKING_SCALARS -DPERL_POISON -D_USE_32BIT_TIME_T',
     optimize='-Od -MDd -Zi -D_DEBUG -DDEBUGGING',
     cppflags='-DWIN32'
     ccversion='13.10.6030', gccversion='', gccosandvers=''
     intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234, 
doublekind=3
     d_longlong=undef, longlongsize=8, d_longdbl=define, longdblsize=8, 
longdblkind=0
     ivtype='long', ivsize=4, nvtype='double', nvsize=8, 
Off_t='__int64', lseeksize=8
     alignbytes=8, prototype=define
   Linker and Libraries:
     ld='link', ldflags ='-nologo -nodefaultlib -debug 	 
-libpath:"c:\perl521\lib\CORE" 		-machine:x86'
     libpth=\lib
     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 msvcrtd.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 msvcrtd.lib
     libc=msvcrtd.lib, so=dll, useshrplib=true, libperl=perl523.lib
     gnulibc_version=''
   Dynamic Linking:
     dlsrc=dl_win32.xs, dlext=dll, d_dlsymun=undef, ccdlflags=' '
     cccdlflags=' ', lddlflags='-dll -nologo -nodefaultlib -debug 	 
-libpath:"c:\perl521\lib\CORE" 		-machine:x86'

Locally applied patches:
     uncommitted-changes


@INC for perl 5.23.0:
     C:/perl521/srcnewb4opt/lib
     .


Environment for perl 5.23.0:
     HOME (unset)
     LANG (unset)
     LANGUAGE (unset)
     LD_LIBRARY_PATH (unset)
     LOGDIR (unset)
     PATH=C:\Program Files\Dr. 
Memory\bin;C:\sperl\c\bin;C:\WINDOWS\system32;C:\Program Files\Microsoft 
Visual Studio .NET 2003\Vc7\bin;C:\Program Files\Microsoft Visual Studio 
.NET 2003\Common7\IDE;C:\WINDOWS;C:\Program Files\Git\cmd;C:\Program 
Files\Microsoft Visual Studio .NET 2003\Common7\Tools\bin;C:\perl\bin
     PERL_BADLANG (unset)
     PERL_DESTRUCT_LEVEL=2
     SHELL (unset)

@p5pRT
Copy link
Author

p5pRT commented Jul 2, 2015

From @bulk88

<assembly xmlns="urn​:schemas-microsoft-com​:asm.v1" manifestVersion="1.0">
  <trustInfo xmlns="urn​:schemas-microsoft-com​:asm.v3">
  <security>
  <requestedPrivileges>
  <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
  </requestedPrivileges>
  </security>
  </trustInfo>
  <dependency>
  <dependentAssembly>
  <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
  </dependentAssembly>
  </dependency>
  <dependency>
  <dependentAssembly>
  <assemblyIdentity type="win32" name="Microsoft.VC80.CRT" version="8.0.50608.0" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
  </dependentAssembly>
  </dependency>
  <dependency>
  <dependentAssembly>
  <assemblyIdentity type="win32" name="Microsoft.VC80.DebugCRT" version="8.0.50608.0" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
  </dependentAssembly>
  </dependency>
  <dependency>
  <dependentAssembly>
  <assemblyIdentity type="win32" name="Microsoft.VC90.DebugCRT" version="9.0.30729.1" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
  </dependentAssembly>
  </dependency>
</assembly>

@p5pRT
Copy link
Author

p5pRT commented Jul 2, 2015

From @bulk88

// putenvsegv.cpp : Defines the entry point for the console application.
//

#define _WIN32_WINNT 0x0500
#include <tchar.h>

#include <stdio.h>
#include <windows.h>
#include <winternl.h>

void trycrt(const char * libname) {
  HMODULE lib = LoadLibrary(libname);
  int (*_putenvptr) ( const char *envstring ) = (int (*)(const char *))GetProcAddress(lib, "_putenv");
  if(_putenvptr) {
  printf("found %s\n", libname);
  __try {
  _putenvptr("TZ=EST5");
  SetEnvironmentVariable("TZ", NULL);
  _putenvptr("TZ=");
  }
  __except((printf("expection %u seen\n", GetExceptionCode())),(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)) {
  printf("SEGV exception in %s\n", libname);
  }
  }
  else
  printf("didnt find %s\n", libname);
}

// Using Microsoft's intrinsics instead of inline assembly
PPEB getPEB() {
  return (PPEB)__readfsdword(0x30);
}

int _tmain(int argc, _TCHAR* argv[])
{
  static const char * crts [] = {
  "msvcrt.dll",
  "msvcr70.dll",
  "msvcr71.dll",
  "msvcr71d.dll",
  "msvcr80.dll",
  "msvcr80d.dll",
  "msvcr90.dll",
  "msvcr90d.dll",
  "msvcr100.dll",
  "msvcr120.dll" };
  ACTCTX ActCtx;
  HANDLE hActCtx;
  ULONG_PTR ctxCookie;
  //
  //system("pause");
  //DebugBreak();
  //none of this helps, _putenv has a try block that is probably (I can't
  //confirm this with a C debugger, due to native of the C debugger) catching
  //0x80000003 STATUS_BREAKPOINT that that debuggibg heap throws, the only way to see
  //the corruption messages is with running the process in a C debugger
  printf("orignal being debugged %u\n", getPEB()->BeingDebugged);
  getPEB()->BeingDebugged = TRUE;
  printf("new being debugged %u\n", getPEB()->BeingDebugged);
  ActCtx.cbSize = sizeof(ACTCTX);
  ActCtx.dwFlags = 0;
  ActCtx.lpSource = "crtmanifest.manifest";
  ActCtx.wProcessorArchitecture = 0;
  ActCtx.wLangId = 0;
  ActCtx.lpAssemblyDirectory = 0;
  ActCtx.lpResourceName = NULL;
  ActCtx.lpApplicationName = 0;
  ActCtx.hModule = NULL;

  hActCtx = CreateActCtx(&ActCtx);
  if (hActCtx == INVALID_HANDLE_VALUE) {
  printf("\nCreateActCtx() Failed!, Code=%02d", GetLastError());
  }
  if (!ActivateActCtx(hActCtx, &ctxCookie)) {
  printf("\nActivateActCtx() Failed! Code=%02d", GetLastError());
  }

  for(int i = 0; i< (sizeof(crts)/sizeof(void*)); i++) {
  trycrt(crts[i]);
  }

  return 0;
}

@p5pRT
Copy link
Author

p5pRT commented Jul 2, 2015

From @bulk88

putenvsegv.sln

@p5pRT
Copy link
Author

p5pRT commented Jul 2, 2015

From @bulk88

putenvsegv.suo

@p5pRT
Copy link
Author

p5pRT commented Jul 2, 2015

From @bulk88

<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
  ProjectType="Visual C++"
  Version="7.10"
  Name="putenvsegv"
  ProjectGUID="{8483524E-D584-4B3E-839E-EB0E7F45876A}"
  Keyword="Win32Proj">
  <Platforms>
  <Platform
  Name="Win32"/>
  </Platforms>
  <Configurations>
  <Configuration
  Name="Debug|Win32"
  OutputDirectory="Debug"
  IntermediateDirectory="Debug"
  ConfigurationType="1"
  CharacterSet="2">
  <Tool
  Name="VCCLCompilerTool"
  Optimization="0"
  PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
  MinimalRebuild="TRUE"
  BasicRuntimeChecks="3"
  RuntimeLibrary="5"
  UsePrecompiledHeader="0"
  WarningLevel="3"
  Detect64BitPortabilityProblems="TRUE"
  DebugInformationFormat="4"/>
  <Tool
  Name="VCCustomBuildTool"/>
  <Tool
  Name="VCLinkerTool"
  OutputFile="$(OutDir)/putenvsegv.exe"
  LinkIncremental="2"
  GenerateDebugInformation="TRUE"
  ProgramDatabaseFile="$(OutDir)/putenvsegv.pdb"
  SubSystem="1"
  TargetMachine="1"/>
  <Tool
  Name="VCMIDLTool"/>
  <Tool
  Name="VCPostBuildEventTool"/>
  <Tool
  Name="VCPreBuildEventTool"/>
  <Tool
  Name="VCPreLinkEventTool"/>
  <Tool
  Name="VCResourceCompilerTool"/>
  <Tool
  Name="VCWebServiceProxyGeneratorTool"/>
  <Tool
  Name="VCXMLDataGeneratorTool"/>
  <Tool
  Name="VCWebDeploymentTool"/>
  <Tool
  Name="VCManagedWrapperGeneratorTool"/>
  <Tool
  Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
  </Configuration>
  <Configuration
  Name="Release|Win32"
  OutputDirectory="Release"
  IntermediateDirectory="Release"
  ConfigurationType="1"
  CharacterSet="2"
  WholeProgramOptimization="TRUE">
  <Tool
  Name="VCCLCompilerTool"
  PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
  RuntimeLibrary="4"
  UsePrecompiledHeader="0"
  WarningLevel="3"
  Detect64BitPortabilityProblems="TRUE"
  DebugInformationFormat="3"/>
  <Tool
  Name="VCCustomBuildTool"/>
  <Tool
  Name="VCLinkerTool"
  OutputFile="$(OutDir)/putenvsegv.exe"
  LinkIncremental="1"
  GenerateDebugInformation="TRUE"
  SubSystem="1"
  OptimizeReferences="2"
  EnableCOMDATFolding="2"
  TargetMachine="1"/>
  <Tool
  Name="VCMIDLTool"/>
  <Tool
  Name="VCPostBuildEventTool"/>
  <Tool
  Name="VCPreBuildEventTool"/>
  <Tool
  Name="VCPreLinkEventTool"/>
  <Tool
  Name="VCResourceCompilerTool"/>
  <Tool
  Name="VCWebServiceProxyGeneratorTool"/>
  <Tool
  Name="VCXMLDataGeneratorTool"/>
  <Tool
  Name="VCWebDeploymentTool"/>
  <Tool
  Name="VCManagedWrapperGeneratorTool"/>
  <Tool
  Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
  </Configuration>
  </Configurations>
  <References>
  </References>
  <Files>
  <Filter
  Name="Source Files"
  Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
  UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
  <File
  RelativePath=".\putenvsegv.cpp">
  </File>
  </Filter>
  <Filter
  Name="Header Files"
  Filter="h;hpp;hxx;hm;inl;inc;xsd"
  UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
  </Filter>
  <Filter
  Name="Resource Files"
  Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
  UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
  </Filter>
  <File
  RelativePath=".\ReadMe.txt">
  </File>
  </Files>
  <Globals>
  </Globals>
</VisualStudioProject>

@p5pRT
Copy link
Author

p5pRT commented Jul 2, 2015

From @dolmen

The right place to report this issue is probably on GitHub​:
https://github.com/rjbs/Time-Piece/

Le Jeu 02 Jui 2015 02​:17​:12, bulk88 a écrit :

This is a bug report for perl from bulk88@​hotmail.com,
generated with the help of perlbug 1.40 running under perl 5.23.0.

-----------------------------------------------------------------
[Please describe your issue here]

TLDR​: Time​::Piece causes random memory corruption due to an

@p5pRT
Copy link
Author

p5pRT commented Jul 2, 2015

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

@p5pRT
Copy link
Author

p5pRT commented Jul 2, 2015

From @bulk88

On Thu Jul 02 10​:22​:01 2015, dolmen wrote​:

The right place to report this issue is probably on GitHub​:
https://github.com/rjbs/Time-Piece/

Steve Hay/Tony C/JDB will never see the ticket if I file it there on T​::P's github. I don't see RJBS or Sam Smith (T​::P's maints) being able to comment on Win32 C issues. Time​::Piece's Win32 putenv code was developed on P5P ML in http​://www.nntp.perl.org/group/perl.perl5.porters/2009/03/msg145324.html and committed to P5P repo before published on CPAN ( http​://perl5.git.perl.org/perl.git/commitdiff/12016aadb5ccd03002d026d37636471225cf9aa5 and http​://perl5.git.perl.org/perl.git/commitdiff/6e0733998eff7a098d2d21d5602f3eb2a7521e1f Dual-Life/Time-Piece@5229c9b ). This bug also wouldn't happen if Perl core used putenv instead of SetEnvironmentVariable (but I am not advocating this change). This ticket asks if Perl core needs to a C function to resync the Win32 env that Perl uses with the libc CRT env. I think P5P is the most appropriate venue for the ticket.

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Jul 14, 2015

From @tonycoz

On Thu Jul 02 11​:33​:09 2015, bulk88 wrote​:

On Thu Jul 02 10​:22​:01 2015, dolmen wrote​:

The right place to report this issue is probably on GitHub​:
https://github.com/rjbs/Time-Piece/

Steve Hay/Tony C/JDB will never see the ticket if I file it there on
T​::P's github. I don't see RJBS or Sam Smith (T​::P's maints) being
able to comment on Win32 C issues. Time​::Piece's Win32 putenv code was
developed on P5P ML in
http​://www.nntp.perl.org/group/perl.perl5.porters/2009/03/msg145324.html
and committed to P5P repo before published on CPAN (
http​://perl5.git.perl.org/perl.git/commitdiff/12016aadb5ccd03002d026d37636471225cf9aa5
and
http​://perl5.git.perl.org/perl.git/commitdiff/6e0733998eff7a098d2d21d5602f3eb2a7521e1f
https://github.com/rjbs/Time-
Piece/commit/5229c9b71945060c265166e906a26da347025cbc ). This bug also
wouldn't happen if Perl core used putenv instead of
SetEnvironmentVariable (but I am not advocating this change). This
ticket asks if Perl core needs to a C function to resync the Win32 env
that Perl uses with the libc CRT env. I think P5P is the most
appropriate venue for the ticket.

Can you see a solution that isn't "use a newer compiler"?

Tony

@p5pRT
Copy link
Author

p5pRT commented Jul 14, 2015

From @bulk88

On Mon Jul 13 18​:32​:12 2015, tonyc wrote​:

Can you see a solution that isn't "use a newer compiler"?

Tony

1. P5P offers a "sync win32 env with crt env using _environ and _wenviron data vars" function to XS code. Time​::Piece uses that function on older VCs.

2. Time​::Piece applies the patch in Dual-Life/Time-Piece#16 . I've attached that patch to the Perl RT ticket (this ticket) for ease of viewing (on github its, eghhh, steganography stored).

3. Live patch machine code old CRTs at runtime. Similar in spirit to this



win32/win32.c | 23 +++++++++++++++++++++++
1 files changed, 23 insertions(+), 0 deletions(-)

Inline Patch
diff --git a/win32/win32.c b/win32/win32.c
index 3bb6e06..f2d01e5 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -4491,6 +4491,29 @@ Perl_win32_init(int *argcp, char ***argvp)
 #endif
 
     ansify_path();
+    {
+        char * f = (char*)GetProcAddress(GetModuleHandle("kernel32.dll"), "BaseQueryModuleData");
+        if(f) {
+            if(memcmp(f, "\x8B\xFF\x55\x8B\xEC\x51\xE8", sizeof("\x8B\xFF\x55\x8B\xEC\x51\xE8")-1) == 0){
+                f += sizeof("\x8B\xFF\x55\x8B\xEC\x51\xE8")-1;
+                f = f + 4 + *(U32*)f;
+                if(memcmp(f, "\x8B\xFF\x55\x8B\xEC\x83\xEC\x20\xA1\xCC\x56\x88\x7C\x57\x33\xFF\x89\x45\xFC\xA1",
+                       sizeof("\x8B\xFF\x55\x8B\xEC\x83\xEC\x20\xA1\xCC\x56\x88\x7C\x57\x33\xFF\x89\x45\xFC\xA1")-1)
+                == 0){
+                    U32 * flag;
+                    f+= sizeof("\x8B\xFF\x55\x8B\xEC\x83\xEC\x20\xA1\xCC\x56\x88\x7C\x57\x33\xFF\x89\x45\xFC\xA1")-1;
+                    flag = *(U32**)f;
+                    /* now we have address of the static var */
+                    *flag = 1;
+                    return;
+                }
+            }
+            DebugBreak(); /* failed, time to analyze */
+        }
+    }
 }
 
 void
-- 

I think #3 is a joke.

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Jul 14, 2015

From @bulk88

0001-fix-perl-125529-putenv-mem-corruption-on-older-ms-cr.patch
From 6b5432abccf86bb4fdb11c47b4eaebbb767eee22 Mon Sep 17 00:00:00 2001
From: Daniel Dragan <bulk88@hotmail.com>
Date: Sat, 4 Jul 2015 22:04:31 -0400
Subject: [PATCH] fix [perl #125529] putenv mem corruption on older MS
 CRTs/libs

---
 cpan/Time-Piece/Piece.pm |    2 +-
 cpan/Time-Piece/Piece.xs |   16 +++++++++++++++-
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/cpan/Time-Piece/Piece.pm b/cpan/Time-Piece/Piece.pm
index a8b80fc..6833529 100644
--- a/cpan/Time-Piece/Piece.pm
+++ b/cpan/Time-Piece/Piece.pm
@@ -17,7 +17,7 @@ our %EXPORT_TAGS = (
     ':override' => 'internal',
     );
 
-our $VERSION = '1.30';
+our $VERSION = '1.31';
 
 require DynaLoader;
 {
diff --git a/cpan/Time-Piece/Piece.xs b/cpan/Time-Piece/Piece.xs
index eafb790..221376f 100644
--- a/cpan/Time-Piece/Piece.xs
+++ b/cpan/Time-Piece/Piece.xs
@@ -153,8 +153,22 @@ fix_win32_tzenv(void)
     if (crt_tz_env == NULL)
         crt_tz_env = "";
     if (strcmp(perl_tz_env, crt_tz_env) != 0) {
-        newenv = (char*)malloc((strlen(perl_tz_env) + 4) * sizeof(char));
+        STRLEN perl_tz_env_len = strlen(perl_tz_env);
+        newenv = (char*)malloc((perl_tz_env_len + 4) * sizeof(char));
         if (newenv != NULL) {
+/* putenv with old MS CRTs will cause a double free internally if you delete
+   an env var with the CRT env that doesn't exist in Win32 env (perl %ENV only
+   modifies the Win32 env, not CRT env), so always create the env var in Win32
+   env before deleting it with CRT env api, so the error branch never executes
+   in __crtsetenv after SetEnvironmentVariableA executes inside __crtsetenv.
+
+   VC 9/2008 and up dont have this bug, older VC (msvcrt80.dll and older) and
+   mingw (msvcrt.dll) have it see [perl #125529]
+*/
+#if !(_MSC_VER >= 1500)
+            if(!perl_tz_env_len)
+                SetEnvironmentVariableA("TZ", "");
+#endif
             sprintf(newenv, "TZ=%s", perl_tz_env);
             putenv(newenv);
             if (oldenv != NULL)
-- 
1.7.9.msysgit.0

@p5pRT
Copy link
Author

p5pRT commented Jul 14, 2015

From @bulk88

On Thu Jul 02 02​:17​:12 2015, bulk88 wrote​:

This is a bug report for perl from bulk88@​hotmail.com,
generated with the help of perlbug 1.40 running under perl 5.23.0.

-----------------------------------------------------------------
[Please describe your issue here]

TLDR​: Time​::Piece causes random memory corruption due to an
interaction
between CRT and Perl about env vars, first reported in
https://groups.google.com/forum/#!topic/microsoft.public.vc.language/q44IweCm6Rg
"Crash in C runtime library on Windows XP" in 2003

For the record, PHP found this bug in 2005 https://bugs.php.net/bug.php?id=32957 , they decided to fix it by always creating the env var in Win32 env before calling CRT putenv.

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Aug 3, 2015

From @bulk88

On Thu Jul 02 02​:17​:12 2015, bulk88 wrote​:

How does the CRT get env var info from the OS? We know it supposedly
never syncronizes Win32 env to match the CRT env, but it will forward
manipulations of the CRT env to the Win32 env.

So where does it initially do the import from Win32 env to CRT env?

This is from studying msvcr71.dll

GetEnvironmentVariableA, only used to lookup __MSVCRT_HEAP_SELECT in
_heap_select.
GetEnvironmentVariableW, never used

GetEnvironmentStringsW/GetEnvironmentStrings, unexported _setenvp
called
from _CRTDLL_INIT. The _environ and _wenviron data vars are exported
though. So in theory we can reimplement _setenvp and
__crtGetEnvironmentStringsA ourselves in perl52*.dll and swap out the
pointers in data vars _environ and _wenviron and resync the CRT env
and
Win32 env that way. Chalanages include calling _lock( _ENV_LOCK ); to
obtain the MT env lock. Using _lock of CRTs is complicated, see
http​://www.nntp.perl.org/group/perl.perl5.porters/2012/09/msg191776.html

So should perl core have a win32_sync_crt_env function or not?

If not, then the Time​::Piece fix has to go into Time​::Piece and get shipped. cpan/Time-Piece/t/02core.t randomly SEGVing/panic errors/test failing on unthreaded perl is annoying me.

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Aug 16, 2015

From @bulk88

On Sun Aug 02 21​:47​:47 2015, bulk88 wrote​:

So should perl core have a win32_sync_crt_env function or not?

If not, then the Time​::Piece fix has to go into Time​::Piece and get
shipped. cpan/Time-Piece/t/02core.t randomly SEGVing/panic errors/test
failing on unthreaded perl is annoying me.

Bump.

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Aug 17, 2015

From @tonycoz

On Sun Aug 02 21​:47​:47 2015, bulk88 wrote​:

On Thu Jul 02 02​:17​:12 2015, bulk88 wrote​:

How does the CRT get env var info from the OS? We know it supposedly
never syncronizes Win32 env to match the CRT env, but it will forward
manipulations of the CRT env to the Win32 env.

So where does it initially do the import from Win32 env to CRT env?

This is from studying msvcr71.dll

GetEnvironmentVariableA, only used to lookup __MSVCRT_HEAP_SELECT in
_heap_select.
GetEnvironmentVariableW, never used

GetEnvironmentStringsW/GetEnvironmentStrings, unexported _setenvp
called
from _CRTDLL_INIT. The _environ and _wenviron data vars are exported
though. So in theory we can reimplement _setenvp and
__crtGetEnvironmentStringsA ourselves in perl52*.dll and swap out the
pointers in data vars _environ and _wenviron and resync the CRT env
and
Win32 env that way. Chalanages include calling _lock( _ENV_LOCK );
to
obtain the MT env lock. Using _lock of CRTs is complicated, see
http​://www.nntp.perl.org/group/perl.perl5.porters/2012/09/msg191776.html

So should perl core have a win32_sync_crt_env function or not?

If not, then the Time​::Piece fix has to go into Time​::Piece and get
shipped. cpan/Time-Piece/t/02core.t randomly SEGVing/panic errors/test
failing on unthreaded perl is annoying me.

I think the answer is #2.

I don't see a ticket for this at https://rt.cpan.org//Dist/Display.html?Queue=Time-Piece which is supposed to be the dist's bug tracker.

Tony

@p5pRT
Copy link
Author

p5pRT commented Aug 17, 2015

From @bulk88

On Sun Aug 16 18​:45​:22 2015, tonyc wrote​:

On Sun Aug 02 21​:47​:47 2015, bulk88 wrote​:

On Thu Jul 02 02​:17​:12 2015, bulk88 wrote​:

How does the CRT get env var info from the OS? We know it
supposedly
never syncronizes Win32 env to match the CRT env, but it will
forward
manipulations of the CRT env to the Win32 env.

So where does it initially do the import from Win32 env to CRT env?

This is from studying msvcr71.dll

GetEnvironmentVariableA, only used to lookup __MSVCRT_HEAP_SELECT
in
_heap_select.
GetEnvironmentVariableW, never used

GetEnvironmentStringsW/GetEnvironmentStrings, unexported _setenvp
called
from _CRTDLL_INIT. The _environ and _wenviron data vars are
exported
though. So in theory we can reimplement _setenvp and
__crtGetEnvironmentStringsA ourselves in perl52*.dll and swap out
the
pointers in data vars _environ and _wenviron and resync the CRT env
and
Win32 env that way. Chalanages include calling _lock( _ENV_LOCK );
to
obtain the MT env lock. Using _lock of CRTs is complicated, see
http​://www.nntp.perl.org/group/perl.perl5.porters/2012/09/msg191776.html

So should perl core have a win32_sync_crt_env function or not?

If not, then the Time​::Piece fix has to go into Time​::Piece and get
shipped. cpan/Time-Piece/t/02core.t randomly SEGVing/panic
errors/test
failing on unthreaded perl is annoying me.

I think the answer is #2.

I don't see a ticket for this at
https://rt.cpan.org//Dist/Display.html?Queue=Time-Piece which is
supposed to be the dist's bug tracker.

Tony

Dual-Life/Time-Piece#16  I didn't want to do a PR to prevent a hasty merge before the CRT sync question is answered, since Time-Piece probably isn't the only XS module in the world to do "#undef getenv" or NO_XSLOCKS and use the real libc putenv instead of the perl psuedofork one. Since it doesn't look anyone else will answer, I am slightly learning towards #2 since it is the quicker solution than trying to manipulate the env copy inside the CRT which is not that friendly to do (see also the VC 2015 threads/tickets where the undocumented _pioinfo data array was dropped from the CRT lib in the 2015 edition). You could also argue that only old CRTs need to have their ENVs synced, and those old CRTs are out of support for MS and won't ever change.

there are other open issues on github so I am not the first to use GH issue tracker by accident Dual-Life/Time-Piece#15 , maybe rjbs can disable the issue tracker if he chooses

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Aug 24, 2015

From p5p@net153.net

On 08/16/2015 10​:02 PM, bulk88 via RT wrote​:

On Sun Aug 16 18​:45​:22 2015, tonyc wrote​:

On Sun Aug 02 21​:47​:47 2015, bulk88 wrote​:

On Thu Jul 02 02​:17​:12 2015, bulk88 wrote​:

How does the CRT get env var info from the OS? We know it
supposedly
never syncronizes Win32 env to match the CRT env, but it will
forward
manipulations of the CRT env to the Win32 env.

So where does it initially do the import from Win32 env to CRT env?

This is from studying msvcr71.dll

GetEnvironmentVariableA, only used to lookup __MSVCRT_HEAP_SELECT
in
_heap_select.
GetEnvironmentVariableW, never used

GetEnvironmentStringsW/GetEnvironmentStrings, unexported _setenvp
called
from _CRTDLL_INIT. The _environ and _wenviron data vars are
exported
though. So in theory we can reimplement _setenvp and
__crtGetEnvironmentStringsA ourselves in perl52*.dll and swap out
the
pointers in data vars _environ and _wenviron and resync the CRT env
and
Win32 env that way. Chalanages include calling _lock( _ENV_LOCK );
to
obtain the MT env lock. Using _lock of CRTs is complicated, see
http​://www.nntp.perl.org/group/perl.perl5.porters/2012/09/msg191776.html

So should perl core have a win32_sync_crt_env function or not?

If not, then the Time​::Piece fix has to go into Time​::Piece and get
shipped. cpan/Time-Piece/t/02core.t randomly SEGVing/panic
errors/test
failing on unthreaded perl is annoying me.

I think the answer is #2.

I don't see a ticket for this at
https://rt.cpan.org//Dist/Display.html?Queue=Time-Piece which is
supposed to be the dist's bug tracker.

Tony

Dual-Life/Time-Piece#16  I didn't want to do a PR to prevent a hasty merge before the CRT sync question is answered, since Time-Piece probably isn't the only XS module in the world to do "#undef getenv" or NO_XSLOCKS and use the real libc putenv instead of the perl psuedofork one. Since it doesn't look anyone else will answer, I am slightly learning towards #2 since it is the quicker solution than trying to manipulate the env copy inside the CRT which is not that friendly to do (see also the VC 2015 threads/tickets where the undocumented _pioinfo data array was dropped from the CRT lib in the 2015 edition). You could also argue that only old CRTs need to have their ENVs synced, and those old CRTs are out of support for MS and won't ever change.

there are other open issues on github so I am not the first to use GH issue tracker by accident Dual-Life/Time-Piece#15 , maybe rjbs can disable the issue tracker if he chooses

Added patch and pushed out as version 1.30_01. Did not test on windows
machine yet. Figured cpantesters would get it, but cpantesters is down
again :|

--Sam

@p5pRT
Copy link
Author

p5pRT commented Sep 10, 2015

From @bulk88

On Thu Jul 02 02​:17​:12 2015, bulk88 wrote​:

This is a bug report for perl from bulk88@​hotmail.com,
generated with the help of perlbug 1.40 running under perl 5.23.0.

-----------------------------------------------------------------
[Please describe your issue here]

TLDR​: Time​::Piece causes random memory corruption due to an
interaction
between CRT and Perl about env vars, first reported in
https://groups.google.com/forum/#!topic/microsoft.public.vc.language/q44IweCm6Rg
"Crash in C runtime library on Windows XP" in 2003

I found the same code as in Time​::Piece in POSIX​::, http​://perl5.git.perl.org/perl.git/blob/2efb8b4b644d5f3c28974a8f577081b4142decd2​:/ext/POSIX/POSIX.xs#l1738 so POSIX will need fixing whatever way Time​::Piece will be fixed.

--
bulk88 ~ bulk88 at hotmail.com

@toddr
Copy link
Member

toddr commented Feb 13, 2020

Given Time::Piece fixed this in 1.32 and 1.33 is in blead, I'm closing this case.

@toddr toddr closed this as completed Feb 13, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants