Force 8-byte alignment on HTTP_REQUEST buffer

Addresses #126
This commit is contained in:
moozzyk 2016-06-15 10:46:24 -07:00
parent 10df99de67
commit f63e53b597
6 changed files with 77 additions and 45 deletions

View File

@ -1049,12 +1049,13 @@ namespace Microsoft.Net.Http.Server
// Server API // Server API
internal static void GetUnknownHeaders(IDictionary<string, StringValues> unknownHeaders, byte[] memoryBlob, IntPtr originalAddress) internal static void GetUnknownHeaders(IDictionary<string, StringValues> unknownHeaders, byte[] memoryBlob,
int requestOffset, IntPtr originalAddress)
{ {
// Return value. // Return value.
fixed (byte* pMemoryBlob = memoryBlob) fixed (byte* pMemoryBlob = memoryBlob)
{ {
HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; HTTP_REQUEST* request = (HTTP_REQUEST*)(pMemoryBlob + requestOffset);
long fixup = pMemoryBlob - (byte*)originalAddress; long fixup = pMemoryBlob - (byte*)originalAddress;
int index; int index;
@ -1064,7 +1065,7 @@ namespace Microsoft.Net.Http.Server
HTTP_UNKNOWN_HEADER* pUnknownHeader = (HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders); HTTP_UNKNOWN_HEADER* pUnknownHeader = (HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders);
for (index = 0; index < request->Headers.UnknownHeaderCount; index++) for (index = 0; index < request->Headers.UnknownHeaderCount; index++)
{ {
// For unknown headers, when header value is empty, RawValueLength will be 0 and // For unknown headers, when header value is empty, RawValueLength will be 0 and
// pRawValue will be null. // pRawValue will be null.
if (pUnknownHeader->pName != null && pUnknownHeader->NameLength > 0) if (pUnknownHeader->pName != null && pUnknownHeader->NameLength > 0)
{ {
@ -1093,7 +1094,7 @@ namespace Microsoft.Net.Http.Server
string header = null; string header = null;
HTTP_KNOWN_HEADER* pKnownHeader = (&request->Headers.KnownHeaders) + headerIndex; HTTP_KNOWN_HEADER* pKnownHeader = (&request->Headers.KnownHeaders) + headerIndex;
// For known headers, when header value is empty, RawValueLength will be 0 and // For known headers, when header value is empty, RawValueLength will be 0 and
// pRawValue will point to empty string ("\0") // pRawValue will point to empty string ("\0")
if (pKnownHeader->pRawValue != null) if (pKnownHeader->pRawValue != null)
{ {
@ -1103,11 +1104,12 @@ namespace Microsoft.Net.Http.Server
return header; return header;
} }
internal static string GetKnownHeader(byte[] memoryBlob, IntPtr originalAddress, int headerIndex) internal static string GetKnownHeader(byte[] memoryBlob, int requestOffset, IntPtr originalAddress, int headerIndex)
{ {
fixed (byte* pMemoryBlob = memoryBlob) fixed (byte* pMemoryBlob = memoryBlob)
{ {
return GetKnownHeader((HTTP_REQUEST*)pMemoryBlob, pMemoryBlob - (byte*)originalAddress, headerIndex); return GetKnownHeader(
(HTTP_REQUEST*)(pMemoryBlob + requestOffset), pMemoryBlob - (byte*)originalAddress, headerIndex);
} }
} }
@ -1127,21 +1129,21 @@ namespace Microsoft.Net.Http.Server
return verb; return verb;
} }
internal static unsafe string GetVerb(byte[] memoryBlob, IntPtr originalAddress) internal static unsafe string GetVerb(byte[] memoryBlob, int requestOffset, IntPtr originalAddress)
{ {
fixed (byte* pMemoryBlob = memoryBlob) fixed (byte* pMemoryBlob = memoryBlob)
{ {
return GetVerb((HTTP_REQUEST*)pMemoryBlob, pMemoryBlob - (byte*)originalAddress); return GetVerb((HTTP_REQUEST*)(pMemoryBlob + requestOffset), pMemoryBlob - (byte*)originalAddress);
} }
} }
internal static HTTP_VERB GetKnownVerb(byte[] memoryBlob, IntPtr originalAddress) internal static HTTP_VERB GetKnownVerb(byte[] memoryBlob, int requestOffset, IntPtr originalAddress)
{ {
// Return value. // Return value.
HTTP_VERB verb = HTTP_VERB.HttpVerbUnknown; HTTP_VERB verb = HTTP_VERB.HttpVerbUnknown;
fixed (byte* pMemoryBlob = memoryBlob) fixed (byte* pMemoryBlob = memoryBlob)
{ {
HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; HTTP_REQUEST* request = (HTTP_REQUEST*)(pMemoryBlob + requestOffset);
if ((int)request->Verb > (int)HTTP_VERB.HttpVerbUnparsed && (int)request->Verb < (int)HTTP_VERB.HttpVerbMaximum) if ((int)request->Verb > (int)HTTP_VERB.HttpVerbUnparsed && (int)request->Verb < (int)HTTP_VERB.HttpVerbMaximum)
{ {
verb = request->Verb; verb = request->Verb;
@ -1151,13 +1153,14 @@ namespace Microsoft.Net.Http.Server
return verb; return verb;
} }
internal static uint GetChunks(byte[] memoryBlob, IntPtr originalAddress, ref int dataChunkIndex, ref uint dataChunkOffset, byte[] buffer, int offset, int size) internal static uint GetChunks(byte[] memoryBlob, int requestOffset, IntPtr originalAddress,
ref int dataChunkIndex, ref uint dataChunkOffset, byte[] buffer, int offset, int size)
{ {
// Return value. // Return value.
uint dataRead = 0; uint dataRead = 0;
fixed (byte* pMemoryBlob = memoryBlob) fixed (byte* pMemoryBlob = memoryBlob)
{ {
HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; HTTP_REQUEST* request = (HTTP_REQUEST*)(pMemoryBlob + requestOffset);
long fixup = pMemoryBlob - (byte*)originalAddress; long fixup = pMemoryBlob - (byte*)originalAddress;
if (request->EntityChunkCount > 0 && dataChunkIndex < request->EntityChunkCount && dataChunkIndex != -1) if (request->EntityChunkCount > 0 && dataChunkIndex < request->EntityChunkCount && dataChunkIndex != -1)
@ -1205,30 +1208,30 @@ namespace Microsoft.Net.Http.Server
return dataRead; return dataRead;
} }
internal static SocketAddress GetRemoteEndPoint(byte[] memoryBlob, IntPtr originalAddress) internal static SocketAddress GetRemoteEndPoint(byte[] memoryBlob, int requestOffset, IntPtr originalAddress)
{ {
fixed (byte* pMemoryBlob = memoryBlob) fixed (byte* pMemoryBlob = memoryBlob)
{ {
HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; HTTP_REQUEST* request = (HTTP_REQUEST*)(pMemoryBlob + requestOffset);
return GetEndPoint(memoryBlob, originalAddress, (byte*)request->Address.pRemoteAddress); return GetEndPoint(memoryBlob, requestOffset, originalAddress, (byte*)request->Address.pRemoteAddress);
} }
} }
internal static SocketAddress GetLocalEndPoint(byte[] memoryBlob, IntPtr originalAddress) internal static SocketAddress GetLocalEndPoint(byte[] memoryBlob, int requestOffset, IntPtr originalAddress)
{ {
fixed (byte* pMemoryBlob = memoryBlob) fixed (byte* pMemoryBlob = memoryBlob)
{ {
HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; HTTP_REQUEST* request = (HTTP_REQUEST*)(pMemoryBlob + requestOffset);
return GetEndPoint(memoryBlob, originalAddress, (byte*)request->Address.pLocalAddress); return GetEndPoint(memoryBlob, requestOffset, originalAddress, (byte*)request->Address.pLocalAddress);
} }
} }
internal static SocketAddress GetEndPoint(byte[] memoryBlob, IntPtr originalAddress, byte* source) internal static SocketAddress GetEndPoint(byte[] memoryBlob, int requestOffset, IntPtr originalAddress, byte* source)
{ {
fixed (byte* pMemoryBlob = memoryBlob) fixed (byte* pMemoryBlob = memoryBlob)
{ {
IntPtr address = source != null ? IntPtr address = source != null ?
(IntPtr)(pMemoryBlob - (byte*)originalAddress + source) : IntPtr.Zero; (IntPtr)(pMemoryBlob + requestOffset - (byte*)originalAddress + source) : IntPtr.Zero;
return CopyOutAddress(address); return CopyOutAddress(address);
} }
} }

View File

@ -31,9 +31,11 @@ namespace Microsoft.Net.Http.Server
internal unsafe class NativeRequestContext : IDisposable internal unsafe class NativeRequestContext : IDisposable
{ {
private const int DefaultBufferSize = 4096; private const int DefaultBufferSize = 4096;
private const int AlignmentPadding = 8;
private UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST* _memoryBlob; private UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST* _memoryBlob;
private IntPtr _originalBlobAddress; private IntPtr _originalBlobAddress;
private byte[] _backingBuffer; private byte[] _backingBuffer;
private int _bufferAlignment;
private SafeNativeOverlapped _nativeOverlapped; private SafeNativeOverlapped _nativeOverlapped;
private AsyncAcceptContext _acceptResult; private AsyncAcceptContext _acceptResult;
@ -80,7 +82,15 @@ namespace Microsoft.Net.Http.Server
{ {
get get
{ {
return (uint)_backingBuffer.Length; return (uint)_backingBuffer.Length - AlignmentPadding;
}
}
internal int BufferAlignment
{
get
{
return _bufferAlignment;
} }
} }
@ -89,7 +99,7 @@ namespace Microsoft.Net.Http.Server
get get
{ {
UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST* blob = _memoryBlob; UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST* blob = _memoryBlob;
return (blob == null ? _originalBlobAddress : (IntPtr)blob); return blob == null ? _originalBlobAddress : (IntPtr)blob;
} }
} }
@ -155,12 +165,13 @@ namespace Microsoft.Net.Http.Server
private void SetBuffer(int size) private void SetBuffer(int size)
{ {
_backingBuffer = size == 0 ? null : new byte[size]; Debug.Assert(size != 0, "unexpected size");
_backingBuffer = new byte[size + AlignmentPadding];
} }
private UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST* Allocate(uint size) private UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST* Allocate(uint size)
{ {
uint newSize = size != 0 ? size : RequestBuffer == null ? DefaultBufferSize : Size;
// We can't reuse overlapped objects // We can't reuse overlapped objects
if (_nativeOverlapped != null) if (_nativeOverlapped != null)
{ {
@ -168,15 +179,19 @@ namespace Microsoft.Net.Http.Server
_nativeOverlapped = null; _nativeOverlapped = null;
nativeOverlapped.Dispose(); nativeOverlapped.Dispose();
} }
if (_nativeOverlapped == null)
{ uint newSize = size != 0 ? size : RequestBuffer == null ? DefaultBufferSize : Size;
SetBuffer(checked((int)newSize)); SetBuffer(checked((int)newSize));
var boundHandle = _acceptResult.Server.BoundHandle; var boundHandle = _acceptResult.Server.BoundHandle;
_nativeOverlapped = new SafeNativeOverlapped(boundHandle, _nativeOverlapped = new SafeNativeOverlapped(boundHandle,
boundHandle.AllocateNativeOverlapped(AsyncAcceptContext.IOCallback, _acceptResult, RequestBuffer)); boundHandle.AllocateNativeOverlapped(AsyncAcceptContext.IOCallback, _acceptResult, RequestBuffer));
return (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST*)Marshal.UnsafeAddrOfPinnedArrayElement(RequestBuffer, 0);
} // HttpReceiveHttpRequest expects the request pointer to be 8-byte-aligned or it fails. On ARM
return RequestBlob; // CLR creates buffers that are 4-byte-aligned so we need force 8-byte alignment.
var requestAddress = Marshal.UnsafeAddrOfPinnedArrayElement(RequestBuffer, 0);
_bufferAlignment = (int)(requestAddress.ToInt64() & 0x07);
return (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST*)(requestAddress + _bufferAlignment);
} }
internal void Reset(ulong requestId, uint size) internal void Reset(ulong requestId, uint size)

View File

@ -141,7 +141,7 @@ namespace Microsoft.Net.Http.Server
_httpVersion = new Version(major, minor); _httpVersion = new Version(major, minor);
} }
_httpMethod = UnsafeNclNativeMethods.HttpApi.GetVerb(RequestBuffer, OriginalBlobAddress); _httpMethod = UnsafeNclNativeMethods.HttpApi.GetVerb(RequestBuffer, BufferAlignment, OriginalBlobAddress);
_headers = new HeaderCollection(new RequestHeaders(_nativeRequestContext)); _headers = new HeaderCollection(new RequestHeaders(_nativeRequestContext));
var requestV2 = (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2*)memoryBlob.RequestBlob; var requestV2 = (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2*)memoryBlob.RequestBlob;
@ -190,6 +190,15 @@ namespace Microsoft.Net.Http.Server
} }
} }
internal int BufferAlignment
{
get
{
CheckDisposed();
return _nativeRequestContext.BufferAlignment;
}
}
internal IntPtr OriginalBlobAddress internal IntPtr OriginalBlobAddress
{ {
get get
@ -304,7 +313,7 @@ namespace Microsoft.Net.Http.Server
return _rawUrl; return _rawUrl;
} }
} }
public Version ProtocolVersion public Version ProtocolVersion
{ {
get get
@ -329,7 +338,7 @@ namespace Microsoft.Net.Http.Server
{ {
if (_remoteEndPoint == null) if (_remoteEndPoint == null)
{ {
_remoteEndPoint = UnsafeNclNativeMethods.HttpApi.GetRemoteEndPoint(RequestBuffer, OriginalBlobAddress); _remoteEndPoint = UnsafeNclNativeMethods.HttpApi.GetRemoteEndPoint(RequestBuffer, BufferAlignment, OriginalBlobAddress);
} }
return _remoteEndPoint; return _remoteEndPoint;
@ -342,7 +351,7 @@ namespace Microsoft.Net.Http.Server
{ {
if (_localEndPoint == null) if (_localEndPoint == null)
{ {
_localEndPoint = UnsafeNclNativeMethods.HttpApi.GetLocalEndPoint(RequestBuffer, OriginalBlobAddress); _localEndPoint = UnsafeNclNativeMethods.HttpApi.GetLocalEndPoint(RequestBuffer, BufferAlignment, OriginalBlobAddress);
} }
return _localEndPoint; return _localEndPoint;
@ -406,7 +415,7 @@ namespace Microsoft.Net.Http.Server
internal UnsafeNclNativeMethods.HttpApi.HTTP_VERB GetKnownMethod() internal UnsafeNclNativeMethods.HttpApi.HTTP_VERB GetKnownMethod()
{ {
return UnsafeNclNativeMethods.HttpApi.GetKnownVerb(RequestBuffer, OriginalBlobAddress); return UnsafeNclNativeMethods.HttpApi.GetKnownVerb(RequestBuffer, BufferAlignment, OriginalBlobAddress);
} }
// Populates the client certificate. The result may be null if there is no client cert. // Populates the client certificate. The result may be null if there is no client cert.

View File

@ -86,13 +86,13 @@ namespace Microsoft.Net.Http.Server
private string GetKnownHeader(HttpSysRequestHeader header) private string GetKnownHeader(HttpSysRequestHeader header)
{ {
return UnsafeNclNativeMethods.HttpApi.GetKnownHeader(_requestMemoryBlob.RequestBuffer, return UnsafeNclNativeMethods.HttpApi.GetKnownHeader(_requestMemoryBlob.RequestBuffer,
_requestMemoryBlob.OriginalBlobAddress, (int)header); _requestMemoryBlob.BufferAlignment, _requestMemoryBlob.OriginalBlobAddress, (int)header);
} }
private void GetUnknownHeaders(IDictionary<string, StringValues> extra) private void GetUnknownHeaders(IDictionary<string, StringValues> extra)
{ {
UnsafeNclNativeMethods.HttpApi.GetUnknownHeaders(extra, _requestMemoryBlob.RequestBuffer, UnsafeNclNativeMethods.HttpApi.GetUnknownHeaders(extra, _requestMemoryBlob.RequestBuffer,
_requestMemoryBlob.OriginalBlobAddress); _requestMemoryBlob.BufferAlignment, _requestMemoryBlob.OriginalBlobAddress);
} }
void IDictionary<string, StringValues>.Add(string key, StringValues value) void IDictionary<string, StringValues>.Add(string key, StringValues value)

View File

@ -147,7 +147,9 @@ namespace Microsoft.Net.Http.Server
if (_dataChunkIndex != -1) if (_dataChunkIndex != -1)
{ {
dataRead = UnsafeNclNativeMethods.HttpApi.GetChunks(_requestContext.Request.RequestBuffer, _requestContext.Request.OriginalBlobAddress, ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size); dataRead = UnsafeNclNativeMethods.HttpApi.GetChunks(_requestContext.Request.RequestBuffer,
_requestContext.Request.BufferAlignment, _requestContext.Request.OriginalBlobAddress,
ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size);
} }
if (_dataChunkIndex == -1 && dataRead < size) if (_dataChunkIndex == -1 && dataRead < size)
@ -223,7 +225,9 @@ namespace Microsoft.Net.Http.Server
uint dataRead = 0; uint dataRead = 0;
if (_dataChunkIndex != -1) if (_dataChunkIndex != -1)
{ {
dataRead = UnsafeNclNativeMethods.HttpApi.GetChunks(_requestContext.Request.RequestBuffer, _requestContext.Request.OriginalBlobAddress, ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size); dataRead = UnsafeNclNativeMethods.HttpApi.GetChunks(_requestContext.Request.RequestBuffer, _requestContext.Request.BufferAlignment,
_requestContext.Request.OriginalBlobAddress, ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size);
if (_dataChunkIndex != -1 && dataRead == size) if (_dataChunkIndex != -1 && dataRead == size)
{ {
asyncResult = new RequestStreamAsyncResult(this, state, callback, buffer, offset, 0); asyncResult = new RequestStreamAsyncResult(this, state, callback, buffer, offset, 0);
@ -339,7 +343,8 @@ namespace Microsoft.Net.Http.Server
uint dataRead = 0; uint dataRead = 0;
if (_dataChunkIndex != -1) if (_dataChunkIndex != -1)
{ {
dataRead = UnsafeNclNativeMethods.HttpApi.GetChunks(_requestContext.Request.RequestBuffer, _requestContext.Request.OriginalBlobAddress, ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size); dataRead = UnsafeNclNativeMethods.HttpApi.GetChunks(_requestContext.Request.RequestBuffer, _requestContext.Request.BufferAlignment,
_requestContext.Request.OriginalBlobAddress, ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size);
if (_dataChunkIndex != -1 && dataRead == size) if (_dataChunkIndex != -1 && dataRead == size)
{ {
UpdateAfterRead(UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS, dataRead); UpdateAfterRead(UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS, dataRead);
@ -347,7 +352,7 @@ namespace Microsoft.Net.Http.Server
return Task.FromResult<int>((int)dataRead); return Task.FromResult<int>((int)dataRead);
} }
} }
if (_dataChunkIndex == -1 && dataRead < size) if (_dataChunkIndex == -1 && dataRead < size)
{ {
uint statusCode = 0; uint statusCode = 0;

View File

@ -890,7 +890,7 @@ namespace Microsoft.Net.Http.Server
blob = new byte[size]; blob = new byte[size];
fixed (byte* blobPtr = blob) fixed (byte* blobPtr = blob)
{ {
// Http.sys team: ServiceName will always be null if // Http.sys team: ServiceName will always be null if
// HTTP_RECEIVE_SECURE_CHANNEL_TOKEN flag is set. // HTTP_RECEIVE_SECURE_CHANNEL_TOKEN flag is set.
statusCode = UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate( statusCode = UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate(
RequestQueueHandle, RequestQueueHandle,