Front page | perl.perl5.porters |
Postings from March 2014
[perl #121471] uninit mem read in win32_fseek() randomly creates multi GB files on Win64
From:
bulk88
Date:
March 19, 2014 12:59
Subject:
[perl #121471] uninit mem read in win32_fseek() randomly creates multi GB files on Win64
Message ID:
rt-4.0.18-18195-1395233977-846.121471-75-0@perl.org
# New Ticket Created by bulk88
# Please include the string: [perl #121471]
# in the subject line of all future correspondence about this issue.
# <URL: https://rt.perl.org/Ticket/Display.html?id=121471 >
This is a bug report for perl from bulk88@hotmail.com,
generated with the help of perlbug 1.40 running under perl 5.19.10.
-----------------------------------------------------------------
[Please describe your issue here]
Using "Microsoft (R) C/C++ Optimizing Compiler Version 15.00.30729.01
for x64" Visual C 2008 x64, atleast with -O1 non DEBUGGING, there is a
uninit memory read in win32_fseek in win32.c due to what I think is a
compiler bug since I can't find anything wrong with the C code. Win64
passes the first 4 args in registers, with shadow space on C stack
mirroring the registers. If a function chooses so, (win32_fseek MUST do
this), it can copy the incoming register to the C stack mirroring slot.
win32_fseek does a 4 byte asm mov instead of a 8 byte mov. It laster
does "&" on C stack, and passes that to fsetpos. The upper 4 bytes are
uninitialized C stack now. Those bytes by chance now contain 0x00000001.
Hench ~ 4 GB of data being written.
Specifically a file is extended to 4 GB file is randomly (over the last
couple years by me, I never investigated it, but it is repeatable if
your commit happens to being a failing one that causes the upper 4 bytes
to not be zero for some reason, the garbage/uninit mem on C stack
happens to be 0x0000000140074379 from some other previous pp_* opcode
since pp_sysseek never wrote to that piece of C stack memory, which
leaves the 33rd bit being high). The command is "..\miniperl -I..\lib
bin\exetype.pl ..\wperl.exe WINDOWS". On win32 Perl type Off_t is a
__int64. At
http://perl5.git.perl.org/perl.git/blob/f0fe019a81bffdc6f23c6c00d5fa100df21f8428:/win32/bin/exetype.pl#l48
------------------------------------
seek EXE, $offset+4+20+68, 0;
------------------------------------
Happens. The sum of $offset+4+20+68 should be, and on PP level is ~
0x150. The C callstack for that line is
-------------------------------------
kernel32.dll!SetFilePointer()
msvcr90.dll!_lseeki64_nolock(int fh=4, __int64 pos=4294967636, int
mthd=0) Line 146 + 0x15 bytes C
msvcr90.dll!_lseeki64(int fh=4, __int64 pos=2020275856, int mthd=0) Line
85 + 0xd bytes C
msvcr90.dll!_fseeki64_nolock(_iobuf * str=0x00000000786af690, __int64
offset=28048264, int whence=340) Line 142 + 0x15 bytes C
msvcr90.dll!_fseeki64(_iobuf * stream=0x00000000786af690, __int64
offset=2020275856, int whence=340) Line 65 + 0xe bytes C
msvcr90.dll!fsetpos(_iobuf * stream=0x0000000000000154, const __int64 *
pos=0x000000014009c1bc) Line 46 C
miniperl.exe!win32_fseek(_iobuf * pf=0x0000000001a7d590, long
offset=340, int origin=28049368) Line 2843 C
miniperl.exe!Perl_pp_sysseek() Line 2169 + 0x3e bytes C
miniperl.exe!Perl_runops_standard() Line 42 + 0x3 bytes C
miniperl.exe!S_run_body(long oldscope=0) Line 2451 C
miniperl.exe!perl_run(interpreter * my_perl=0x0000000001a73c60) Line
2365 + 0x9 bytes C
miniperl.exe!main(int argc=0, char * * argv=0x0001686340c4f679, char * *
env=0x0000000000000000) Line 122 C
miniperl.exe!__tmainCRTStartup() Line 582 + 0x19 bytes C
kernel32.dll!BaseProcessStart() + 0x2c bytes
----------------------------------------------
Note the first pos is 340 bytes (~0x150) above 2^32. This really is the
value passed to the kerenel. The disk thrashing doesn't start until a
print or close is done, for example
---------------------------------------------------------
ntdll.dll!NtWriteFile() + 0xa bytes
kernel32.dll!WriteFile() + 0xab bytes
msvcr90.dll!_write_nolock(int fh=4, const void * buf=0x0000000000000160,
unsigned int cnt=0) Line 472 + 0x1a bytes C
msvcr90.dll!_write(int fh=4, const void * buf=0x00000000786af690,
unsigned int cnt=0) Line 75 + 0xd bytes C
msvcr90.dll!_flush(_iobuf * str=0x00000000786af690) Line 154 + 0x13 bytes C
msvcr90.dll!_fclose_nolock(_iobuf * str=0x00000000786af690) Line 106 C
msvcr90.dll!fclose(_iobuf * stream=0x00000000786af690) Line 57 + 0x8 bytes C
miniperl.exe!Perl_io_close(io * io=0x0000000001b34a10, char
not_implicit='') Line 984 + 0xc bytes C
miniperl.exe!Perl_do_close(gv * gv=0x0000000001afccc8, char
not_implicit='˜') Line 946 C
miniperl.exe!Perl_pp_close() Line 670 + 0xe bytes C
miniperl.exe!Perl_runops_standard() Line 42 + 0x3 bytes C
miniperl.exe!S_run_body(long oldscope=0) Line 2451 C
miniperl.exe!perl_run(interpreter * my_perl=0x0000000001af2d10) Line
2365 + 0x9 bytes C
miniperl.exe!main(int argc=0, char * * argv=0x0001624349b91237, char * *
env=0x0000000000000000) Line 122 C
miniperl.exe!__tmainCRTStartup() Line 582 + 0x19 bytes C
kernel32.dll!BaseProcessStart() + 0x2c bytes
---------------------------------------------------------
Asm dump of the broken win32_fseek from the binary
---------------------------------------------------------
2816:
2817: DllExport int
2818: win32_fseek(FILE *pf, Off_t offset,int origin)
2819: {
00000001400CF300 mov dword ptr [rsp+10h],edx ;;;;;;;;;;;;PROBLEM RIGHT
HERE, 4 byte copy, not 8 byte copy
00000001400CF304 push rbx
00000001400CF305 sub rsp,20h
00000001400CF309 mov rbx,rcx
2820: #if defined(WIN64) || defined(USE_LARGE_FILES)
2821: fpos_t pos;
2822: switch (origin) {
00000001400CF30C test r8d,r8d
00000001400CF30F je win32_fseek+6Bh (1400CF36Bh)
00000001400CF311 sub r8d,1
00000001400CF315 je win32_fseek+4Fh (1400CF34Fh)
00000001400CF317 cmp r8d,1
00000001400CF31B je win32_fseek+2Bh (1400CF32Bh)
2820: #if defined(WIN64) || defined(USE_LARGE_FILES)
2821: fpos_t pos;
2822: switch (origin) {
2823: case SEEK_CUR:
2824: if (fgetpos(pf, &pos))
2825: return -1;
2826: offset += pos;
2827: break;
2828: case SEEK_END:
2829: fseek(pf, 0, SEEK_END);
2830: pos = _telli64(fileno(pf));
2831: offset += pos;
2832: break;
2833: case SEEK_SET:
2834: break;
2835: default:
2836: errno = EINVAL;
00000001400CF31D call qword ptr [__imp__errno (1400D2470h)]
00000001400CF323 mov dword ptr [rax],16h
2837: return -1;
00000001400CF329 jmp win32_fseek+5Eh (1400CF35Eh)
2813: return ftell(pf);
2814: #endif
2815: }
2816:
2817: DllExport int
2818: win32_fseek(FILE *pf, Off_t offset,int origin)
2819: {
2820: #if defined(WIN64) || defined(USE_LARGE_FILES)
2821: fpos_t pos;
2822: switch (origin) {
2823: case SEEK_CUR:
2824: if (fgetpos(pf, &pos))
2825: return -1;
2826: offset += pos;
2827: break;
2828: case SEEK_END:
2829: fseek(pf, 0, SEEK_END);
00000001400CF32B xor edx,edx
00000001400CF32D lea r8d,[rdx+2]
00000001400CF331 call qword ptr [__imp_fseek (1400D2518h)]
2830: pos = _telli64(fileno(pf));
00000001400CF337 mov rcx,rbx
00000001400CF33A call qword ptr [__imp_fileno (1400D2568h)]
00000001400CF340 mov ecx,eax
00000001400CF342 call qword ptr [__imp__telli64 (1400D24C8h)]
00000001400CF348 mov qword ptr [pos],rax
2831: offset += pos;
2832: break;
00000001400CF34D jmp win32_fseek+67h (1400CF367h)
2823: case SEEK_CUR:
2824: if (fgetpos(pf, &pos))
00000001400CF34F lea rdx,[pos] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;now do
& on it, upper 4 are uninit
00000001400CF354 call qword ptr [__imp_fgetpos (1400D2450h)]
00000001400CF35A test eax,eax
00000001400CF35C je win32_fseek+63h (1400CF363h)
2825: return -1;
00000001400CF35E or eax,0FFFFFFFFh
00000001400CF361 jmp win32_fseek+79h (1400CF379h)
2826: offset += pos;
00000001400CF363 mov eax,dword ptr [pos]
00000001400CF367 add dword ptr [offset],eax
2838: }
2839: return fsetpos(pf, &offset);
00000001400CF36B lea rdx,[offset]
00000001400CF370 mov rcx,rbx
00000001400CF373 call qword ptr [__imp_fsetpos (1400D2488h)]
2840: #else
2841: return fseek(pf, (long)offset, origin);
2842: #endif
2843: }
00000001400CF379 add rsp,20h
00000001400CF37D pop rbx
00000001400CF37E ret
---------------------------------------------------------
Sending win32_fseek through preprocessor shows everything is correct.
Below is original win32_fseek
---------------------------------------------------------
DllExport int
win32_fseek(FILE *pf, Off_t offset,int origin)
{
#if defined(WIN64) || defined(USE_LARGE_FILES)
fpos_t pos;
switch (origin) {
case SEEK_CUR:
if (fgetpos(pf, &pos))
return -1;
offset += pos;
break;
case SEEK_END:
fseek(pf, 0, SEEK_END);
pos = _telli64(fileno(pf));
offset += pos;
break;
case SEEK_SET:
break;
default:
errno = EINVAL;
return -1;
}
return fsetpos(pf, &offset);
#else
return fseek(pf, (long)offset, origin);
#endif
}
---------------------------------------------------------
after preprocessor, as win32 miniperl
---------------------------------------------------------
int
win32_fseek(FILE * pf, __int64 offset, int origin)
{
fpos_t pos;
switch (origin) {
case 1:
if (fgetpos(pf, &pos))
return -1;
offset += pos;
break;
case 2:
fseek(pf, 0, 2);
pos = _telli64(fileno(pf));
offset += pos;
break;
case 0:
break;
default:
(*_errno()) = 22;
return -1;
}
return fsetpos(pf, &offset);
#line 2843 "win32.c"
}
---------------------------------------------------------
In the attached asm output from Visual C (NOT the asm from the final
executable binary), also shows NO PROBLEM and the correct kind of mov op
(rdx). I have no idea where the edx reg came from instead of rdx reg.
The perlbug data below is irrelevant to this broken perl. The C code is
correct, the compiler is wrong, right?
[Please do not change anything below this line]
-----------------------------------------------------------------
---
Flags:
category=core
severity=low
---
Site configuration information for perl 5.19.10:
Configured by Owner at Sat Mar 15 22:30:42 2014.
Summary of my perl5 (revision 5 version 19 subversion 10) configuration:
Derived from: 2179658b5e799a6e3c4e736ec7c84b0f50bf3473
Ancestor: e9251c1a8f4944e6dceff5240d9e109ba075ff29
Platform:
osname=MSWin32, osvers=5.1, archname=MSWin32-x86-multi-thread
uname=''
config_args='undef'
hint=recommended, useposix=true, d_sigaction=undef
useithreads=define, usemultiplicity=define
use64bitint=undef, use64bitall=undef, uselongdouble=undef
usemymalloc=n, bincompat5005=undef
Compiler:
cc='cl', ccflags ='-nologo -GF -W3 -O1 -MD -Zi -DNDEBUG -G7 -GL -DWIN32
-D_CONSOLE -DNO_STRICT -DPERL_TEXTMODE_SCRIPTS
-DPERL_HASH_FUNC_ONE_AT_A_TIME -DPERL_IMPLICIT_CONTEXT
-DPERL_IMPLICIT_SYS -DUSE_PERLIO -D_USE_32BIT_TIME_T',
optimize='-O1 -MD -Zi -DNDEBUG -G7 -GL',
cppflags='-DWIN32'
ccversion='13.10.6030', gccversion='', gccosandvers=''
intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
d_longlong=undef, longlongsize=8, d_longdbl=define, longdblsize=8
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 -opt:ref,icf -ltcg
-libpath:"c:\perl519\lib\CORE" -machine:x86'
libpth="C:\Program Files\Microsoft Visual Studio .NET 2003\VC7\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 msvcrt.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
libc=msvcrt.lib, so=dll, useshrplib=true, libperl=perl519.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:\perl519\lib\CORE" -machine:x86'
Locally applied patches:
uncommitted-changes
2179658b5e799a6e3c4e736ec7c84b0f50bf3473
---
@INC for perl 5.19.10:
C:/perl519/site/lib
C:/perl519/lib
.
---
Environment for perl 5.19.10:
HOME (unset)
LANG (unset)
LANGUAGE (unset)
LD_LIBRARY_PATH (unset)
LOGDIR (unset)
PATH=C:\perl519\bin;C:\Program Files\Microsoft Visual Studio .NET
2003\Common7\IDE;C:\Program Files\Microsoft Visual Studio .NET
2003\VC7\BIN;C:\Program Files\Microsoft Visual Studio .NET
2003\Common7\Tools;C:\Program Files\Microsoft Visual Studio .NET
2003\Common7\Tools\bin\prerelease;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\system32\wbem;
PERL_BADLANG (unset)
SHELL (unset)
-
[perl #121471] uninit mem read in win32_fseek() randomly creates multi GB files on Win64
by bulk88