http://bugs.winehq.org/show_bug.cgi?id=21624
--- Comment #9 from Anastasius Focht focht@gmx.net 2010-02-07 15:16:26 --- Hello,
thanks for the logs. My instinct for interesting bugs did not let me down ;-)
The problem is most likely buried within iphlpapi.GetNetworkParams() and use of dns resolver API.
Query the buffer size:
--- snip --- 002d:Call iphlpapi.GetNetworkParams(00000000,0457df5c) ret=03994f7e 002d:Ret iphlpapi.GetNetworkParams() retval=0000006f ret=03994f7e ... 002d:trace:heap:RtlAllocateHeap (0x110000,70000062,00000270): returning 0x400a138 ... --- snip ---
Fill the network/dns info into provided buffer:
--- snip --- 002d:Call iphlpapi.GetNetworkParams(0400a138,0457df5c) ret=03994f7e ... 002d:Call advapi32.RegQueryValueExA(000002e4,7e058bb9 "ScopeID",00000000,00000000,0400a270,0457dde4) ret=7e053c8b 002d:Ret advapi32.RegQueryValueExA() retval=00000002 ret=7e053c8b ... 002d:Ret iphlpapi.GetNetworkParams() retval=00000000 ret=03994f7e --- snip ---
Relevant structures to calculate sizes (needed later):
--- snip include/iptypes.h --- typedef struct { char String[4 * 4]; } IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING;
typedef struct _IP_ADDR_STRING { struct _IP_ADDR_STRING* Next; IP_ADDRESS_STRING IpAddress; IP_MASK_STRING IpMask; DWORD Context; } IP_ADDR_STRING, *PIP_ADDR_STRING;
...
typedef struct { char HostName[MAX_HOSTNAME_LEN + 4] ; char DomainName[MAX_DOMAIN_NAME_LEN + 4]; PIP_ADDR_STRING CurrentDnsServer; IP_ADDR_STRING DnsServerList; UINT NodeType; char ScopeId[MAX_SCOPE_ID_LEN + 4]; UINT EnableRouting; UINT EnableProxy; UINT EnableDns; } FIXED_INFO, *PFIXED_INFO; --- snip include/iptypes.h ---
Sizes:
128+4 HostName 128+4 DomainName 4 CurrentDnsServer 40 DnsServerList 4 NodeType 256+4 ScopeId 4 EnableRouting 4 EnableProxy 4 EnableDns
Sum: 584 bytes -> 0x248 hex bytes
0x270 bytes from previous iphlpapi.GetNetworkParams() calls:
sizeof(FIXED_INFO) + sizeof(IP_ADDR_STRING) = two dns server entries. Additional dns server entries (_res.nscount > 1) are appended to struct FIXED_INFO.
Some time later (on different thread 0x3d):
--- snip --- 003d:Call iphlpapi.GetNetworkParams(00000000,0b7ddc50) ret=03994f7e 003d:Ret iphlpapi.GetNetworkParams() retval=0000006f ret=03994f7e ... 003d:trace:heap:RtlAllocateHeap (0x110000,70000062,00000248): returning 0x4009aa0 ... 003d:Call iphlpapi.GetNetworkParams(04009aa0,0b7ddc50) ret=03994f7e ... 003d:Call KERNEL32.GetComputerNameExA(00000001,04009aa0,0b7ddadc) ret=7e053b75 ... 003d:Ret KERNEL32.GetComputerNameExA() retval=00000001 ret=7e053b75 003d:Call KERNEL32.GetComputerNameExA(00000002,04009b24,0b7ddadc) ret=7e053b9c ... 003d:Ret KERNEL32.GetComputerNameExA() retval=00000001 ret=7e053b9c 003d:Call advapi32.RegOpenKeyExA(80000002,7e058870 "SYSTEM\CurrentControlSet\Services\VxD\MSTCP",00000000,00020019,0b7ddad8) ret=7e053c43 003d:Ret advapi32.RegOpenKeyExA() retval=00000000 ret=7e053c43 003d:Call advapi32.RegQueryValueExA(000003b4,7e058bb9 "ScopeID",00000000,00000000,04009bd8,0b7ddad4) ret=7e053c8b 0009:err:heap:HEAP_ValidateInUseArena Heap 0x110000: block 0x4009aa0 tail overwritten at 0x4009ce8 (byte 0/8 == 0x00) 003d:Ret advapi32.RegQueryValueExA() retval=00000002 ret=7e053c8b --- snip ---
This is where wine's heap detector raises its hand first time because the FIXED_INFO buffer was written past the end.
If you look closely you notice this time the GetNetworkParams() query for buffer size returned only 0x248 bytes - which is just sizeof(FIXED_INFO) = 1 dns server entry?!
Looking at the corresponding code:
--- snip dlls/iphlpapi/ --- DWORD WINAPI GetNetworkParams(PFIXED_INFO pFixedInfo, PULONG pOutBufLen) { DWORD ret, size; LONG regReturn; HKEY hKey;
TRACE("pFixedInfo %p, pOutBufLen %p\n", pFixedInfo, pOutBufLen); if (!pOutBufLen) return ERROR_INVALID_PARAMETER;
initialise_resolver(); size = sizeof(FIXED_INFO) + (_res.nscount > 0 ? (_res.nscount - 1) * sizeof(IP_ADDR_STRING) : 0); if (!pFixedInfo || *pOutBufLen < size) { *pOutBufLen = size; return ERROR_BUFFER_OVERFLOW; }
memset(pFixedInfo, 0, size); size = sizeof(pFixedInfo->HostName); GetComputerNameExA(ComputerNameDnsHostname, pFixedInfo->HostName, &size); size = sizeof(pFixedInfo->DomainName); GetComputerNameExA(ComputerNameDnsDomain, pFixedInfo->DomainName, &size); if (_res.nscount > 0) { PIP_ADDR_STRING ptr; int i;
for (i = 0, ptr = &pFixedInfo->DnsServerList; i < _res.nscount && ptr; i++, ptr = ptr->Next) { toIPAddressString(_res.nsaddr_list[i].sin_addr.s_addr, ptr->IpAddress.String); if (i == _res.nscount - 1) ptr->Next = NULL; else if (i == 0) ptr->Next = (PIP_ADDR_STRING)((LPBYTE)pFixedInfo + sizeof(FIXED_INFO)); else ptr->Next = (PIP_ADDR_STRING)((PBYTE)ptr + sizeof(IP_ADDR_STRING)); } } pFixedInfo->NodeType = HYBRID_NODETYPE; regReturn = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\CurrentControlSet\Services\VxD\MSTCP", 0, KEY_READ, &hKey); if (regReturn != ERROR_SUCCESS) regReturn = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\CurrentControlSet\Services\NetBT\Parameters", 0, KEY_READ, &hKey); if (regReturn == ERROR_SUCCESS) { DWORD size = sizeof(pFixedInfo->ScopeId);
RegQueryValueExA(hKey, "ScopeID", NULL, NULL, (LPBYTE)pFixedInfo->ScopeId, &size); RegCloseKey(hKey); } ... --- snip dlls/iphlpapi/ ---
The only code that is able to write past the FIXED_INFO buffer is the dns server entries list population loop. If for some reason "_res.nscount" changes between overall buffer size calculation and dns server entries list population it could be possible to corrupt nearby block.
Now the question is: who can change "_res.nscount"? The API that might be the culprit is GetComputerNameExA( ComputerNameDnsHostname, ...) which is called in between "_res.nscount" queries.
dlls/kernel32/computername.c: dns_hostname() -> dns_fqdn() -> gethostname() and dns_gethostbyname()/gethostbyname_r()
So this code path seems potentially harmful to GetNetworkParams() as it might change "_res" dns resolver struct members implicitly.
Again this is just my theory how the memory corruption might be caused. I could be wrong as I can't reproduce the problem on my machine. For testing you could place a consistency check, comparing "_res.nscount" before and after GetComputerNameExA().
Regards