MmapPool: Fix expand block moving.

D dereferences the pointer wrong because of missing difference between .
and -> operators, if trying to write a block over another
block. So use memmove first to move the memory and then update the fields
that should be changed (only size).
This commit is contained in:
Eugen Wissner 2017-01-14 20:39:33 +01:00
parent fe884541fc
commit c567b88d5d
2 changed files with 122 additions and 68 deletions

View File

@ -243,7 +243,7 @@ final class MmapPool : Allocator
return true; return true;
} }
immutable dataSize = addAlignment(size); immutable dataSize = addAlignment(size);
immutable delta = dataSize - p.length; immutable delta = dataSize - addAlignment(p.length);
if (block1.next is null if (block1.next is null
|| !block1.next.free || !block1.next.free
@ -255,21 +255,17 @@ final class MmapPool : Allocator
} }
if (block1.next.size >= delta + alignment_) if (block1.next.size >= delta + alignment_)
{ {
// We should move the start position of the next block. The order may be // Move size from block2 to block1.
// important because the old block and the new one can overlap. block1.next.size = block1.next.size - delta;
auto block2 = cast(Block) (p.ptr + dataSize);
block2.size = block1.next.size - delta;
block2.free = true;
block2.region = block1.region;
block2.next = block1.next.next;
block2.prev = block1;
block1.size = block1.size + delta; block1.size = block1.size + delta;
auto block2 = cast(Block) (p.ptr + dataSize);
if (block1.next.next !is null) if (block1.next.next !is null)
{ {
block1.next.next.prev = block2; block1.next.next.prev = block2;
} }
// block1.next and block2 can overlap.
memmove(cast(void*) block2, cast(void*) block1.next, BlockEntry.sizeof);
block1.next = block2; block1.next = block2;
} }
else else
@ -574,3 +570,61 @@ final class MmapPool : Allocator
} }
private alias Block = shared BlockEntry*; private alias Block = shared BlockEntry*;
} }
// A lot of allocations/deallocations, but it is the minimum caused a
// segmentation fault because MmapPool expand moves a block wrong.
unittest
{
auto a = MmapPool.instance.allocate(16);
auto d = MmapPool.instance.allocate(16);
auto b = MmapPool.instance.allocate(16);
auto e = MmapPool.instance.allocate(16);
auto c = MmapPool.instance.allocate(16);
auto f = MmapPool.instance.allocate(16);
MmapPool.instance.deallocate(a);
MmapPool.instance.deallocate(b);
MmapPool.instance.deallocate(c);
a = MmapPool.instance.allocate(50);
MmapPool.instance.expand(a, 64);
MmapPool.instance.deallocate(a);
a = MmapPool.instance.allocate(1);
auto tmp1 = MmapPool.instance.allocate(1);
auto h1 = MmapPool.instance.allocate(1);
auto tmp2 = cast(ubyte[]) MmapPool.instance.allocate(1);
auto h2 = MmapPool.instance.allocate(2);
tmp1 = MmapPool.instance.allocate(1);
MmapPool.instance.deallocate(h2);
MmapPool.instance.deallocate(h1);
h2 = MmapPool.instance.allocate(2);
h1 = MmapPool.instance.allocate(1);
MmapPool.instance.deallocate(h2);
auto rep = cast(void[]) tmp2;
MmapPool.instance.reallocate(rep, tmp1.length);
tmp2 = cast(ubyte[]) rep;
MmapPool.instance.reallocate(tmp1, 9);
rep = cast(void[]) tmp2;
MmapPool.instance.reallocate(rep, tmp1.length);
tmp2 = cast(ubyte[]) rep;
MmapPool.instance.reallocate(tmp1, 17);
tmp2[$ - 1] = 0;
MmapPool.instance.deallocate(tmp1);
b = MmapPool.instance.allocate(16);
MmapPool.instance.deallocate(h1);
MmapPool.instance.deallocate(a);
MmapPool.instance.deallocate(b);
MmapPool.instance.deallocate(d);
MmapPool.instance.deallocate(e);
MmapPool.instance.deallocate(f);
}

View File

@ -174,13 +174,13 @@ else version (Windows)
alias WSASocket = WSASocketW; alias WSASocket = WSASocketW;
alias LPFN_GETACCEPTEXSOCKADDRS = VOID function(PVOID, alias LPFN_GETACCEPTEXSOCKADDRS = VOID function(PVOID,
DWORD, DWORD,
DWORD, DWORD,
DWORD, DWORD,
SOCKADDR**, SOCKADDR**,
LPINT, LPINT,
SOCKADDR**, SOCKADDR**,
LPINT); LPINT);
const GUID WSAID_GETACCEPTEXSOCKADDRS = {0xb5367df2,0xcbac,0x11cf,[0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92]}; const GUID WSAID_GETACCEPTEXSOCKADDRS = {0xb5367df2,0xcbac,0x11cf,[0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92]};
struct WSABUF { struct WSABUF {
@ -248,23 +248,23 @@ else version (Windows)
* Throws: $(D_PSYMBOL SocketException) if unable to receive. * Throws: $(D_PSYMBOL SocketException) if unable to receive.
*/ */
bool beginReceive(ubyte[] buffer, bool beginReceive(ubyte[] buffer,
SocketState overlapped, SocketState overlapped,
Flags flags = Flags(Flag.none)) @nogc @trusted Flags flags = Flags(Flag.none)) @nogc @trusted
{ {
auto receiveFlags = cast(DWORD) flags; auto receiveFlags = cast(DWORD) flags;
overlapped.handle = cast(HANDLE) handle_; overlapped.handle = cast(HANDLE) handle_;
overlapped.event = OverlappedSocketEvent.read; overlapped.event = OverlappedSocketEvent.read;
overlapped.buffer.len = buffer.length; overlapped.buffer.len = cast(ULONG) buffer.length;
overlapped.buffer.buf = cast(char*) buffer.ptr; overlapped.buffer.buf = cast(char*) buffer.ptr;
auto result = WSARecv(handle_, auto result = WSARecv(handle_,
&overlapped.buffer, &overlapped.buffer,
1u, 1u,
NULL, NULL,
&receiveFlags, &receiveFlags,
&overlapped.overlapped, &overlapped.overlapped,
NULL); NULL);
if (result == SOCKET_ERROR && !wouldHaveBlocked) if (result == SOCKET_ERROR && !wouldHaveBlocked)
{ {
@ -292,9 +292,9 @@ else version (Windows)
{ {
DWORD lpNumber; DWORD lpNumber;
BOOL result = GetOverlappedResult(overlapped.handle, BOOL result = GetOverlappedResult(overlapped.handle,
&overlapped.overlapped, &overlapped.overlapped,
&lpNumber, &lpNumber,
FALSE); FALSE);
if (result == FALSE && !wouldHaveBlocked) if (result == FALSE && !wouldHaveBlocked)
{ {
disconnected_ = true; disconnected_ = true;
@ -321,21 +321,21 @@ else version (Windows)
* Throws: $(D_PSYMBOL SocketException) if unable to send. * Throws: $(D_PSYMBOL SocketException) if unable to send.
*/ */
bool beginSend(ubyte[] buffer, bool beginSend(ubyte[] buffer,
SocketState overlapped, SocketState overlapped,
Flags flags = Flags(Flag.none)) @nogc @trusted Flags flags = Flags(Flag.none)) @nogc @trusted
{ {
overlapped.handle = cast(HANDLE) handle_; overlapped.handle = cast(HANDLE) handle_;
overlapped.event = OverlappedSocketEvent.write; overlapped.event = OverlappedSocketEvent.write;
overlapped.buffer.len = buffer.length; overlapped.buffer.len = cast(ULONG) buffer.length;
overlapped.buffer.buf = cast(char*) buffer.ptr; overlapped.buffer.buf = cast(char*) buffer.ptr;
auto result = WSASend(handle_, auto result = WSASend(handle_,
&overlapped.buffer, &overlapped.buffer,
1u, 1u,
NULL, NULL,
cast(DWORD) flags, cast(DWORD) flags,
&overlapped.overlapped, &overlapped.overlapped,
NULL); NULL);
if (result == SOCKET_ERROR && !wouldHaveBlocked) if (result == SOCKET_ERROR && !wouldHaveBlocked)
{ {
@ -364,9 +364,9 @@ else version (Windows)
{ {
DWORD lpNumber; DWORD lpNumber;
BOOL result = GetOverlappedResult(overlapped.handle, BOOL result = GetOverlappedResult(overlapped.handle,
&overlapped.overlapped, &overlapped.overlapped,
&lpNumber, &lpNumber,
FALSE); FALSE);
if (result == FALSE && !wouldHaveBlocked) if (result == FALSE && !wouldHaveBlocked)
{ {
disconnected_ = true; disconnected_ = true;
@ -385,7 +385,7 @@ else version (Windows)
* Create a socket. * Create a socket.
* *
* Params: * Params:
* af = Address family. * af = Address family.
* *
* Throws: $(D_PSYMBOL SocketException) on errors. * Throws: $(D_PSYMBOL SocketException) on errors.
*/ */
@ -402,14 +402,14 @@ else version (Windows)
DWORD dwBytes; DWORD dwBytes;
auto result = WSAIoctl(handle_, auto result = WSAIoctl(handle_,
SIO_GET_EXTENSION_FUNCTION_POINTER, SIO_GET_EXTENSION_FUNCTION_POINTER,
&guidAcceptEx, &guidAcceptEx,
guidAcceptEx.sizeof, guidAcceptEx.sizeof,
&acceptExtension, &acceptExtension,
acceptExtension.sizeof, acceptExtension.sizeof,
&dwBytes, &dwBytes,
NULL, NULL,
NULL); NULL);
if (!result == SOCKET_ERROR) if (!result == SOCKET_ERROR)
{ {
throw make!SocketException(defaultAllocator, throw make!SocketException(defaultAllocator,
@ -449,13 +449,13 @@ else version (Windows)
// We don't want to get any data now, but only start to accept the connections // We don't want to get any data now, but only start to accept the connections
BOOL result = acceptExtension(handle_, BOOL result = acceptExtension(handle_,
socket, socket,
overlapped.buffer.buf, overlapped.buffer.buf,
0u, 0u,
sockaddr_in.sizeof + 16, sockaddr_in.sizeof + 16,
sockaddr_in.sizeof + 16, sockaddr_in.sizeof + 16,
&dwBytes, &dwBytes,
&overlapped.overlapped); &overlapped.overlapped);
if (result == FALSE && !wouldHaveBlocked) if (result == FALSE && !wouldHaveBlocked)
{ {
throw defaultAllocator.make!SocketException("Unable to accept socket connection"); throw defaultAllocator.make!SocketException("Unable to accept socket connection");
@ -478,7 +478,7 @@ else version (Windows)
{ {
scope (exit) scope (exit)
{ {
defaultAllocator.dispose(overlapped.buffer.buf[0..overlapped.buffer.len]); defaultAllocator.dispose(overlapped.buffer.buf[0 .. overlapped.buffer.len]);
} }
auto socket = make!OverlappedConnectedSocket(defaultAllocator, auto socket = make!OverlappedConnectedSocket(defaultAllocator,
cast(socket_t) overlapped.handle, cast(socket_t) overlapped.handle,
@ -539,9 +539,9 @@ shared static this()
auto ws2Lib = GetModuleHandle("ws2_32.dll"); auto ws2Lib = GetModuleHandle("ws2_32.dll");
getaddrinfoPointer = cast(typeof(getaddrinfoPointer)) getaddrinfoPointer = cast(typeof(getaddrinfoPointer))
GetProcAddress(ws2Lib, "getaddrinfo"); GetProcAddress(ws2Lib, "getaddrinfo");
freeaddrinfoPointer = cast(typeof(freeaddrinfoPointer)) freeaddrinfoPointer = cast(typeof(freeaddrinfoPointer))
GetProcAddress(ws2Lib, "freeaddrinfo"); GetProcAddress(ws2Lib, "freeaddrinfo");
} }
else version (Posix) else version (Posix)
{ {
@ -754,10 +754,10 @@ abstract class Socket
{ {
auto length = cast(socklen_t) result.length; auto length = cast(socklen_t) result.length;
if (getsockopt(handle_, if (getsockopt(handle_,
cast(int) level, cast(int) level,
cast(int) option, cast(int) option,
result.ptr, result.ptr,
&length) == SOCKET_ERROR) &length) == SOCKET_ERROR)
{ {
throw defaultAllocator.make!SocketException("Unable to get socket option"); throw defaultAllocator.make!SocketException("Unable to get socket option");
} }
@ -821,10 +821,10 @@ abstract class Socket
void[] value) const @trusted @nogc void[] value) const @trusted @nogc
{ {
if (setsockopt(handle_, if (setsockopt(handle_,
cast(int)level, cast(int)level,
cast(int)option, cast(int)option,
value.ptr, value.ptr,
cast(uint) value.length) == SOCKET_ERROR) cast(uint) value.length) == SOCKET_ERROR)
{ {
throw defaultAllocator.make!SocketException("Unable to set socket option"); throw defaultAllocator.make!SocketException("Unable to set socket option");
} }