Make 'mmap_realloc' on MS-Windows more reliable

* src/w32heap.c (mmap_alloc): If reserving memory succeeds, but
committing fails, return NULL.  Don't call GetLastError twice for
the same API error.
(mmap_realloc): Zero out MEMORY_BASIC_INFORMATION structures
before calling VirtualQuery, to avoid using garbled values if the
call fails.  If committing more pages from the same block fails,
fall back on mmap_alloc + CopyMemory.  Enhance debugging printouts
if the call to VirtualAlloc to commit more pages fails.
(Bug#22526)
This commit is contained in:
Eli Zaretskii 2016-02-14 19:46:29 +02:00
parent 856cd948d1
commit 8badf953da

View file

@ -652,15 +652,19 @@ mmap_alloc (void **var, size_t nbytes)
{ {
/* Now, commit pages for NBYTES. */ /* Now, commit pages for NBYTES. */
*var = VirtualAlloc (p, nbytes, MEM_COMMIT, PAGE_READWRITE); *var = VirtualAlloc (p, nbytes, MEM_COMMIT, PAGE_READWRITE);
if (*var == NULL)
p = *var;
} }
if (!p) if (!p)
{ {
if (GetLastError () == ERROR_NOT_ENOUGH_MEMORY) DWORD e = GetLastError ();
if (e == ERROR_NOT_ENOUGH_MEMORY)
errno = ENOMEM; errno = ENOMEM;
else else
{ {
DebPrint (("mmap_alloc: error %ld\n", GetLastError ())); DebPrint (("mmap_alloc: error %ld\n", e));
errno = EINVAL; errno = EINVAL;
} }
} }
@ -683,6 +687,7 @@ void *
mmap_realloc (void **var, size_t nbytes) mmap_realloc (void **var, size_t nbytes)
{ {
MEMORY_BASIC_INFORMATION memInfo, m2; MEMORY_BASIC_INFORMATION memInfo, m2;
void *old_ptr;
if (*var == NULL) if (*var == NULL)
return mmap_alloc (var, nbytes); return mmap_alloc (var, nbytes);
@ -694,12 +699,14 @@ mmap_realloc (void **var, size_t nbytes)
return mmap_alloc (var, nbytes); return mmap_alloc (var, nbytes);
} }
memset (&memInfo, 0, sizeof (memInfo));
if (VirtualQuery (*var, &memInfo, sizeof (memInfo)) == 0) if (VirtualQuery (*var, &memInfo, sizeof (memInfo)) == 0)
DebPrint (("mmap_realloc: VirtualQuery error = %ld\n", GetLastError ())); DebPrint (("mmap_realloc: VirtualQuery error = %ld\n", GetLastError ()));
/* We need to enlarge the block. */ /* We need to enlarge the block. */
if (memInfo.RegionSize < nbytes) if (memInfo.RegionSize < nbytes)
{ {
memset (&m2, 0, sizeof (m2));
if (VirtualQuery (*var + memInfo.RegionSize, &m2, sizeof(m2)) == 0) if (VirtualQuery (*var + memInfo.RegionSize, &m2, sizeof(m2)) == 0)
DebPrint (("mmap_realloc: VirtualQuery error = %ld\n", DebPrint (("mmap_realloc: VirtualQuery error = %ld\n",
GetLastError ())); GetLastError ()));
@ -715,31 +722,31 @@ mmap_realloc (void **var, size_t nbytes)
MEM_COMMIT, PAGE_READWRITE); MEM_COMMIT, PAGE_READWRITE);
if (!p /* && GetLastError() != ERROR_NOT_ENOUGH_MEMORY */) if (!p /* && GetLastError() != ERROR_NOT_ENOUGH_MEMORY */)
{ {
DebPrint (("realloc enlarge: VirtualAlloc error %ld\n", DebPrint (("realloc enlarge: VirtualAlloc (%p + %I64x, %I64x) error %ld\n",
*var, (uint64_t)memInfo.RegionSize,
(uint64_t)(nbytes - memInfo.RegionSize),
GetLastError ())); GetLastError ()));
errno = ENOMEM; DebPrint (("next region: %p %p %I64x %x\n", m2.BaseAddress,
m2.AllocationBase, m2.RegionSize, m2.AllocationProtect));
} }
else
return *var;
}
/* Else we must actually enlarge the block by allocating a new
one and copying previous contents from the old to the new one. */
old_ptr = *var;
if (mmap_alloc (var, nbytes))
{
CopyMemory (*var, old_ptr, memInfo.RegionSize);
mmap_free (&old_ptr);
return *var; return *var;
} }
else else
{ {
/* Else we must actually enlarge the block by allocating a /* We failed to reallocate the buffer. */
new one and copying previous contents from the old to the *var = old_ptr;
new one. */ return NULL;
void *old_ptr = *var;
if (mmap_alloc (var, nbytes))
{
CopyMemory (*var, old_ptr, memInfo.RegionSize);
mmap_free (&old_ptr);
return *var;
}
else
{
/* We failed to enlarge the buffer. */
*var = old_ptr;
return NULL;
}
} }
} }
@ -751,7 +758,7 @@ mmap_realloc (void **var, size_t nbytes)
{ {
/* Let's give some memory back to the system and release /* Let's give some memory back to the system and release
some pages. */ some pages. */
void *old_ptr = *var; old_ptr = *var;
if (mmap_alloc (var, nbytes)) if (mmap_alloc (var, nbytes))
{ {